diff --git a/src/Makefile.am b/src/Makefile.am index 39ba9af3ed9f..02eedbbe1f54 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -398,8 +398,6 @@ libbitcoin_server_a_SOURCES = \ index/base.cpp \ index/blockfilterindex.cpp \ index/txindex.cpp \ - interfaces/chain.cpp \ - interfaces/node.cpp \ init.cpp \ governance/governance.cpp \ governance/classes.cpp \ @@ -436,6 +434,7 @@ libbitcoin_server_a_SOURCES = \ node/coin.cpp \ node/coinstats.cpp \ node/context.cpp \ + node/interfaces.cpp \ node/transaction.cpp \ noui.cpp \ policy/fees.cpp \ @@ -495,12 +494,12 @@ libbitcoin_wallet_a_SOURCES = \ coinjoin/client.cpp \ coinjoin/options.cpp \ coinjoin/util.cpp \ - interfaces/wallet.cpp \ wallet/coincontrol.cpp \ wallet/context.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/fees.cpp \ + wallet/interfaces.cpp \ wallet/load.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 0f6d54e83604..280d56991c87 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -148,6 +148,7 @@ BITCOIN_QT_H = \ qt/rpcconsole.h \ qt/sendcoinsdialog.h \ qt/sendcoinsentry.h \ + qt/sendcoinsrecipient.h \ qt/signverifymessagedialog.h \ qt/splashscreen.h \ qt/trafficgraphdata.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index b8e5f98e5f68..4d1b921ba174 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -105,6 +105,7 @@ BITCOIN_TESTS =\ test/getarg_tests.cpp \ test/governance_validators_tests.cpp \ test/hash_tests.cpp \ + test/interfaces_tests.cpp \ test/key_io_tests.cpp \ test/key_tests.cpp \ test/lcg.h \ diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp index 677e18ff3947..11f98059022a 100644 --- a/src/bench/wallet_balance.cpp +++ b/src/bench/wallet_balance.cpp @@ -17,15 +17,13 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b RegTestingSetup test_setup; const auto& ADDRESS_WATCHONLY = ADDRESS_B58T_UNSPENDABLE; - NodeContext node; - std::unique_ptr chain = interfaces::MakeChain(node); - CWallet wallet{chain.get(), "", CreateMockWalletDatabase()}; + CWallet wallet{test_setup.m_node.chain.get(), "", CreateMockWalletDatabase()}; { wallet.SetupLegacyScriptPubKeyMan(); bool first_run; if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) assert(false); } - auto handler = chain->handleNotifications({ &wallet, [](CWallet*) {} }); + auto handler = test_setup.m_node.chain->handleNotifications({ &wallet, [](CWallet*) {} }); const std::optional address_mine{add_mine ? std::optional{getnewaddress(wallet)} : std::nullopt}; if (add_watchonly) importaddress(wallet, ADDRESS_WATCHONLY); diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index f91214ccea82..1c8e57ed4fa9 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -1416,7 +1416,7 @@ template static bool CheckHashSig(const ProTx& proTx, const PKHash& pkhash, CValidationState& state) { std::string strError; - if (!CHashSigner::VerifyHash(::SerializeHash(proTx), CKeyID(pkhash), proTx.vchSig, strError)) { + if (!CHashSigner::VerifyHash(::SerializeHash(proTx), ToKeyID(pkhash), proTx.vchSig, strError)) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-sig"); } return true; @@ -1426,7 +1426,7 @@ template static bool CheckStringSig(const ProTx& proTx, const PKHash& pkhash, CValidationState& state) { std::string strError; - if (!CMessageSigner::VerifyMessage(CKeyID(pkhash), proTx.vchSig, proTx.MakeSignString(), strError)) { + if (!CMessageSigner::VerifyMessage(ToKeyID(pkhash), proTx.vchSig, proTx.MakeSignString(), strError)) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-sig"); } return true; diff --git a/src/init.cpp b/src/init.cpp index a342859805ea..00f8025e4a66 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2190,11 +2190,8 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc if (llmq::utils::IsV19Active(tip)) bls::bls_legacy_scheme.store(false); - // Only verify the DB of the active chainstate. This is fixed in later - // work when we allow VerifyDB to be parameterized by chainstate. - if (&::ChainstateActive() == chainstate && - !CVerifyDB().VerifyDB( - chainparams, *chainstate, &chainstate->CoinsDB(), + if (!CVerifyDB().VerifyDB( + *chainstate, chainparams, chainstate->CoinsDB(), *node.evodb, args.GetArg("-checklevel", DEFAULT_CHECKLEVEL), args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp deleted file mode 100644 index 4795e5b9d4f1..000000000000 --- a/src/interfaces/chain.cpp +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright (c) 2018-2019 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace interfaces { -namespace { -class NotificationsProxy : public CValidationInterface -{ -public: - explicit NotificationsProxy(std::shared_ptr notifications) - : m_notifications(std::move(notifications)) {} - virtual ~NotificationsProxy() = default; - void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) override - { - m_notifications->TransactionAddedToMempool(tx, nAcceptTime); - } - void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override - { - m_notifications->TransactionRemovedFromMempool(tx, reason); - } - void BlockConnected(const std::shared_ptr& block, const CBlockIndex* index) override - { - m_notifications->BlockConnected(*block, index->nHeight); - } - void BlockDisconnected(const std::shared_ptr& block, const CBlockIndex* index) override - { - m_notifications->BlockDisconnected(*block, index->nHeight); - } - void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override - { - m_notifications->UpdatedBlockTip(); - } - void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); } - void NotifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr& clsig) override - { - m_notifications->NotifyChainLock(pindexChainLock, clsig); - } - void NotifyTransactionLock(const CTransactionRef &tx, const std::shared_ptr& islock) override - { - m_notifications->NotifyTransactionLock(tx, islock); - } - std::shared_ptr m_notifications; -}; - - -class NotificationsHandlerImpl : public Handler -{ -public: - explicit NotificationsHandlerImpl(std::shared_ptr notifications) - : m_proxy(std::make_shared(std::move(notifications))) - { - RegisterSharedValidationInterface(m_proxy); - } - ~NotificationsHandlerImpl() override { disconnect(); } - void disconnect() override - { - if (m_proxy) { - UnregisterSharedValidationInterface(m_proxy); - m_proxy.reset(); - } - } - std::shared_ptr m_proxy; -}; - -class RpcHandlerImpl : public Handler -{ -public: - explicit RpcHandlerImpl(const CRPCCommand& command) : m_command(command), m_wrapped_command(&command) - { - m_command.actor = [this](const JSONRPCRequest& request, UniValue& result, bool last_handler) { - if (!m_wrapped_command) return false; - try { - return m_wrapped_command->actor(request, result, last_handler); - } catch (const UniValue& e) { - // If this is not the last handler and a wallet not found - // exception was thrown, return false so the next handler can - // try to handle the request. Otherwise, reraise the exception. - if (!last_handler) { - const UniValue& code = e["code"]; - if (code.isNum() && code.get_int() == RPC_WALLET_NOT_FOUND) { - return false; - } - } - throw; - } - }; - ::tableRPC.appendCommand(m_command.name, &m_command); - } - - void disconnect() override final - { - if (m_wrapped_command) { - m_wrapped_command = nullptr; - ::tableRPC.removeCommand(m_command.name, &m_command); - } - } - - ~RpcHandlerImpl() override { disconnect(); } - - CRPCCommand m_command; - const CRPCCommand* m_wrapped_command; -}; - -class ChainImpl : public Chain -{ -public: - explicit ChainImpl(NodeContext& node) : m_node(node) {} - std::optional getHeight() override - { - LOCK(::cs_main); - int height = ::ChainActive().Height(); - if (height >= 0) { - return height; - } - return std::nullopt; - } - std::optional getBlockHeight(const uint256& hash) override - { - LOCK(::cs_main); - CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(hash); - if (block && ::ChainActive().Contains(block)) { - return block->nHeight; - } - return std::nullopt; - } - uint256 getBlockHash(int height) override - { - LOCK(::cs_main); - CBlockIndex* block = ::ChainActive()[height]; - assert(block != nullptr); - return block->GetBlockHash(); - } - int64_t getBlockTime(int height) override - { - LOCK(cs_main); - CBlockIndex* block = ::ChainActive()[height]; - assert(block != nullptr); - return block->GetBlockTime(); - } - int64_t getBlockMedianTimePast(int height) override - { - LOCK(cs_main); - CBlockIndex* block = ::ChainActive()[height]; - assert(block != nullptr); - return block->GetMedianTimePast(); - } - bool haveBlockOnDisk(int height) override - { - LOCK(cs_main); - CBlockIndex* block = ::ChainActive()[height]; - return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0; - } - std::optional findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override - { - LOCK(cs_main); - CBlockIndex* block = ::ChainActive().FindEarliestAtLeast(time, height); - if (block) { - if (hash) *hash = block->GetBlockHash(); - return block->nHeight; - } - return std::nullopt; - } - std::optional findPruned(int start_height, std::optional stop_height) override - { - LOCK(cs_main); - if (::fPruneMode) { - CBlockIndex* block = stop_height ? ::ChainActive()[*stop_height] : ::ChainActive().Tip(); - while (block && block->nHeight >= start_height) { - if ((block->nStatus & BLOCK_HAVE_DATA) == 0) { - return block->nHeight; - } - block = block->pprev; - } - } - return std::nullopt; - } - std::optional findFork(const uint256& hash, std::optional* height) override - { - LOCK(cs_main); - const CBlockIndex* block = g_chainman.m_blockman.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 std::nullopt; - } - CBlockLocator getTipLocator() override - { - LOCK(cs_main); - return ::ChainActive().GetLocator(); - } - std::optional findLocatorFork(const CBlockLocator& locator) override - { - LOCK(cs_main); - if (CBlockIndex* fork = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator)) { - return fork->nHeight; - } - return std::nullopt; - } - bool checkFinalTx(const CTransaction& tx) override - { - LOCK(cs_main); - return CheckFinalTx(::ChainActive().Tip(), tx); - } - bool findBlock(const uint256& hash, CBlock* block, int64_t* time, int64_t* time_max) override - { - CBlockIndex* index; - { - LOCK(cs_main); - index = g_chainman.m_blockman.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; - } - void findCoins(std::map& coins) override { return FindCoins(m_node, coins); } - double guessVerificationProgress(const uint256& block_hash) override - { - LOCK(cs_main); - return GuessVerificationProgress(Params().TxData(), g_chainman.m_blockman.LookupBlockIndex(block_hash)); - } - bool hasDescendantsInMempool(const uint256& txid) override - { - if (!m_node.mempool) return false; - LOCK(m_node.mempool->cs); - auto it = m_node.mempool->GetIter(txid); - return it && (*it)->GetCountWithDescendants() > 1; - } - bool broadcastTransaction(const CTransactionRef& tx, std::string& err_string, const CAmount& max_tx_fee, bool relay) override - { - const TransactionError err = BroadcastTransaction(m_node, tx, err_string, max_tx_fee, relay, /*wait_callback*/ false); - // Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures. - // Note: this will need to be updated if BroadcastTransactions() is updated to return other non-mempool failures - // that Chain clients do not need to know about. - return TransactionError::OK == err; - } - void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override - { - ancestors = descendants = 0; - if (!m_node.mempool) return; - m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants); - } - void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override - { - limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); - limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); - } - bool checkChainLimits(const CTransactionRef& tx) override - { - if (!m_node.mempool) return true; - LockPoints lp; - CTxMemPoolEntry entry(tx, 0, 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(m_node.mempool->cs); - return m_node.mempool->CalculateMemPoolAncestors( - entry, ancestors, limit_ancestor_count, limit_ancestor_size, - limit_descendant_count, limit_descendant_size, unused_error_string); - } - CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override - { - if (!m_node.fee_estimator) return {}; - return m_node.fee_estimator->estimateSmartFee(num_blocks, calc, conservative); - } - unsigned int estimateMaxBlocks() override - { - if (!m_node.fee_estimator) return 0; - return m_node.fee_estimator->HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); - } - CFeeRate mempoolMinFee() override - { - if (!m_node.mempool) return {}; - return m_node.mempool->GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); - } - CFeeRate relayMinFee() override { return ::minRelayTxFee; } - CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; } - CFeeRate relayDustFee() override { return ::dustRelayFee; } - bool havePruned() override - { - LOCK(cs_main); - return ::fHavePruned; - } - bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !::ChainstateActive().IsInitialBlockDownload(); } - bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } - bool shutdownRequested() override { return ShutdownRequested(); } - int64_t getAdjustedTime() override { return GetAdjustedTime(); } - void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); } - void initWarning(const bilingual_str& message) override { InitWarning(message); } - void initError(const bilingual_str& message) override { InitError(message); } - void showProgress(const std::string& title, int progress, bool resume_possible) override - { - ::uiInterface.ShowProgress(title, progress, resume_possible); - } - std::unique_ptr handleNotifications(std::shared_ptr notifications) override - { - return std::make_unique(std::move(notifications)); - } - void waitForNotificationsIfTipChanged(const uint256& old_tip) override - { - if (!old_tip.IsNull()) { - LOCK(::cs_main); - if (old_tip == ::ChainActive().Tip()->GetBlockHash()) return; - } - SyncWithValidationInterfaceQueue(); - } - std::unique_ptr handleRpc(const CRPCCommand& command) override - { - return std::make_unique(command); - } - bool rpcEnableDeprecated(const std::string& method) override { return IsDeprecatedRPCEnabled(method); } - void rpcRunLater(const std::string& name, std::function fn, int64_t seconds) override - { - RPCRunLater(name, std::move(fn), seconds); - } - util::SettingsValue getRwSetting(const std::string& name) override - { - util::SettingsValue result; - gArgs.LockSettings([&](const util::Settings& settings) { - if (const util::SettingsValue* value = util::FindKey(settings.rw_settings, name)) { - result = *value; - } - }); - return result; - } - bool updateRwSetting(const std::string& name, const util::SettingsValue& value) override - { - gArgs.LockSettings([&](util::Settings& settings) { - if (value.isNull()) { - settings.rw_settings.erase(name); - } else { - settings.rw_settings[name] = value; - } - }); - return gArgs.WriteSettingsFile(); - } - void requestMempoolTransactions(Notifications& notifications) override - { - if (!m_node.mempool) return; - LOCK2(::cs_main, m_node.mempool->cs); - for (const CTxMemPoolEntry& entry : m_node.mempool->mapTx) { - notifications.TransactionAddedToMempool(entry.GetSharedTx(), 0); - } - } - NodeContext& m_node; -}; -} // namespace - -std::unique_ptr MakeChain(NodeContext& node) { return std::make_unique(node); } - -} // namespace interfaces diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index 10282086975d..6f3f01faa2f5 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -46,6 +46,33 @@ namespace interfaces { class Wallet; class Handler; +//! Helper for findBlock to selectively return pieces of block data. +class FoundBlock +{ +public: + FoundBlock& hash(uint256& hash) { m_hash = &hash; return *this; } + FoundBlock& height(int& height) { m_height = &height; return *this; } + FoundBlock& time(int64_t& time) { m_time = &time; return *this; } + FoundBlock& maxTime(int64_t& max_time) { m_max_time = &max_time; return *this; } + FoundBlock& mtpTime(int64_t& mtp_time) { m_mtp_time = &mtp_time; return *this; } + //! Return whether block is in the active (most-work) chain. + FoundBlock& inActiveChain(bool& in_active_chain) { m_in_active_chain = &in_active_chain; return *this; } + //! Return next block in the active chain if current block is in the active chain. + FoundBlock& nextBlock(const FoundBlock& next_block) { m_next_block = &next_block; return *this; } + //! Read block data from disk. If the block exists but doesn't have data + //! (for example due to pruning), the CBlock variable will be set to null. + FoundBlock& data(CBlock& data) { m_data = &data; return *this; } + + uint256* m_hash = nullptr; + int* m_height = nullptr; + int64_t* m_time = nullptr; + int64_t* m_max_time = nullptr; + int64_t* m_mtp_time = nullptr; + bool* m_in_active_chain = nullptr; + const FoundBlock* m_next_block = nullptr; + CBlock* m_data = nullptr; +}; + //! Interface giving clients (wallet processes, maybe other analysis tools in //! the future) ability to access to the chain state, receive notifications, //! estimate fees, and submit transactions. @@ -67,9 +94,9 @@ class Handler; //! wallet cache it, fee estimation being driven by node mempool, wallet //! should be the consumer. //! -//! * The `guessVerificationProgress`, `getBlockHeight`, `getBlockHash`, etc -//! methods can go away if rescan logic is moved on the node side, and wallet -//! only register rescan request. +//! * `guessVerificationProgress` and similar methods can go away if rescan +//! logic moves out of the wallet, and the wallet just requests scans from the +//! node (https://github.com/bitcoin/bitcoin/issues/11756) class Chain { public: @@ -80,36 +107,13 @@ class Chain //! any blocks) virtual std::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 std::optional getBlockHeight(const uint256& hash) = 0; - //! Get block hash. Height must be valid or this function will abort. virtual uint256 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 and height equal or greater than the - //! given height, or nullopt if there is no block with a high enough - //! timestamp and height. 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 std::optional findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 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 std::optional findPruned(int start_height = 0, std::optional stop_height = std::nullopt) = 0; - //! Return height of the specified block if it is on the chain, otherwise //! return the height of the highest block on chain that's an ancestor //! of the specified block, or nullopt if there is no common ancestor. @@ -131,14 +135,31 @@ class Chain //! 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 uint256& hash, - CBlock* block = nullptr, - int64_t* time = nullptr, - int64_t* max_time = nullptr) = 0; + virtual bool findBlock(const uint256& hash, const FoundBlock& block={}) = 0; + + //! Find first block in the chain with timestamp >= the given time + //! and height >= than the given height, return false if there is no block + //! with a high enough timestamp and height. Optionally return block + //! information. + virtual bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block={}) = 0; + + //! Find ancestor of block at specified height and optionally return + //! ancestor information. + virtual bool findAncestorByHeight(const uint256& block_hash, int ancestor_height, const FoundBlock& ancestor_out={}) = 0; + + //! Return whether block descends from a specified ancestor, and + //! optionally return ancestor information. + virtual bool findAncestorByHash(const uint256& block_hash, + const uint256& ancestor_hash, + const FoundBlock& ancestor_out={}) = 0; + + //! Find most recent common ancestor between two blocks and optionally + //! return block information. + virtual bool findCommonAncestor(const uint256& block_hash1, + const uint256& block_hash2, + const FoundBlock& ancestor_out={}, + const FoundBlock& block1_out={}, + const FoundBlock& block2_out={}) = 0; //! Look up unspent output information. Returns coins in the mempool and in //! the current chain UTXO set. Iterates through all the keys in the map and @@ -149,6 +170,11 @@ class Chain //! the specified block hash are verified. virtual double guessVerificationProgress(const uint256& block_hash) = 0; + //! Return true if data is available for all blocks in the specified range + //! of blocks. This checks all blocks that are ancestors of block_hash in + //! the height range from min_height to max_height, inclusive. + virtual bool hasBlocks(const uint256& block_hash, int min_height = 0, std::optional max_height = {}) = 0; + //! Check if transaction has descendants in mempool. virtual bool hasDescendantsInMempool(const uint256& txid) = 0; diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp deleted file mode 100644 index 0b26159f469b..000000000000 --- a/src/interfaces/node.cpp +++ /dev/null @@ -1,448 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(HAVE_CONFIG_H) -#include -#endif - -#include -#include - -#include - -#include - -namespace interfaces { -namespace { - -class EVOImpl : public EVO -{ -public: - CDeterministicMNList getListAtChainTip() override - { - return deterministicMNManager == nullptr ? CDeterministicMNList() : deterministicMNManager->GetListAtChainTip(); - } -}; - -class GOVImpl : public GOV -{ -public: - void getAllNewerThan(std::vector &objs, int64_t nMoreThanTime) override - { - if (governance == nullptr) return; - governance->GetAllNewerThan(objs, nMoreThanTime); - } -}; - -class LLMQImpl : public LLMQ -{ -public: - size_t getInstantSentLockCount() override - { - return llmq::quorumInstantSendManager == nullptr ? 0 : llmq::quorumInstantSendManager->GetInstantSendLockCount(); - } -}; - -class MasternodeSyncImpl : public Masternode::Sync -{ -public: - bool isSynced() override - { - return ::masternodeSync == nullptr ? false : ::masternodeSync->IsSynced(); - } - bool isBlockchainSynced() override - { - return ::masternodeSync == nullptr ? false : ::masternodeSync->IsBlockchainSynced(); - } - std::string getSyncStatus() override - { - return ::masternodeSync == nullptr ? "" : ::masternodeSync->GetSyncStatus(); - } -}; - -class CoinJoinOptionsImpl : public CoinJoin::Options -{ -public: - int getRounds() override - { - return CCoinJoinClientOptions::GetRounds(); - } - int getAmount() override - { - return CCoinJoinClientOptions::GetAmount(); - } - void setEnabled(bool fEnabled) override - { - return CCoinJoinClientOptions::SetEnabled(fEnabled); - } - void setMultiSessionEnabled(bool fEnabled) override - { - CCoinJoinClientOptions::SetMultiSessionEnabled(fEnabled); - } - void setRounds(int nRounds) override - { - CCoinJoinClientOptions::SetRounds(nRounds); - } - void setAmount(CAmount amount) override - { - CCoinJoinClientOptions::SetAmount(amount); - } - bool isEnabled() override - { - return CCoinJoinClientOptions::IsEnabled(); - } - bool isMultiSessionEnabled() override - { - return CCoinJoinClientOptions::IsMultiSessionEnabled(); - } - bool isCollateralAmount(CAmount nAmount) override - { - return CCoinJoin::IsCollateralAmount(nAmount); - } - CAmount getMinCollateralAmount() override - { - return CCoinJoin::GetCollateralAmount(); - } - CAmount getMaxCollateralAmount() override - { - return CCoinJoin::GetMaxCollateralAmount(); - } - CAmount getSmallestDenomination() override - { - return CCoinJoin::GetSmallestDenomination(); - } - bool isDenominated(CAmount nAmount) override - { - return CCoinJoin::IsDenominatedAmount(nAmount); - } - std::array getStandardDenominations() override - { - return CCoinJoin::GetStandardDenominations(); - } -}; - -class NodeImpl : public Node -{ -public: - EVOImpl m_evo; - GOVImpl m_gov; - LLMQImpl m_llmq; - MasternodeSyncImpl m_masternodeSync; - CoinJoinOptionsImpl m_coinjoin; - - NodeImpl(NodeContext* context) { setContext(context); } - void initError(const bilingual_str& message) override { InitError(message); } - bool parseParameters(int argc, const char* const argv[], std::string& error) override - { - return gArgs.ParseParameters(argc, argv, error); - } - bool readConfigFiles(std::string& error) override { return gArgs.ReadConfigFiles(error, true); } - bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); } - bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } - void selectParams(const std::string& network) override { SelectParams(network); } - bool initSettings(std::string& error) override { return gArgs.InitSettings(error); } - uint64_t getAssumedBlockchainSize() override { return Params().AssumedBlockchainSize(); } - uint64_t getAssumedChainStateSize() override { return Params().AssumedChainStateSize(); } - std::string getNetwork() override { return Params().NetworkIDString(); } - void initLogging() override { InitLogging(gArgs); } - void initParameterInteraction() override { InitParameterInteraction(gArgs); } - std::string getWarnings() override { return GetWarnings(true); } - uint64_t getLogCategories() override { return LogInstance().GetCategoryMask(); } - bool baseInitialize() override - { - return AppInitBasicSetup(gArgs) && AppInitParameterInteraction(gArgs) && AppInitSanityChecks() && - AppInitLockDataDirectory() && AppInitInterfaces(*m_context); - } - bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override - { - return AppInitMain(m_context_ref, *m_context, tip_info); - } - void appShutdown() override - { - Interrupt(*m_context); - Shutdown(*m_context); - } - void appPrepareShutdown() override - { - Interrupt(*m_context); - StartRestart(); - PrepareShutdown(*m_context); - } - void startShutdown() override { StartShutdown(); } - bool shutdownRequested() override { return ShutdownRequested(); } - void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } - void setupServerArgs() override { return SetupServerArgs(*m_context); } - bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } - size_t getNodeCount(CConnman::NumConnections flags) override - { - return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0; - } - bool getNodesStats(NodesStats& stats) override - { - stats.clear(); - - if (m_context->connman) { - std::vector stats_temp; - m_context->connman->GetNodeStats(stats_temp); - - stats.reserve(stats_temp.size()); - for (auto& node_stats_temp : stats_temp) { - stats.emplace_back(std::move(node_stats_temp), false, CNodeStateStats()); - } - - // Try to retrieve the CNodeStateStats for each node. - TRY_LOCK(::cs_main, lockMain); - if (lockMain) { - for (auto& node_stats : stats) { - std::get<1>(node_stats) = - GetNodeStateStats(std::get<0>(node_stats).nodeid, std::get<2>(node_stats)); - } - } - return true; - } - return false; - } - bool getBanned(banmap_t& banmap) override - { - if (m_context->banman) { - m_context->banman->GetBanned(banmap); - return true; - } - return false; - } - bool ban(const CNetAddr& net_addr, int64_t ban_time_offset) override - { - if (m_context->banman) { - m_context->banman->Ban(net_addr, ban_time_offset); - return true; - } - return false; - } - bool unban(const CSubNet& ip) override - { - if (m_context->banman) { - m_context->banman->Unban(ip); - return true; - } - return false; - } - bool disconnect(const CNetAddr& net_addr) override - { - if (m_context->connman) { - return m_context->connman->DisconnectNode(net_addr); - } - return false; - } - bool disconnect(NodeId id) override - { - if (m_context->connman) { - return m_context->connman->DisconnectNode(id); - } - return false; - } - int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; } - int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; } - size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; } - size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; } - bool getHeaderTip(int& height, int64_t& block_time) override - { - LOCK(::cs_main); - if (::pindexBestHeader) { - height = ::pindexBestHeader->nHeight; - block_time = ::pindexBestHeader->GetBlockTime(); - return true; - } - return false; - } - int getNumBlocks() override - { - LOCK(::cs_main); - return ::ChainActive().Height(); - } - int64_t getLastBlockTime() override - { - LOCK(::cs_main); - if (::ChainActive().Tip()) { - return ::ChainActive().Tip()->GetBlockTime(); - } - return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network - } - std::string getLastBlockHash() override - { - LOCK(::cs_main); - if (::ChainActive().Tip()) { - return ::ChainActive().Tip()->GetBlockHash().ToString(); - } - return Params().GenesisBlock().GetHash().ToString(); // Genesis block's hash of current network - } - double getVerificationProgress() override - { - const CBlockIndex* tip; - { - LOCK(::cs_main); - tip = ::ChainActive().Tip(); - } - return GuessVerificationProgress(Params().TxData(), tip); - } - bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } - bool getReindex() override { return ::fReindex; } - bool getImporting() override { return ::fImporting; } - void setNetworkActive(bool active) override - { - if (m_context->connman) { - m_context->connman->SetNetworkActive(active); - } - } - bool getNetworkActive() override { return m_context->connman && m_context->connman->GetNetworkActive(); } - CFeeRate getDustRelayFee() override { return ::dustRelayFee; } - UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override - { - JSONRPCRequest req(m_context_ref); - req.params = params; - req.strMethod = command; - req.URI = uri; - return ::tableRPC.execute(req); - } - std::vector listRpcCommands() override { return ::tableRPC.listCommands(); } - void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); } - void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); } - bool getUnspentOutput(const COutPoint& output, Coin& coin) override - { - LOCK(::cs_main); - return ::ChainstateActive().CoinsTip().GetCoin(output, coin); - } - WalletClient& walletClient() override - { - return *Assert(m_context->wallet_client); - } - - EVO& evo() override { return m_evo; } - GOV& gov() override { return m_gov; } - LLMQ& llmq() override { return m_llmq; } - Masternode::Sync& masternodeSync() override { return m_masternodeSync; } - CoinJoin::Options& coinJoinOptions() override { return m_coinjoin; } - - std::unique_ptr handleInitMessage(InitMessageFn fn) override - { - return MakeHandler(::uiInterface.InitMessage_connect(fn)); - } - std::unique_ptr handleMessageBox(MessageBoxFn fn) override - { - return MakeHandler(::uiInterface.ThreadSafeMessageBox_connect(fn)); - } - std::unique_ptr handleQuestion(QuestionFn fn) override - { - return MakeHandler(::uiInterface.ThreadSafeQuestion_connect(fn)); - } - std::unique_ptr handleShowProgress(ShowProgressFn fn) override - { - return MakeHandler(::uiInterface.ShowProgress_connect(fn)); - } - std::unique_ptr handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override - { - return MakeHandler(::uiInterface.NotifyNumConnectionsChanged_connect(fn)); - } - std::unique_ptr handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) override - { - return MakeHandler(::uiInterface.NotifyNetworkActiveChanged_connect(fn)); - } - std::unique_ptr handleNotifyAlertChanged(NotifyAlertChangedFn fn) override - { - return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn)); - } - std::unique_ptr handleBannedListChanged(BannedListChangedFn fn) override - { - return MakeHandler(::uiInterface.BannedListChanged_connect(fn)); - } - std::unique_ptr handleNotifyBlockTip(NotifyBlockTipFn fn) override - { - return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](bool initial_download, const CBlockIndex* block) { - fn(initial_download, block->nHeight, block->GetBlockTime(), block->GetBlockHash().ToString(), - GuessVerificationProgress(Params().TxData(), block)); - })); - } - std::unique_ptr handleNotifyChainLock(NotifyChainLockFn fn) override - { - return MakeHandler(::uiInterface.NotifyChainLock_connect([fn](const std::string& bestChainLockHash, int bestChainLockHeight) { - fn(bestChainLockHash, bestChainLockHeight); - })); - } - std::unique_ptr handleNotifyHeaderTip(NotifyHeaderTipFn fn) override - { - return MakeHandler( - ::uiInterface.NotifyHeaderTip_connect([fn](bool initial_download, const CBlockIndex* block) { - fn(initial_download, block->nHeight, block->GetBlockTime(), block->GetBlockHash().ToString(), - /* verification progress is unused when a header was received */ 0); - })); - } - std::unique_ptr handleNotifyMasternodeListChanged(NotifyMasternodeListChangedFn fn) override - { - return MakeHandler( - ::uiInterface.NotifyMasternodeListChanged_connect([fn](const CDeterministicMNList& newList) { - fn(newList); - })); - } - std::unique_ptr handleNotifyAdditionalDataSyncProgressChanged(NotifyAdditionalDataSyncProgressChangedFn fn) override - { - return MakeHandler( - ::uiInterface.NotifyAdditionalDataSyncProgressChanged_connect([fn](double nSyncProgress) { - fn(nSyncProgress); - })); - } - NodeContext* context() override { return m_context; } - void setContext(NodeContext* context) override - { - m_context = context; - if (context) { - m_context_ref = *context; - } else { - m_context_ref = std::nullopt; - } - } - NodeContext* m_context{nullptr}; - CoreContext m_context_ref{std::nullopt}; -}; - -} // namespace - -std::unique_ptr MakeNode(NodeContext* context) { return std::make_unique(context); } - -} // namespace interfaces diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 6d145f2a6a45..8aee777821d4 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -38,6 +38,7 @@ struct NodeContext; namespace interfaces { class Handler; class WalletClient; +struct BlockTip; //! Interface for the src/evo part of a dash node (dashd process). class EVO @@ -229,6 +230,9 @@ class Node //! Get num blocks. virtual int getNumBlocks() = 0; + //! Get best block hash. + virtual uint256 getBestBlockHash() = 0; + //! Get last block time. virtual int64_t getLastBlockTime() = 0; @@ -327,7 +331,7 @@ class Node //! Register handler for block tip messages. using NotifyBlockTipFn = - std::function; + std::function; virtual std::unique_ptr handleNotifyBlockTip(NotifyBlockTipFn fn) = 0; //! Register handler for chainlock messages. @@ -337,7 +341,7 @@ class Node //! Register handler for header tip messages. using NotifyHeaderTipFn = - std::function; + std::function; virtual std::unique_ptr handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0; //! Register handler for masternode list update messages. @@ -359,6 +363,12 @@ class Node //! Return implementation of Node interface. std::unique_ptr MakeNode(NodeContext* context = nullptr); +//! Block tip (could be a header or not, depends on the subscribed signal). +struct BlockTip { + int block_height; + int64_t block_time; + uint256 block_hash; +}; } // namespace interfaces #endif // BITCOIN_INTERFACES_NODE_H diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index fba448f54cb5..d3154753ddef 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -203,6 +203,7 @@ class Wallet //! Try to get updated status for a particular transaction, if possible without blocking. virtual bool tryGetTxStatus(const uint256& txid, WalletTxStatus& tx_status, + int& num_blocks, int64_t& block_time) = 0; //! Get transaction details. @@ -229,7 +230,7 @@ class Wallet virtual WalletBalances getBalances() = 0; //! Get balances if possible without blocking. - virtual bool tryGetBalances(WalletBalances& balances, int& num_blocks) = 0; + virtual bool tryGetBalances(WalletBalances& balances, uint256& block_hash) = 0; //! Get balance. virtual CAmount getBalance() = 0; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp new file mode 100644 index 000000000000..12f5727cb2b4 --- /dev/null +++ b/src/node/interfaces.cpp @@ -0,0 +1,854 @@ +// Copyright (c) 2018-2020 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include +#include + +#include + +#include + +#include +#include + +using interfaces::BlockTip; +using interfaces::Chain; +using interfaces::EVO; +using interfaces::FoundBlock; +using interfaces::GOV; +using interfaces::Handler; +using interfaces::LLMQ; +using interfaces::MakeHandler; +using interfaces::Node; +using interfaces::WalletClient; + +namespace node { +namespace { +class EVOImpl : public EVO +{ +public: + CDeterministicMNList getListAtChainTip() override + { + return deterministicMNManager == nullptr ? CDeterministicMNList() : deterministicMNManager->GetListAtChainTip(); + } +}; + +class GOVImpl : public GOV +{ +public: + void getAllNewerThan(std::vector &objs, int64_t nMoreThanTime) override + { + if (governance == nullptr) return; + governance->GetAllNewerThan(objs, nMoreThanTime); + } +}; + +class LLMQImpl : public LLMQ +{ +public: + size_t getInstantSentLockCount() override + { + return llmq::quorumInstantSendManager == nullptr ? 0 : llmq::quorumInstantSendManager->GetInstantSendLockCount(); + } +}; + +namespace Masternode = interfaces::Masternode; +class MasternodeSyncImpl : public Masternode::Sync +{ +public: + bool isSynced() override + { + return ::masternodeSync == nullptr ? false : ::masternodeSync->IsSynced(); + } + bool isBlockchainSynced() override + { + return ::masternodeSync == nullptr ? false : ::masternodeSync->IsBlockchainSynced(); + } + std::string getSyncStatus() override + { + return ::masternodeSync == nullptr ? "" : ::masternodeSync->GetSyncStatus(); + } +}; + +namespace CoinJoin = interfaces::CoinJoin; +class CoinJoinOptionsImpl : public CoinJoin::Options +{ +public: + int getRounds() override + { + return CCoinJoinClientOptions::GetRounds(); + } + int getAmount() override + { + return CCoinJoinClientOptions::GetAmount(); + } + void setEnabled(bool fEnabled) override + { + return CCoinJoinClientOptions::SetEnabled(fEnabled); + } + void setMultiSessionEnabled(bool fEnabled) override + { + CCoinJoinClientOptions::SetMultiSessionEnabled(fEnabled); + } + void setRounds(int nRounds) override + { + CCoinJoinClientOptions::SetRounds(nRounds); + } + void setAmount(CAmount amount) override + { + CCoinJoinClientOptions::SetAmount(amount); + } + bool isEnabled() override + { + return CCoinJoinClientOptions::IsEnabled(); + } + bool isMultiSessionEnabled() override + { + return CCoinJoinClientOptions::IsMultiSessionEnabled(); + } + bool isCollateralAmount(CAmount nAmount) override + { + return CCoinJoin::IsCollateralAmount(nAmount); + } + CAmount getMinCollateralAmount() override + { + return CCoinJoin::GetCollateralAmount(); + } + CAmount getMaxCollateralAmount() override + { + return CCoinJoin::GetMaxCollateralAmount(); + } + CAmount getSmallestDenomination() override + { + return CCoinJoin::GetSmallestDenomination(); + } + bool isDenominated(CAmount nAmount) override + { + return CCoinJoin::IsDenominatedAmount(nAmount); + } + std::array getStandardDenominations() override + { + return CCoinJoin::GetStandardDenominations(); + } +}; + +class NodeImpl : public Node +{ +public: + EVOImpl m_evo; + GOVImpl m_gov; + LLMQImpl m_llmq; + MasternodeSyncImpl m_masternodeSync; + CoinJoinOptionsImpl m_coinjoin; + + NodeImpl(NodeContext* context) { setContext(context); } + void initError(const bilingual_str& message) override { InitError(message); } + bool parseParameters(int argc, const char* const argv[], std::string& error) override + { + return gArgs.ParseParameters(argc, argv, error); + } + bool readConfigFiles(std::string& error) override { return gArgs.ReadConfigFiles(error, true); } + bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); } + bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } + void selectParams(const std::string& network) override { SelectParams(network); } + bool initSettings(std::string& error) override { return gArgs.InitSettings(error); } + uint64_t getAssumedBlockchainSize() override { return Params().AssumedBlockchainSize(); } + uint64_t getAssumedChainStateSize() override { return Params().AssumedChainStateSize(); } + std::string getNetwork() override { return Params().NetworkIDString(); } + void initLogging() override { InitLogging(gArgs); } + void initParameterInteraction() override { InitParameterInteraction(gArgs); } + std::string getWarnings() override { return GetWarnings(true); } + uint64_t getLogCategories() override { return LogInstance().GetCategoryMask(); } + bool baseInitialize() override + { + return AppInitBasicSetup(gArgs) && AppInitParameterInteraction(gArgs) && AppInitSanityChecks() && + AppInitLockDataDirectory() && AppInitInterfaces(*m_context); + } + bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override + { + return AppInitMain(m_context_ref, *m_context, tip_info); + } + void appShutdown() override + { + Interrupt(*m_context); + Shutdown(*m_context); + } + void appPrepareShutdown() override + { + Interrupt(*m_context); + StartRestart(); + PrepareShutdown(*m_context); + } + void startShutdown() override { StartShutdown(); } + bool shutdownRequested() override { return ShutdownRequested(); } + void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } + void setupServerArgs() override { return SetupServerArgs(*m_context); } + bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } + size_t getNodeCount(CConnman::NumConnections flags) override + { + return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0; + } + bool getNodesStats(NodesStats& stats) override + { + stats.clear(); + + if (m_context->connman) { + std::vector stats_temp; + m_context->connman->GetNodeStats(stats_temp); + + stats.reserve(stats_temp.size()); + for (auto& node_stats_temp : stats_temp) { + stats.emplace_back(std::move(node_stats_temp), false, CNodeStateStats()); + } + + // Try to retrieve the CNodeStateStats for each node. + TRY_LOCK(::cs_main, lockMain); + if (lockMain) { + for (auto& node_stats : stats) { + std::get<1>(node_stats) = + GetNodeStateStats(std::get<0>(node_stats).nodeid, std::get<2>(node_stats)); + } + } + return true; + } + return false; + } + bool getBanned(banmap_t& banmap) override + { + if (m_context->banman) { + m_context->banman->GetBanned(banmap); + return true; + } + return false; + } + bool ban(const CNetAddr& net_addr, int64_t ban_time_offset) override + { + if (m_context->banman) { + m_context->banman->Ban(net_addr, ban_time_offset); + return true; + } + return false; + } + bool unban(const CSubNet& ip) override + { + if (m_context->banman) { + m_context->banman->Unban(ip); + return true; + } + return false; + } + bool disconnect(const CNetAddr& net_addr) override + { + if (m_context->connman) { + return m_context->connman->DisconnectNode(net_addr); + } + return false; + } + bool disconnect(NodeId id) override + { + if (m_context->connman) { + return m_context->connman->DisconnectNode(id); + } + return false; + } + int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; } + int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; } + size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; } + size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; } + bool getHeaderTip(int& height, int64_t& block_time) override + { + LOCK(::cs_main); + if (::pindexBestHeader) { + height = ::pindexBestHeader->nHeight; + block_time = ::pindexBestHeader->GetBlockTime(); + return true; + } + return false; + } + int getNumBlocks() override + { + LOCK(::cs_main); + return ::ChainActive().Height(); + } + uint256 getBestBlockHash() override + { + const CBlockIndex* tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip()); + return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash(); + } + int64_t getLastBlockTime() override + { + LOCK(::cs_main); + if (::ChainActive().Tip()) { + return ::ChainActive().Tip()->GetBlockTime(); + } + return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network + } + std::string getLastBlockHash() override + { + LOCK(::cs_main); + if (::ChainActive().Tip()) { + return ::ChainActive().Tip()->GetBlockHash().ToString(); + } + return Params().GenesisBlock().GetHash().ToString(); // Genesis block's hash of current network + } + double getVerificationProgress() override + { + const CBlockIndex* tip; + { + LOCK(::cs_main); + tip = ::ChainActive().Tip(); + } + return GuessVerificationProgress(Params().TxData(), tip); + } + bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } + bool getReindex() override { return ::fReindex; } + bool getImporting() override { return ::fImporting; } + void setNetworkActive(bool active) override + { + if (m_context->connman) { + m_context->connman->SetNetworkActive(active); + } + } + bool getNetworkActive() override { return m_context->connman && m_context->connman->GetNetworkActive(); } + CFeeRate getDustRelayFee() override { return ::dustRelayFee; } + UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override + { + JSONRPCRequest req(m_context_ref); + req.params = params; + req.strMethod = command; + req.URI = uri; + return ::tableRPC.execute(req); + } + std::vector listRpcCommands() override { return ::tableRPC.listCommands(); } + void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); } + void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); } + bool getUnspentOutput(const COutPoint& output, Coin& coin) override + { + LOCK(::cs_main); + return ::ChainstateActive().CoinsTip().GetCoin(output, coin); + } + WalletClient& walletClient() override + { + return *Assert(m_context->wallet_client); + } + + EVO& evo() override { return m_evo; } + GOV& gov() override { return m_gov; } + LLMQ& llmq() override { return m_llmq; } + Masternode::Sync& masternodeSync() override { return m_masternodeSync; } + CoinJoin::Options& coinJoinOptions() override { return m_coinjoin; } + + std::unique_ptr handleInitMessage(InitMessageFn fn) override + { + return MakeHandler(::uiInterface.InitMessage_connect(fn)); + } + std::unique_ptr handleMessageBox(MessageBoxFn fn) override + { + return MakeHandler(::uiInterface.ThreadSafeMessageBox_connect(fn)); + } + std::unique_ptr handleQuestion(QuestionFn fn) override + { + return MakeHandler(::uiInterface.ThreadSafeQuestion_connect(fn)); + } + std::unique_ptr handleShowProgress(ShowProgressFn fn) override + { + return MakeHandler(::uiInterface.ShowProgress_connect(fn)); + } + std::unique_ptr handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override + { + return MakeHandler(::uiInterface.NotifyNumConnectionsChanged_connect(fn)); + } + std::unique_ptr handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) override + { + return MakeHandler(::uiInterface.NotifyNetworkActiveChanged_connect(fn)); + } + std::unique_ptr handleNotifyAlertChanged(NotifyAlertChangedFn fn) override + { + return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn)); + } + std::unique_ptr handleBannedListChanged(BannedListChangedFn fn) override + { + return MakeHandler(::uiInterface.BannedListChanged_connect(fn)); + } + std::unique_ptr handleNotifyBlockTip(NotifyBlockTipFn fn) override + { + return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](bool initial_download, const CBlockIndex* block) { + fn(initial_download, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()}, + GuessVerificationProgress(Params().TxData(), block)); + })); + } + std::unique_ptr handleNotifyChainLock(NotifyChainLockFn fn) override + { + return MakeHandler(::uiInterface.NotifyChainLock_connect([fn](const std::string& bestChainLockHash, int bestChainLockHeight) { + fn(bestChainLockHash, bestChainLockHeight); + })); + } + std::unique_ptr handleNotifyHeaderTip(NotifyHeaderTipFn fn) override + { + return MakeHandler( + ::uiInterface.NotifyHeaderTip_connect([fn](bool initial_download, const CBlockIndex* block) { + fn(initial_download, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()}, + /* verification progress is unused when a header was received */ 0); + })); + } + std::unique_ptr handleNotifyMasternodeListChanged(NotifyMasternodeListChangedFn fn) override + { + return MakeHandler( + ::uiInterface.NotifyMasternodeListChanged_connect([fn](const CDeterministicMNList& newList) { + fn(newList); + })); + } + std::unique_ptr handleNotifyAdditionalDataSyncProgressChanged(NotifyAdditionalDataSyncProgressChangedFn fn) override + { + return MakeHandler( + ::uiInterface.NotifyAdditionalDataSyncProgressChanged_connect([fn](double nSyncProgress) { + fn(nSyncProgress); + })); + } + NodeContext* context() override { return m_context; } + void setContext(NodeContext* context) override + { + m_context = context; + if (context) { + m_context_ref = *context; + } else { + m_context_ref = std::nullopt; + } + } + NodeContext* m_context{nullptr}; + CoreContext m_context_ref{std::nullopt}; +}; + +bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock& lock, const CChain& active) +{ + if (!index) return false; + if (block.m_hash) *block.m_hash = index->GetBlockHash(); + if (block.m_height) *block.m_height = index->nHeight; + if (block.m_time) *block.m_time = index->GetBlockTime(); + if (block.m_max_time) *block.m_max_time = index->GetBlockTimeMax(); + if (block.m_mtp_time) *block.m_mtp_time = index->GetMedianTimePast(); + if (block.m_in_active_chain) *block.m_in_active_chain = active[index->nHeight] == index; + if (block.m_next_block) FillBlock(active[index->nHeight] == index ? active[index->nHeight + 1] : nullptr, *block.m_next_block, lock, active); + if (block.m_data) { + REVERSE_LOCK(lock); + if (!ReadBlockFromDisk(*block.m_data, index, Params().GetConsensus())) block.m_data->SetNull(); + } + return true; +} + +class NotificationsProxy : public CValidationInterface +{ +public: + explicit NotificationsProxy(std::shared_ptr notifications) + : m_notifications(std::move(notifications)) {} + virtual ~NotificationsProxy() = default; + void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) override + { + m_notifications->TransactionAddedToMempool(tx, nAcceptTime); + } + void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override + { + m_notifications->TransactionRemovedFromMempool(tx, reason); + } + void BlockConnected(const std::shared_ptr& block, const CBlockIndex* index) override + { + m_notifications->BlockConnected(*block, index->nHeight); + } + void BlockDisconnected(const std::shared_ptr& block, const CBlockIndex* index) override + { + m_notifications->BlockDisconnected(*block, index->nHeight); + } + void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override + { + m_notifications->UpdatedBlockTip(); + } + void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); } + void NotifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr& clsig) override + { + m_notifications->NotifyChainLock(pindexChainLock, clsig); + } + void NotifyTransactionLock(const CTransactionRef &tx, const std::shared_ptr& islock) override + { + m_notifications->NotifyTransactionLock(tx, islock); + } + std::shared_ptr m_notifications; +}; + +class NotificationsHandlerImpl : public Handler +{ +public: + explicit NotificationsHandlerImpl(std::shared_ptr notifications) + : m_proxy(std::make_shared(std::move(notifications))) + { + RegisterSharedValidationInterface(m_proxy); + } + ~NotificationsHandlerImpl() override { disconnect(); } + void disconnect() override + { + if (m_proxy) { + UnregisterSharedValidationInterface(m_proxy); + m_proxy.reset(); + } + } + std::shared_ptr m_proxy; +}; + +class RpcHandlerImpl : public Handler +{ +public: + explicit RpcHandlerImpl(const CRPCCommand& command) : m_command(command), m_wrapped_command(&command) + { + m_command.actor = [this](const JSONRPCRequest& request, UniValue& result, bool last_handler) { + if (!m_wrapped_command) return false; + try { + return m_wrapped_command->actor(request, result, last_handler); + } catch (const UniValue& e) { + // If this is not the last handler and a wallet not found + // exception was thrown, return false so the next handler can + // try to handle the request. Otherwise, reraise the exception. + if (!last_handler) { + const UniValue& code = e["code"]; + if (code.isNum() && code.get_int() == RPC_WALLET_NOT_FOUND) { + return false; + } + } + throw; + } + }; + ::tableRPC.appendCommand(m_command.name, &m_command); + } + + void disconnect() override final + { + if (m_wrapped_command) { + m_wrapped_command = nullptr; + ::tableRPC.removeCommand(m_command.name, &m_command); + } + } + + ~RpcHandlerImpl() override { disconnect(); } + + CRPCCommand m_command; + const CRPCCommand* m_wrapped_command; +}; + +class ChainImpl : public Chain +{ +public: + explicit ChainImpl(NodeContext& node) : m_node(node) {} + std::optional getHeight() override + { + LOCK(::cs_main); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + int height = active.Height(); + if (height >= 0) { + return height; + } + return std::nullopt; + } + uint256 getBlockHash(int height) override + { + LOCK(::cs_main); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + CBlockIndex* block = active[height]; + assert(block != nullptr); + return block->GetBlockHash(); + } + bool haveBlockOnDisk(int height) override + { + LOCK(cs_main); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + CBlockIndex* block = active[height]; + return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0; + } + std::optional findFork(const uint256& hash, std::optional* height) override + { + LOCK(cs_main); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(hash); + const CBlockIndex* fork = block ? active.FindFork(block) : nullptr; + if (height) { + if (block) { + *height = block->nHeight; + } else { + height->reset(); + } + } + if (fork) { + return fork->nHeight; + } + return std::nullopt; + } + CBlockLocator getTipLocator() override + { + LOCK(cs_main); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + return active.GetLocator(); + } + std::optional findLocatorFork(const CBlockLocator& locator) override + { + LOCK(cs_main); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + if (CBlockIndex* fork = g_chainman.m_blockman.FindForkInGlobalIndex(active, locator)) { + return fork->nHeight; + } + return std::nullopt; + } + bool checkFinalTx(const CTransaction& tx) override + { + LOCK(cs_main); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + return CheckFinalTx(active.Tip(), tx); + } + bool findBlock(const uint256& hash, const FoundBlock& block) override + { + WAIT_LOCK(cs_main, lock); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + return FillBlock(g_chainman.m_blockman.LookupBlockIndex(hash), block, lock, active); + } + bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override + { + WAIT_LOCK(cs_main, lock); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + return FillBlock(active.FindEarliestAtLeast(min_time, min_height), block, lock, active); + } + bool findAncestorByHeight(const uint256& block_hash, int ancestor_height, const FoundBlock& ancestor_out) override + { + WAIT_LOCK(cs_main, lock); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + if (const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) { + if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) { + return FillBlock(ancestor, ancestor_out, lock, active); + } + } + return FillBlock(nullptr, ancestor_out, lock, active); + } + bool findAncestorByHash(const uint256& block_hash, const uint256& ancestor_hash, const FoundBlock& ancestor_out) override + { + WAIT_LOCK(cs_main, lock); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash); + const CBlockIndex* ancestor = g_chainman.m_blockman.LookupBlockIndex(ancestor_hash); + if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr; + return FillBlock(ancestor, ancestor_out, lock, active); + } + bool findCommonAncestor(const uint256& block_hash1, const uint256& block_hash2, const FoundBlock& ancestor_out, const FoundBlock& block1_out, const FoundBlock& block2_out) override + { + WAIT_LOCK(cs_main, lock); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + const CBlockIndex* block1 = g_chainman.m_blockman.LookupBlockIndex(block_hash1); + const CBlockIndex* block2 = g_chainman.m_blockman.LookupBlockIndex(block_hash2); + const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr; + return FillBlock(ancestor, ancestor_out, lock, active) & FillBlock(block1, block1_out, lock, active) & FillBlock(block2, block2_out, lock, active); + } + void findCoins(std::map& coins) override { return FindCoins(m_node, coins); } + double guessVerificationProgress(const uint256& block_hash) override + { + LOCK(cs_main); + return GuessVerificationProgress(Params().TxData(), g_chainman.m_blockman.LookupBlockIndex(block_hash)); + } + bool hasBlocks(const uint256& block_hash, int min_height, std::optional max_height) override + { + // hasBlocks returns true if all ancestors of block_hash in specified + // range have block data (are not pruned), false if any ancestors in + // specified range are missing data. + // + // For simplicity and robustness, min_height and max_height are only + // used to limit the range, and passing min_height that's too low or + // max_height that's too high will not crash or change the result. + LOCK(::cs_main); + if (CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) { + if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height); + for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) { + // Check pprev to not segfault if min_height is too low + if (block->nHeight <= min_height || !block->pprev) return true; + } + } + return false; + } + bool hasDescendantsInMempool(const uint256& txid) override + { + if (!m_node.mempool) return false; + LOCK(m_node.mempool->cs); + auto it = m_node.mempool->GetIter(txid); + return it && (*it)->GetCountWithDescendants() > 1; + } + bool broadcastTransaction(const CTransactionRef& tx, std::string& err_string, const CAmount& max_tx_fee, bool relay) override + { + const TransactionError err = BroadcastTransaction(m_node, tx, err_string, max_tx_fee, relay, /*wait_callback*/ false); + // Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures. + // Note: this will need to be updated if BroadcastTransactions() is updated to return other non-mempool failures + // that Chain clients do not need to know about. + return TransactionError::OK == err; + } + void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override + { + ancestors = descendants = 0; + if (!m_node.mempool) return; + m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants); + } + void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override + { + limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + } + bool checkChainLimits(const CTransactionRef& tx) override + { + if (!m_node.mempool) return true; + LockPoints lp; + CTxMemPoolEntry entry(tx, 0, 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(m_node.mempool->cs); + return m_node.mempool->CalculateMemPoolAncestors( + entry, ancestors, limit_ancestor_count, limit_ancestor_size, + limit_descendant_count, limit_descendant_size, unused_error_string); + } + CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override + { + if (!m_node.fee_estimator) return {}; + return m_node.fee_estimator->estimateSmartFee(num_blocks, calc, conservative); + } + unsigned int estimateMaxBlocks() override + { + if (!m_node.fee_estimator) return 0; + return m_node.fee_estimator->HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); + } + CFeeRate mempoolMinFee() override + { + if (!m_node.mempool) return {}; + return m_node.mempool->GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + } + CFeeRate relayMinFee() override { return ::minRelayTxFee; } + CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; } + CFeeRate relayDustFee() override { return ::dustRelayFee; } + bool havePruned() override + { + LOCK(cs_main); + return ::fHavePruned; + } + bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !::ChainstateActive().IsInitialBlockDownload(); } + bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } + bool shutdownRequested() override { return ShutdownRequested(); } + int64_t getAdjustedTime() override { return GetAdjustedTime(); } + void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); } + void initWarning(const bilingual_str& message) override { InitWarning(message); } + void initError(const bilingual_str& message) override { InitError(message); } + void showProgress(const std::string& title, int progress, bool resume_possible) override + { + ::uiInterface.ShowProgress(title, progress, resume_possible); + } + std::unique_ptr handleNotifications(std::shared_ptr notifications) override + { + return std::make_unique(std::move(notifications)); + } + void waitForNotificationsIfTipChanged(const uint256& old_tip) override + { + if (!old_tip.IsNull()) { + LOCK(::cs_main); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + if (old_tip == active.Tip()->GetBlockHash()) return; + } + SyncWithValidationInterfaceQueue(); + } + std::unique_ptr handleRpc(const CRPCCommand& command) override + { + return std::make_unique(command); + } + bool rpcEnableDeprecated(const std::string& method) override { return IsDeprecatedRPCEnabled(method); } + void rpcRunLater(const std::string& name, std::function fn, int64_t seconds) override + { + RPCRunLater(name, std::move(fn), seconds); + } + util::SettingsValue getRwSetting(const std::string& name) override + { + util::SettingsValue result; + gArgs.LockSettings([&](const util::Settings& settings) { + if (const util::SettingsValue* value = util::FindKey(settings.rw_settings, name)) { + result = *value; + } + }); + return result; + } + bool updateRwSetting(const std::string& name, const util::SettingsValue& value) override + { + gArgs.LockSettings([&](util::Settings& settings) { + if (value.isNull()) { + settings.rw_settings.erase(name); + } else { + settings.rw_settings[name] = value; + } + }); + return gArgs.WriteSettingsFile(); + } + void requestMempoolTransactions(Notifications& notifications) override + { + if (!m_node.mempool) return; + LOCK2(::cs_main, m_node.mempool->cs); + for (const CTxMemPoolEntry& entry : m_node.mempool->mapTx) { + notifications.TransactionAddedToMempool(entry.GetSharedTx(), 0); + } + } + NodeContext& m_node; +}; +} // namespace +} // namespace node + +namespace interfaces { +std::unique_ptr MakeNode(NodeContext* context) { return std::make_unique(context); } +std::unique_ptr MakeChain(NodeContext& node) { return std::make_unique(node); } +} // namespace interfaces diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index fc3c246c2960..915863e84d4b 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -346,7 +346,7 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead window->setClientModel(clientModel, &tip_info); #ifdef ENABLE_WALLET if (WalletModel::isWalletEnabled()) { - m_wallet_controller = new WalletController(m_node, optionsModel, this); + m_wallet_controller = new WalletController(*clientModel, this); window->setWalletController(m_wallet_controller); if (paymentServer) { paymentServer->setOptionsModel(optionsModel); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 2eac328c1e0d..c3bcfbf8ca43 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -137,6 +137,37 @@ void ClientModel::getAllGovernanceObjects(std::vector &obj) m_node.gov().getAllNewerThan(obj, 0); } +int ClientModel::getNumBlocks() const +{ + if (m_cached_num_blocks == -1) { + m_cached_num_blocks = m_node.getNumBlocks(); + } + return m_cached_num_blocks; +} + +uint256 ClientModel::getBestBlockHash() +{ + uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)}; + + if (!tip.IsNull()) { + return tip; + } + + // Lock order must be: first `cs_main`, then `m_cached_tip_mutex`. + // The following will lock `cs_main` (and release it), so we must not + // own `m_cached_tip_mutex` here. + tip = m_node.getBestBlockHash(); + + LOCK(m_cached_tip_mutex); + // We checked that `m_cached_tip_blocks` is not null above, but then we + // released the mutex `m_cached_tip_mutex`, so it could have changed in the + // meantime. Thus, check again. + if (m_cached_tip_blocks.IsNull()) { + m_cached_tip_blocks = tip; + } + return m_cached_tip_blocks; +} + void ClientModel::updateNumConnections(int numConnections) { Q_EMIT numConnectionsChanged(numConnections); @@ -258,7 +289,7 @@ static void BannedListChanged(ClientModel *clientmodel) assert(invoked); } -static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int height, int64_t blockTime, const std::string& strBlockHash, double verificationProgress, bool fHeader) +static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, interfaces::BlockTip tip, double verificationProgress, bool fHeader) { // lock free async UI updates in case we have a new block tip // during initial sync, only update the UI if the last update @@ -271,17 +302,20 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int heig if (fHeader) { // cache best headers time and height to reduce future cs_main locks - clientmodel->cachedBestHeaderHeight = height; - clientmodel->cachedBestHeaderTime = blockTime; + clientmodel->cachedBestHeaderHeight = tip.block_height; + clientmodel->cachedBestHeaderTime = tip.block_time; + } else { + clientmodel->m_cached_num_blocks = tip.block_height; + WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;); } // During initial sync, block notifications, and header notifications from reindexing are both throttled. if (!initialSync || (fHeader && !clientmodel->node().getReindex()) || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { //pass an async signal to the UI thread bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection, - Q_ARG(int, height), - Q_ARG(QDateTime, QDateTime::fromTime_t(blockTime)), - Q_ARG(QString, QString::fromStdString(strBlockHash)), + Q_ARG(int, tip.block_height), + Q_ARG(QDateTime, QDateTime::fromTime_t(tip.block_time)), + Q_ARG(QString, QString::fromStdString(tip.block_hash.ToString())), Q_ARG(double, verificationProgress), Q_ARG(bool, fHeader)); assert(invoked); @@ -318,9 +352,9 @@ void ClientModel::subscribeToCoreSignals() m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1)); m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this)); m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this)); - m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, false)); + m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false)); m_handler_notify_chainlock = m_node.handleNotifyChainLock(std::bind(NotifyChainLock, this, std::placeholders::_1, std::placeholders::_2)); - m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, true)); + m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true)); m_handler_notify_masternodelist_changed = m_node.handleNotifyMasternodeListChanged(std::bind(NotifyMasternodeListChanged, this, std::placeholders::_1)); m_handler_notify_additional_data_sync_progess_changed = m_node.handleNotifyAdditionalDataSyncProgressChanged(std::bind(NotifyAdditionalDataSyncProgressChanged, this, std::placeholders::_1)); } diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 330ad8d51fde..8a22a3143abb 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -14,6 +14,7 @@ #include #include +#include class BanTableModel; class OptionsModel; @@ -61,6 +62,8 @@ class ClientModel : public QObject //! Return number of connections, default is in- and outbound (total) int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const; + int getNumBlocks() const; + uint256 getBestBlockHash(); int getHeaderTipHeight() const; int64_t getHeaderTipTime() const; @@ -84,9 +87,13 @@ class ClientModel : public QObject bool getProxyInfo(std::string& ip_port) const; - // caches for the best header + // caches for the best header: hash, number of blocks and block time mutable std::atomic cachedBestHeaderHeight; mutable std::atomic cachedBestHeaderTime; + mutable std::atomic m_cached_num_blocks{-1}; + + Mutex m_cached_tip_mutex; + uint256 m_cached_tip_blocks GUARDED_BY(m_cached_tip_mutex){}; private: interfaces::Node& m_node; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 88958b875991..4f4dd56a6be7 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -512,7 +512,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * { CPubKey pubkey; PKHash *pkhash = std::get_if(&address); - if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, CKeyID(*pkhash), pubkey)) + if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, ToKeyID(*pkhash), pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); } diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 17590691c1f4..f7b522aef34e 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index 14cba40c6e0a..5f91d083e81f 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index d4be4b565124..a60eafb56c2d 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -36,13 +36,17 @@ #include #endif -#include +#include #include #include class OptionsModel; +namespace interfaces { +class Node; +} // namespace interfaces + QT_BEGIN_NAMESPACE class QApplication; class QByteArray; diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index 20b994189934..d3629ef91427 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h index c7195d7596ba..53dd33f6fc9a 100644 --- a/src/qt/receiverequestdialog.h +++ b/src/qt/receiverequestdialog.h @@ -5,10 +5,12 @@ #ifndef BITCOIN_QT_RECEIVEREQUESTDIALOG_H #define BITCOIN_QT_RECEIVEREQUESTDIALOG_H -#include +#include #include +class WalletModel; + namespace Ui { class ReceiveRequestDialog; } diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index e4183f63d253..635d14d935fe 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index 2f9d1a7a9cbb..85ab29fc8e58 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -5,12 +5,14 @@ #ifndef BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H #define BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H -#include +#include #include #include #include +class WalletModel; + class RecentRequestEntry { public: diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index dbcaa5fa437b..944e32c55cc1 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index e5d7a5cb6559..aad10add141a 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -5,12 +5,16 @@ #ifndef BITCOIN_QT_SENDCOINSENTRY_H #define BITCOIN_QT_SENDCOINSENTRY_H -#include +#include #include class WalletModel; +namespace interfaces { +class Node; +} // namespace interfaces + namespace Ui { class SendCoinsEntry; } diff --git a/src/qt/sendcoinsrecipient.h b/src/qt/sendcoinsrecipient.h new file mode 100644 index 000000000000..1d3a1c416694 --- /dev/null +++ b/src/qt/sendcoinsrecipient.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011-2019 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_QT_SENDCOINSRECIPIENT_H +#define BITCOIN_QT_SENDCOINSRECIPIENT_H + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include +#include + +#include + +#include + +class SendCoinsRecipient +{ +public: + explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { } + explicit SendCoinsRecipient(const QString &addr, const QString &_label, const CAmount& _amount, const QString &_message): + address(addr), label(_label), amount(_amount), message(_message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {} + + // If from an unauthenticated payment request, this is used for storing + // the addresses, e.g. address-A
address-B
address-C. + // Info: As we don't need to process addresses in here when using + // payment requests, we can abuse it for displaying an address list. + // Todo: This is a hack, should be replaced with a cleaner solution! + QString address; + QString label; + CAmount amount; + // If from a payment request, this is used for storing the memo + QString message; + // Keep the payment request around as a serialized string to ensure + // load/store is lossless. + std::string sPaymentRequest; + // Empty if no authentication or invalid signature/cert/etc. + QString authenticatedMerchant; + + bool fSubtractFeeFromAmount; // memory only + + static const int CURRENT_VERSION = 1; + int nVersion; + + SERIALIZE_METHODS(SendCoinsRecipient, obj) + { + std::string address_str, label_str, message_str, auth_merchant_str; + SER_WRITE(obj, address_str = obj.address.toStdString()); + SER_WRITE(obj, label_str = obj.label.toStdString()); + SER_WRITE(obj, message_str = obj.message.toStdString()); + SER_WRITE(obj, auth_merchant_str = obj.authenticatedMerchant.toStdString()); + + READWRITE(obj.nVersion, address_str, label_str, obj.amount, message_str, obj.sPaymentRequest, auth_merchant_str); + + SER_READ(obj, obj.address = QString::fromStdString(address_str)); + SER_READ(obj, obj.label = QString::fromStdString(label_str)); + SER_READ(obj, obj.message = QString::fromStdString(message_str)); + SER_READ(obj, obj.authenticatedMerchant = QString::fromStdString(auth_merchant_str)); + } +}; + +#endif // BITCOIN_QT_SENDCOINSRECIPIENT_H diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp index ba4b5665772b..3a7a93c4ac01 100644 --- a/src/qt/test/addressbooktests.cpp +++ b/src/qt/test/addressbooktests.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -105,8 +106,9 @@ void TestAddAddressesToSendBook(interfaces::Node& node) // Initialize relevant QT models. OptionsModel optionsModel(node); + ClientModel clientModel(node, &optionsModel); AddWallet(wallet); - WalletModel walletModel(interfaces::MakeWallet(wallet), node, &optionsModel); + WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel); RemoveWallet(wallet, std::nullopt); EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress); editAddressDialog.setModel(walletModel.getAddressTableModel()); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 470d74b8426d..75df8da71d42 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -123,7 +123,7 @@ void TestGUI(interfaces::Node& node) { WalletRescanReserver reserver(wallet.get()); reserver.reserve(); - CWallet::ScanResult result = wallet->ScanForWalletTransactions(wallet->chain().getBlockHash(0), {} /* stop_block */, reserver, true /* fUpdate */); + CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, 0 /* start_height */, {} /* max_height */, reserver, true /* fUpdate */); QCOMPARE(result.status, CWallet::ScanResult::SUCCESS); QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash()); QVERIFY(result.last_failed_block.IsNull()); @@ -135,7 +135,7 @@ void TestGUI(interfaces::Node& node) TransactionView transactionView; OptionsModel optionsModel(node); ClientModel clientModel(node, &optionsModel); - WalletModel walletModel(interfaces::MakeWallet(wallet), node, &optionsModel);; + WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel);; sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 77d6f1534fa4..1b65631868ed 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -17,9 +17,10 @@ #include #include #include -#include +#include #include