diff options
-rwxr-xr-x | contrib/devtools/gen-manpages.sh | 5 | ||||
-rw-r--r-- | doc/man/Makefile.am | 4 | ||||
-rw-r--r-- | doc/man/bitcoin-cli.1 | 2 | ||||
-rw-r--r-- | doc/man/bitcoin-qt.1 | 2 | ||||
-rw-r--r-- | doc/man/bitcoin-tx.1 | 2 | ||||
-rw-r--r-- | doc/man/bitcoin-util.1 | 5 | ||||
-rw-r--r-- | doc/man/bitcoin-wallet.1 | 2 | ||||
-rw-r--r-- | doc/man/bitcoind.1 | 2 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/bitcoin-util.cpp | 14 | ||||
-rw-r--r-- | src/coins.cpp | 2 | ||||
-rw-r--r-- | src/coins.h | 29 | ||||
-rw-r--r-- | src/index/blockfilterindex.h | 6 | ||||
-rw-r--r-- | src/init.cpp | 4 | ||||
-rw-r--r-- | src/net.h | 23 | ||||
-rw-r--r-- | src/net_processing.cpp | 264 | ||||
-rw-r--r-- | src/net_processing.h | 221 | ||||
-rw-r--r-- | src/script/sigcache.h | 22 | ||||
-rw-r--r-- | src/test/denialofservice_tests.cpp | 16 | ||||
-rw-r--r-- | src/test/util/setup_common.cpp | 6 | ||||
-rw-r--r-- | src/txmempool.cpp | 2 | ||||
-rw-r--r-- | src/txmempool.h | 16 | ||||
-rw-r--r-- | src/util/hasher.cpp | 19 | ||||
-rw-r--r-- | src/util/hasher.h | 99 | ||||
-rw-r--r-- | src/validation.h | 9 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.h | 2 | ||||
-rw-r--r-- | test/sanitizer_suppressions/tsan | 2 |
27 files changed, 435 insertions, 347 deletions
diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh index b933b264e7..b7bf76ce77 100755 --- a/contrib/devtools/gen-manpages.sh +++ b/contrib/devtools/gen-manpages.sh @@ -14,13 +14,14 @@ BITCOIND=${BITCOIND:-$BINDIR/bitcoind} BITCOINCLI=${BITCOINCLI:-$BINDIR/bitcoin-cli} BITCOINTX=${BITCOINTX:-$BINDIR/bitcoin-tx} WALLET_TOOL=${WALLET_TOOL:-$BINDIR/bitcoin-wallet} +BITCOINUTIL=${BITCOINQT:-$BINDIR/bitcoin-util} BITCOINQT=${BITCOINQT:-$BINDIR/qt/bitcoin-qt} [ ! -x $BITCOIND ] && echo "$BITCOIND not found or not executable." && exit 1 # Don't allow man pages to be generated for binaries built from a dirty tree DIRTY="" -for cmd in $BITCOIND $BITCOINCLI $BITCOINTX $WALLET_TOOL $BITCOINQT; do +for cmd in $BITCOIND $BITCOINCLI $BITCOINTX $WALLET_TOOL $BITCOINUTIL $BITCOINQT; do VERSION_OUTPUT=$($cmd --version) if [[ $VERSION_OUTPUT == *"dirty"* ]]; then DIRTY="${DIRTY}${cmd}\n" @@ -43,7 +44,7 @@ read -r -a BTCVER <<< "$($BITCOINCLI --version | head -n1 | awk -F'[ -]' '{ prin echo "[COPYRIGHT]" > footer.h2m $BITCOIND --version | sed -n '1!p' >> footer.h2m -for cmd in $BITCOIND $BITCOINCLI $BITCOINTX $WALLET_TOOL $BITCOINQT; do +for cmd in $BITCOIND $BITCOINCLI $BITCOINTX $WALLET_TOOL $BITCOINUTIL $BITCOINQT; do cmdname="${cmd##*/}" help2man -N --version-string=${BTCVER[0]} --include=footer.h2m -o ${MANDIR}/${cmdname}.1 ${cmd} sed -i "s/\\\-${BTCVER[1]}//g" ${MANDIR}/${cmdname}.1 diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index edbc0911a1..8f890da532 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -16,6 +16,10 @@ if BUILD_BITCOIN_TX dist_man1_MANS+=bitcoin-tx.1 endif +if BUILD_BITCOIN_UTIL + dist_man1_MANS+=bitcoin-util.1 +endif + if ENABLE_WALLET if BUILD_BITCOIN_WALLET dist_man1_MANS+=bitcoin-wallet.1 diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1 index 588ae81fce..6bcad7006b 100644 --- a/doc/man/bitcoin-cli.1 +++ b/doc/man/bitcoin-cli.1 @@ -2,4 +2,4 @@ .SH NAME bitcoin-cli \- manual page for bitcoin-cli -This is a placefolder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. +This is a placeholder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1 index 9c75e9fe54..ff4d1d2c7a 100644 --- a/doc/man/bitcoin-qt.1 +++ b/doc/man/bitcoin-qt.1 @@ -2,4 +2,4 @@ .SH NAME bitcoin-qt \- manual page for bitcoin-qt -This is a placefolder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. +This is a placeholder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1 index 148a5890b0..776bb46234 100644 --- a/doc/man/bitcoin-tx.1 +++ b/doc/man/bitcoin-tx.1 @@ -2,4 +2,4 @@ .SH NAME bitcoin-tx \- manual page for bitcoin-tx -This is a placefolder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. +This is a placeholder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. diff --git a/doc/man/bitcoin-util.1 b/doc/man/bitcoin-util.1 new file mode 100644 index 0000000000..5c733c6e21 --- /dev/null +++ b/doc/man/bitcoin-util.1 @@ -0,0 +1,5 @@ +.TH BITCOIN-UTIL "1" +.SH NAME +bitcoin-util \- manual page for bitcoin-util + +This is a placeholder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. diff --git a/doc/man/bitcoin-wallet.1 b/doc/man/bitcoin-wallet.1 index 69133b33f7..2da43dec66 100644 --- a/doc/man/bitcoin-wallet.1 +++ b/doc/man/bitcoin-wallet.1 @@ -2,4 +2,4 @@ .SH NAME bitcoin-wallet \- manual page for bitcoin-wallet -This is a placefolder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. +This is a placeholder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1 index de338182ff..2c88f74520 100644 --- a/doc/man/bitcoind.1 +++ b/doc/man/bitcoind.1 @@ -2,4 +2,4 @@ .SH NAME bitcoind \- manual page for bitcoind -This is a placefolder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. +This is a placeholder file. Please follow the instructions in \fIcontrib/devtools/README.md\fR to generate the manual pages after a release. diff --git a/src/Makefile.am b/src/Makefile.am index ab355dee3f..2871df124c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -230,6 +230,7 @@ BITCOIN_CORE_H = \ util/error.h \ util/fees.h \ util/golombrice.h \ + util/hasher.h \ util/macros.h \ util/memory.h \ util/message.h \ @@ -550,6 +551,7 @@ libbitcoin_util_a_SOURCES = \ util/bytevectorhash.cpp \ util/error.cpp \ util/fees.cpp \ + util/hasher.cpp \ util/system.cpp \ util/message.cpp \ util/moneystr.cpp \ diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp index f7e670f4e0..b702a68bdf 100644 --- a/src/bitcoin-util.cpp +++ b/src/bitcoin-util.cpp @@ -40,6 +40,8 @@ static void SetupBitcoinUtilArgs(ArgsManager &argsman) { SetupHelpOptions(argsman); + argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + SetupChainParamsBaseOptions(argsman); } @@ -62,12 +64,14 @@ static int AppInitUtil(int argc, char* argv[]) return EXIT_FAILURE; } - if (argc < 2 || HelpRequested(gArgs)) { + if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { // First part of help message is specific to this utility - std::string strUsage = PACKAGE_NAME " bitcoin-util utility version " + FormatFullVersion() + "\n\n" + - "Usage: bitcoin-util [options] [commands] Do stuff\n" + - "\n"; - strUsage += gArgs.GetHelpMessage(); + std::string strUsage = PACKAGE_NAME " bitcoin-util utility version " + FormatFullVersion() + "\n"; + if (!gArgs.IsArgSet("-version")) { + strUsage += "\n" + "Usage: bitcoin-util [options] [commands] Do stuff\n"; + strUsage += "\n" + gArgs.GetHelpMessage(); + } tfm::format(std::cout, "%s", strUsage); diff --git a/src/coins.cpp b/src/coins.cpp index 14f58e956c..dd84e720e7 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -31,8 +31,6 @@ bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); } -SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {} - CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), cachedCoinsUsage(0) {} size_t CCoinsViewCache::DynamicMemoryUsage() const { diff --git a/src/coins.h b/src/coins.h index a3e241ac90..d2eb42d8cf 100644 --- a/src/coins.h +++ b/src/coins.h @@ -8,11 +8,11 @@ #include <compressor.h> #include <core_memusage.h> -#include <crypto/siphash.h> #include <memusage.h> #include <primitives/transaction.h> #include <serialize.h> #include <uint256.h> +#include <util/hasher.h> #include <assert.h> #include <stdint.h> @@ -82,33 +82,6 @@ public: } }; -class SaltedOutpointHasher -{ -private: - /** Salt */ - const uint64_t k0, k1; - -public: - SaltedOutpointHasher(); - - /** - * This *must* return size_t. With Boost 1.46 on 32-bit systems the - * unordered_map will behave unpredictably if the custom hasher returns a - * uint64_t, resulting in failures when syncing the chain (#4634). - * - * Having the hash noexcept allows libstdc++'s unordered_map to recalculate - * the hash during rehash, so it does not have to cache the value. This - * reduces node's memory by sizeof(size_t). The required recalculation has - * a slight performance penalty (around 1.6%), but this is compensated by - * memory savings of about 9% which allow for a larger dbcache setting. - * - * @see https://gcc.gnu.org/onlinedocs/gcc-9.2.0/libstdc++/manual/manual/unordered_associative.html - */ - size_t operator()(const COutPoint& id) const noexcept { - return SipHashUint256Extra(k0, k1, id.hash, id.n); - } -}; - /** * A Coin in one level of the coins database caching hierarchy. * diff --git a/src/index/blockfilterindex.h b/src/index/blockfilterindex.h index a9c8188bb6..221ac02c9e 100644 --- a/src/index/blockfilterindex.h +++ b/src/index/blockfilterindex.h @@ -9,15 +9,11 @@ #include <chain.h> #include <flatfile.h> #include <index/base.h> +#include <util/hasher.h> /** Interval between compact filter checkpoints. See BIP 157. */ static constexpr int CFCHECKPT_INTERVAL = 1000; -struct FilterHeaderHasher -{ - size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } -}; - /** * BlockFilterIndex is used to store and retrieve block filters, hashes, and headers for a range of * blocks by height. An index is constructed for each supported filter type with its own database diff --git a/src/init.cpp b/src/init.cpp index 9a63fe0e03..09eb76eaee 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1410,8 +1410,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA ChainstateManager& chainman = *Assert(node.chainman); assert(!node.peerman); - node.peerman = std::make_unique<PeerManager>(chainparams, *node.connman, node.banman.get(), - *node.scheduler, chainman, *node.mempool, ignores_incoming_txs); + node.peerman = PeerManager::make(chainparams, *node.connman, node.banman.get(), + *node.scheduler, chainman, *node.mempool, ignores_incoming_txs); RegisterValidationInterface(node.peerman.get()); // sanitize comments per BIP-0014, format user agent and check total size @@ -760,11 +760,30 @@ private: class NetEventsInterface { public: - virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; - virtual bool SendMessages(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_sendProcessing) = 0; + /** Initialize a peer (setup state, queue any initial messages) */ virtual void InitializeNode(CNode* pnode) = 0; + + /** Handle removal of a peer (clear state) */ virtual void FinalizeNode(const CNode& node, bool& update_connection_time) = 0; + /** + * Process protocol messages received from a given node + * + * @param[in] pnode The node which we have received messages from. + * @param[in] interrupt Interrupt condition for processing threads + * @return True if there is more work to be done + */ + virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; + + /** + * Send queued protocol messages to a given node. + * + * @param[in] pnode The node which we are sending messages to. + * @return True if there is more work to be done + */ + virtual bool SendMessages(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_sendProcessing) = 0; + + protected: /** * Protected destructor so that instances can only be deleted by derived classes. diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 8f1cb952f2..cf73b1dae2 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -26,6 +26,7 @@ #include <streams.h> #include <tinyformat.h> #include <txmempool.h> +#include <txrequest.h> #include <util/check.h> // For NDEBUG compile time check #include <util/strencodings.h> #include <util/system.h> @@ -168,6 +169,186 @@ void EraseOrphansFor(NodeId peer); // Internal stuff namespace { +/** + * Data structure for an individual peer. This struct is not protected by + * cs_main since it does not contain validation-critical data. + * + * Memory is owned by shared pointers and this object is destructed when + * the refcount drops to zero. + * + * Mutexes inside this struct must not be held when locking m_peer_mutex. + * + * TODO: move most members from CNodeState to this structure. + * TODO: move remaining application-layer data members from CNode to this structure. + */ +struct Peer { + /** Same id as the CNode object for this peer */ + const NodeId m_id{0}; + + /** Protects misbehavior data members */ + Mutex m_misbehavior_mutex; + /** Accumulated misbehavior score for this peer */ + int m_misbehavior_score GUARDED_BY(m_misbehavior_mutex){0}; + /** Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission). */ + bool m_should_discourage GUARDED_BY(m_misbehavior_mutex){false}; + + /** Protects block inventory data members */ + Mutex m_block_inv_mutex; + /** List of blocks that we'll announce via an `inv` message. + * There is no final sorting before sending, as they are always sent + * immediately and in the order requested. */ + std::vector<uint256> m_blocks_for_inv_relay GUARDED_BY(m_block_inv_mutex); + /** Unfiltered list of blocks that we'd like to announce via a `headers` + * message. If we can't announce via a `headers` message, we'll fall back to + * announcing via `inv`. */ + std::vector<uint256> m_blocks_for_headers_relay GUARDED_BY(m_block_inv_mutex); + /** The final block hash that we sent in an `inv` message to this peer. + * When the peer requests this block, we send an `inv` message to trigger + * the peer to request the next sequence of block hashes. + * Most peers use headers-first syncing, which doesn't use this mechanism */ + uint256 m_continuation_block GUARDED_BY(m_block_inv_mutex) {}; + + /** This peer's reported block height when we connected */ + std::atomic<int> m_starting_height{-1}; + + /** Set of txids to reconsider once their parent transactions have been accepted **/ + std::set<uint256> m_orphan_work_set GUARDED_BY(g_cs_orphans); + + /** Protects m_getdata_requests **/ + Mutex m_getdata_requests_mutex; + /** Work queue of items requested by this peer **/ + std::deque<CInv> m_getdata_requests GUARDED_BY(m_getdata_requests_mutex); + + explicit Peer(NodeId id) : m_id(id) {} +}; + +using PeerRef = std::shared_ptr<Peer>; + +class PeerManagerImpl final : public PeerManager +{ +public: + PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, BanMan* banman, + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, + bool ignore_incoming_txs); + + /** Overridden from CValidationInterface. */ + void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override; + void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override; + void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; + void BlockChecked(const CBlock& block, const BlockValidationState& state) override; + void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override; + + /** Implement NetEventsInterface */ + void InitializeNode(CNode* pnode) override; + void FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) override; + bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override; + bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing); + + /** Implement PeerManager */ + void CheckForStaleTipAndEvictPeers() override; + bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) override; + bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; } + void SetBestHeight(int height) override { m_best_height = height; }; + void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) override; + void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, + const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override; + +private: + /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */ + void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + /** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */ + void EvictExtraOutboundPeers(int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + /** Retrieve unbroadcast transactions from the mempool and reattempt sending to peers */ + void ReattemptInitialBroadcast(CScheduler& scheduler) const; + + /** Get a shared pointer to the Peer object. + * May return an empty shared_ptr if the Peer object can't be found. */ + PeerRef GetPeerRef(NodeId id) const; + + /** Get a shared pointer to the Peer object and remove it from m_peer_map. + * May return an empty shared_ptr if the Peer object can't be found. */ + PeerRef RemovePeer(NodeId id); + + /** + * Potentially mark a node discouraged based on the contents of a BlockValidationState object + * + * @param[in] via_compact_block this bool is passed in because net_processing should + * punish peers differently depending on whether the data was provided in a compact + * block message or not. If the compact block had a valid header, but contained invalid + * txs, the peer should not be punished. See BIP 152. + * + * @return Returns true if the peer was punished (probably disconnected) + */ + bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, + bool via_compact_block, const std::string& message = ""); + + /** + * Potentially disconnect and discourage a node based on the contents of a TxValidationState object + * + * @return Returns true if the peer was punished (probably disconnected) + */ + bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = ""); + + /** Maybe disconnect a peer and discourage future connections from its address. + * + * @param[in] pnode The node to check. + * @return True if the peer was marked for disconnection in this function + */ + bool MaybeDiscourageAndDisconnect(CNode& pnode); + + void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans); + /** Process a single headers message from a peer. */ + void ProcessHeadersMessage(CNode& pfrom, const Peer& peer, + const std::vector<CBlockHeader>& headers, + bool via_compact_block); + + void SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req); + + /** Register with TxRequestTracker that an INV has been received from a + * peer. The announcement parameters are decided in PeerManager and then + * passed to TxRequestTracker. */ + void AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time) + EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + + /** Send a version message to a peer */ + void PushNodeVersion(CNode& pnode, int64_t nTime); + + const CChainParams& m_chainparams; + CConnman& m_connman; + /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */ + BanMan* const m_banman; + ChainstateManager& m_chainman; + CTxMemPool& m_mempool; + TxRequestTracker m_txrequest GUARDED_BY(::cs_main); + + /** The height of the best chain */ + std::atomic<int> m_best_height{-1}; + + int64_t m_stale_tip_check_time; //!< Next time to check for stale tip + + /** Whether this node is running in blocks only mode */ + const bool m_ignore_incoming_txs; + + /** Whether we've completed initial sync yet, for determining when to turn + * on extra block-relay-only peers. */ + bool m_initial_sync_finished{false}; + + /** Protects m_peer_map. This mutex must not be locked while holding a lock + * on any of the mutexes inside a Peer object. */ + mutable Mutex m_peer_mutex; + /** + * Map of all Peer objects, keyed by peer id. This map is protected + * by the m_peer_mutex. Once a shared pointer reference is + * taken, the lock may be released. Individual fields are protected by + * their own locks. + */ + std::map<NodeId, PeerRef> m_peer_map GUARDED_BY(m_peer_mutex); +}; +} // namespace + +namespace { /** Number of nodes with fSyncStarted. */ int nSyncStarted GUARDED_BY(cs_main) = 0; @@ -683,7 +864,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec } // namespace -void PeerManager::PushNodeVersion(CNode& pnode, int64_t nTime) +void PeerManagerImpl::PushNodeVersion(CNode& pnode, int64_t nTime) { // Note that pnode->GetLocalServices() is a reflection of the local // services we were offering when the CNode object was created for this @@ -709,7 +890,7 @@ void PeerManager::PushNodeVersion(CNode& pnode, int64_t nTime) } } -void PeerManager::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time) +void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time) { AssertLockHeld(::cs_main); // For m_txrequest NodeId nodeid = node.GetId(); @@ -745,7 +926,8 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) if (state) state->m_last_block_announcement = time_in_seconds; } -void PeerManager::InitializeNode(CNode *pnode) { +void PeerManagerImpl::InitializeNode(CNode *pnode) +{ CAddress addr = pnode->addr; std::string addrName = pnode->GetAddrName(); NodeId nodeid = pnode->GetId(); @@ -764,7 +946,7 @@ void PeerManager::InitializeNode(CNode *pnode) { } } -void PeerManager::ReattemptInitialBroadcast(CScheduler& scheduler) const +void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) const { std::set<uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs(); @@ -785,7 +967,8 @@ void PeerManager::ReattemptInitialBroadcast(CScheduler& scheduler) const scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta); } -void PeerManager::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) { +void PeerManagerImpl::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) +{ NodeId nodeid = node.GetId(); fUpdateConnectionTime = false; LOCK(cs_main); @@ -839,14 +1022,14 @@ void PeerManager::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) { LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); } -PeerRef PeerManager::GetPeerRef(NodeId id) const +PeerRef PeerManagerImpl::GetPeerRef(NodeId id) const { LOCK(m_peer_mutex); auto it = m_peer_map.find(id); return it != m_peer_map.end() ? it->second : nullptr; } -PeerRef PeerManager::RemovePeer(NodeId id) +PeerRef PeerManagerImpl::RemovePeer(NodeId id) { PeerRef ret; LOCK(m_peer_mutex); @@ -858,7 +1041,8 @@ PeerRef PeerManager::RemovePeer(NodeId id) return ret; } -bool PeerManager::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { +bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) +{ { LOCK(cs_main); CNodeState* state = State(nodeid); @@ -1015,7 +1199,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) return nEvicted; } -void PeerManager::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) +void PeerManagerImpl::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) { assert(howmuch > 0); @@ -1033,8 +1217,8 @@ void PeerManager::Misbehaving(const NodeId pnode, const int howmuch, const std:: } } -bool PeerManager::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, - bool via_compact_block, const std::string& message) +bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, + bool via_compact_block, const std::string& message) { switch (state.GetResult()) { case BlockValidationResult::BLOCK_RESULT_UNSET: @@ -1083,7 +1267,7 @@ bool PeerManager::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationSt return false; } -bool PeerManager::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message) +bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message) { switch (state.GetResult()) { case TxValidationResult::TX_RESULT_UNSET: @@ -1129,9 +1313,16 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); } -PeerManager::PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman, - CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, - bool ignore_incoming_txs) +std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, BanMan* banman, + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, + bool ignore_incoming_txs) +{ + return std::make_unique<PeerManagerImpl>(chainparams, connman, banman, scheduler, chainman, pool, ignore_incoming_txs); +} + +PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, BanMan* banman, + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, + bool ignore_incoming_txs) : m_chainparams(chainparams), m_connman(connman), m_banman(banman), @@ -1171,7 +1362,7 @@ PeerManager::PeerManager(const CChainParams& chainparams, CConnman& connman, Ban * block, remember the recently confirmed transactions, and delete tracked * announcements for them. Also save the time of the last tip update. */ -void PeerManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex) +void PeerManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex) { { LOCK(g_cs_orphans); @@ -1222,7 +1413,7 @@ void PeerManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock, co } } -void PeerManager::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) +void PeerManagerImpl::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) { // To avoid relay problems with transactions that were previously // confirmed, clear our filter of recently confirmed transactions whenever @@ -1247,7 +1438,8 @@ static bool fWitnessesPresentInMostRecentCompactBlock GUARDED_BY(cs_most_recent_ * Maintain state about the best-seen block and fast-announce a compact block * to compatible peers. */ -void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) { +void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) +{ std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true); const CNetMsgMaker msgMaker(PROTOCOL_VERSION); @@ -1294,9 +1486,9 @@ void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ * Update our best height and announce any block hashes which weren't previously * in ::ChainActive() to our peers. */ -void PeerManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) +void PeerManagerImpl::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { - m_best_height = pindexNew->nHeight; + SetBestHeight(pindexNew->nHeight); SetServiceFlagsIBDCache(!fInitialDownload); // Don't relay inventory during initial block download. @@ -1333,7 +1525,8 @@ void PeerManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockInde * Handle invalid block rejection and consequent peer discouragement, maintain which * peers announce compact blocks. */ -void PeerManager::BlockChecked(const CBlock& block, const BlockValidationState& state) { +void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationState& state) +{ LOCK(cs_main); const uint256 hash(block.GetHash()); @@ -1761,7 +1954,8 @@ static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_ma return nFetchFlags; } -void PeerManager::SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req) { +void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req) +{ BlockTransactions resp(req); for (size_t i = 0; i < req.indexes.size(); i++) { if (req.indexes[i] >= block.vtx.size()) { @@ -1776,9 +1970,9 @@ void PeerManager::SendBlockTransactions(CNode& pfrom, const CBlock& block, const m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); } -void PeerManager::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, - const std::vector<CBlockHeader>& headers, - bool via_compact_block) +void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, + const std::vector<CBlockHeader>& headers, + bool via_compact_block) { const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); size_t nCount = headers.size(); @@ -1970,7 +2164,7 @@ void PeerManager::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, * may be added to if accepting an orphan causes its children to be * reconsidered. */ -void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set) +void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set) { AssertLockHeld(cs_main); AssertLockHeld(g_cs_orphans); @@ -2267,9 +2461,9 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar connman.PushMessage(&peer, std::move(msg)); } -void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, - const std::chrono::microseconds time_received, - const std::atomic<bool>& interruptMsgProc) +void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, + const std::chrono::microseconds time_received, + const std::atomic<bool>& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId()); @@ -3755,7 +3949,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat return; } -bool PeerManager::MaybeDiscourageAndDisconnect(CNode& pnode) +bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode) { const NodeId peer_id{pnode.GetId()}; PeerRef peer = GetPeerRef(peer_id); @@ -3797,7 +3991,7 @@ bool PeerManager::MaybeDiscourageAndDisconnect(CNode& pnode) return true; } -bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc) +bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc) { bool fMoreWork = false; @@ -3872,7 +4066,7 @@ bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgP return fMoreWork; } -void PeerManager::ConsiderEviction(CNode& pto, int64_t time_in_seconds) +void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds) { AssertLockHeld(cs_main); @@ -3925,7 +4119,7 @@ void PeerManager::ConsiderEviction(CNode& pto, int64_t time_in_seconds) } } -void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds) +void PeerManagerImpl::EvictExtraOutboundPeers(int64_t time_in_seconds) { // If we have any extra block-relay-only peers, disconnect the youngest unless // it's given us a block -- in which case, compare with the second-youngest, and @@ -4026,7 +4220,7 @@ void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds) } } -void PeerManager::CheckForStaleTipAndEvictPeers() +void PeerManagerImpl::CheckForStaleTipAndEvictPeers() { LOCK(cs_main); @@ -4073,7 +4267,7 @@ public: }; } -bool PeerManager::SendMessages(CNode* pto) +bool PeerManagerImpl::SendMessages(CNode* pto) { PeerRef peer = GetPeerRef(pto->GetId()); const Consensus::Params& consensusParams = m_chainparams.GetConsensus(); diff --git a/src/net_processing.h b/src/net_processing.h index 322fabbe11..eaa3b142a8 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -6,19 +6,13 @@ #ifndef BITCOIN_NET_PROCESSING_H #define BITCOIN_NET_PROCESSING_H -#include <consensus/params.h> #include <net.h> #include <sync.h> -#include <txrequest.h> #include <validationinterface.h> -class BlockTransactionsRequest; -class BlockValidationState; -class CBlockHeader; class CChainParams; class CTxMemPool; class ChainstateManager; -class TxValidationState; extern RecursiveMutex cs_main; extern RecursiveMutex g_cs_orphans; @@ -39,216 +33,39 @@ struct CNodeStateStats { std::vector<int> vHeightInFlight; }; -/** - * Data structure for an individual peer. This struct is not protected by - * cs_main since it does not contain validation-critical data. - * - * Memory is owned by shared pointers and this object is destructed when - * the refcount drops to zero. - * - * Mutexes inside this struct must not be held when locking m_peer_mutex. - * - * TODO: move most members from CNodeState to this structure. - * TODO: move remaining application-layer data members from CNode to this structure. - */ -struct Peer { - /** Same id as the CNode object for this peer */ - const NodeId m_id{0}; - - /** Protects misbehavior data members */ - Mutex m_misbehavior_mutex; - /** Accumulated misbehavior score for this peer */ - int m_misbehavior_score GUARDED_BY(m_misbehavior_mutex){0}; - /** Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission). */ - bool m_should_discourage GUARDED_BY(m_misbehavior_mutex){false}; - - /** Protects block inventory data members */ - Mutex m_block_inv_mutex; - /** List of blocks that we'll announce via an `inv` message. - * There is no final sorting before sending, as they are always sent - * immediately and in the order requested. */ - std::vector<uint256> m_blocks_for_inv_relay GUARDED_BY(m_block_inv_mutex); - /** Unfiltered list of blocks that we'd like to announce via a `headers` - * message. If we can't announce via a `headers` message, we'll fall back to - * announcing via `inv`. */ - std::vector<uint256> m_blocks_for_headers_relay GUARDED_BY(m_block_inv_mutex); - /** The final block hash that we sent in an `inv` message to this peer. - * When the peer requests this block, we send an `inv` message to trigger - * the peer to request the next sequence of block hashes. - * Most peers use headers-first syncing, which doesn't use this mechanism */ - uint256 m_continuation_block GUARDED_BY(m_block_inv_mutex) {}; - - /** This peer's reported block height when we connected */ - std::atomic<int> m_starting_height{-1}; - - /** Set of txids to reconsider once their parent transactions have been accepted **/ - std::set<uint256> m_orphan_work_set GUARDED_BY(g_cs_orphans); - - /** Protects m_getdata_requests **/ - Mutex m_getdata_requests_mutex; - /** Work queue of items requested by this peer **/ - std::deque<CInv> m_getdata_requests GUARDED_BY(m_getdata_requests_mutex); - - explicit Peer(NodeId id) : m_id(id) {} -}; - -using PeerRef = std::shared_ptr<Peer>; - -class PeerManager final : public CValidationInterface, public NetEventsInterface { +class PeerManager : public CValidationInterface, public NetEventsInterface +{ public: - PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman, - CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, - bool ignore_incoming_txs); + static std::unique_ptr<PeerManager> make(const CChainParams& chainparams, CConnman& connman, BanMan* banman, + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, + bool ignore_incoming_txs); + virtual ~PeerManager() { } - /** - * Overridden from CValidationInterface. - */ - void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override; - void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override; - /** - * Overridden from CValidationInterface. - */ - void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; - /** - * Overridden from CValidationInterface. - */ - void BlockChecked(const CBlock& block, const BlockValidationState& state) override; - /** - * Overridden from CValidationInterface. - */ - void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override; - - /** Initialize a peer by adding it to mapNodeState and pushing a message requesting its version */ - void InitializeNode(CNode* pnode) override; - /** Handle removal of a peer by updating various state and removing it from mapNodeState */ - void FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) override; - /** - * Process protocol messages received from a given node - * - * @param[in] pfrom The node which we have received messages from. - * @param[in] interrupt Interrupt condition for processing threads - */ - bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override; - /** - * Send queued protocol messages to be sent to a give node. - * - * @param[in] pto The node which we are sending messages to. - * @return True if there is more work to be done - */ - bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing); + /** Get statistics from node state */ + virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) = 0; - /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */ - void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - /** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */ - void CheckForStaleTipAndEvictPeers(); - /** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */ - void EvictExtraOutboundPeers(int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - /** Retrieve unbroadcast transactions from the mempool and reattempt sending to peers */ - void ReattemptInitialBroadcast(CScheduler& scheduler) const; + /** Whether this node ignores txs received over p2p. */ + virtual bool IgnoresIncomingTxs() = 0; - /** Process a single message from a peer. Public for fuzz testing */ - void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, - const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc); + /** Set the best height */ + virtual void SetBestHeight(int height) = 0; /** * Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node * to be discouraged, meaning the peer might be disconnected and added to the discouragement filter. * Public for unit testing. */ - void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message); - - /** Get statistics from node state */ - bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats); - - /** Set the best height */ - void SetBestHeight(int height) { m_best_height = height; }; - - /** Whether this node ignores txs received over p2p. */ - bool IgnoresIncomingTxs() { return m_ignore_incoming_txs; }; - -private: - /** Get a shared pointer to the Peer object. - * May return an empty shared_ptr if the Peer object can't be found. */ - PeerRef GetPeerRef(NodeId id) const; - - /** Get a shared pointer to the Peer object and remove it from m_peer_map. - * May return an empty shared_ptr if the Peer object can't be found. */ - PeerRef RemovePeer(NodeId id); - - /** - * Potentially mark a node discouraged based on the contents of a BlockValidationState object - * - * @param[in] via_compact_block this bool is passed in because net_processing should - * punish peers differently depending on whether the data was provided in a compact - * block message or not. If the compact block had a valid header, but contained invalid - * txs, the peer should not be punished. See BIP 152. - * - * @return Returns true if the peer was punished (probably disconnected) - */ - bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, - bool via_compact_block, const std::string& message = ""); + virtual void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) = 0; /** - * Potentially disconnect and discourage a node based on the contents of a TxValidationState object - * - * @return Returns true if the peer was punished (probably disconnected) - */ - bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = ""); - - /** Maybe disconnect a peer and discourage future connections from its address. - * - * @param[in] pnode The node to check. - * @return True if the peer was marked for disconnection in this function + * Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound. + * Public for unit testing. */ - bool MaybeDiscourageAndDisconnect(CNode& pnode); + virtual void CheckForStaleTipAndEvictPeers() = 0; - void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans); - /** Process a single headers message from a peer. */ - void ProcessHeadersMessage(CNode& pfrom, const Peer& peer, - const std::vector<CBlockHeader>& headers, - bool via_compact_block); - - void SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req); - - /** Register with TxRequestTracker that an INV has been received from a - * peer. The announcement parameters are decided in PeerManager and then - * passed to TxRequestTracker. */ - void AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time) - EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - - /** Send a version message to a peer */ - void PushNodeVersion(CNode& pnode, int64_t nTime); - - const CChainParams& m_chainparams; - CConnman& m_connman; - /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */ - BanMan* const m_banman; - ChainstateManager& m_chainman; - CTxMemPool& m_mempool; - TxRequestTracker m_txrequest GUARDED_BY(::cs_main); - - /** The height of the best chain */ - std::atomic<int> m_best_height{-1}; - - int64_t m_stale_tip_check_time; //!< Next time to check for stale tip - - /** Whether this node is running in blocks only mode */ - const bool m_ignore_incoming_txs; - - /** Whether we've completed initial sync yet, for determining when to turn - * on extra block-relay-only peers. */ - bool m_initial_sync_finished{false}; - - /** Protects m_peer_map. This mutex must not be locked while holding a lock - * on any of the mutexes inside a Peer object. */ - mutable Mutex m_peer_mutex; - /** - * Map of all Peer objects, keyed by peer id. This map is protected - * by the m_peer_mutex. Once a shared pointer reference is - * taken, the lock may be released. Individual fields are protected by - * their own locks. - */ - std::map<NodeId, PeerRef> m_peer_map GUARDED_BY(m_peer_mutex); + /** Process a single message from a peer. Public for fuzz testing */ + virtual void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, + const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) = 0; }; /** Relay transaction to every node */ diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 512f61f2bf..bf0ba38c2d 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -8,6 +8,7 @@ #include <script/interpreter.h> #include <span.h> +#include <util/hasher.h> #include <vector> @@ -20,27 +21,6 @@ static const int64_t MAX_MAX_SIG_CACHE_SIZE = 16384; class CPubKey; -/** - * We're hashing a nonce into the entries themselves, so we don't need extra - * blinding in the set hash computation. - * - * This may exhibit platform endian dependent behavior but because these are - * nonced hashes (random) and this state is only ever used locally it is safe. - * All that matters is local consistency. - */ -class SignatureCacheHasher -{ -public: - template <uint8_t hash_select> - uint32_t operator()(const uint256& key) const - { - static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); - uint32_t u; - std::memcpy(&u, key.begin()+4*hash_select, 4); - return u; - } -}; - class CachingTransactionSignatureChecker : public TransactionSignatureChecker { private: diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index d926f8d767..cf6009d591 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -80,8 +80,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) { const CChainParams& chainparams = Params(); auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = std::make_unique<PeerManager>(chainparams, *connman, nullptr, *m_node.scheduler, - *m_node.chainman, *m_node.mempool, false); + auto peerLogic = PeerManager::make(chainparams, *connman, nullptr, *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); // Mock an outbound peer CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -150,8 +150,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management) { const CChainParams& chainparams = Params(); auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337); - auto peerLogic = std::make_unique<PeerManager>(chainparams, *connman, nullptr, *m_node.scheduler, - *m_node.chainman, *m_node.mempool, false); + auto peerLogic = PeerManager::make(chainparams, *connman, nullptr, *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS; CConnman::Options options; @@ -224,8 +224,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement) const CChainParams& chainparams = Params(); auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = std::make_unique<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, - *m_node.chainman, *m_node.mempool, false); + auto peerLogic = PeerManager::make(chainparams, *connman, banman.get(), *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); banman->ClearBanned(); CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -271,8 +271,8 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) const CChainParams& chainparams = Params(); auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = std::make_unique<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, - *m_node.chainman, *m_node.mempool, false); + auto peerLogic = PeerManager::make(chainparams, *connman, banman.get(), *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); banman->ClearBanned(); int64_t nStartTime = GetTime(); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index e167fc98fd..738f414cd0 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -192,9 +192,9 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests. - m_node.peerman = std::make_unique<PeerManager>(chainparams, *m_node.connman, m_node.banman.get(), - *m_node.scheduler, *m_node.chainman, *m_node.mempool, - false); + m_node.peerman = PeerManager::make(chainparams, *m_node.connman, m_node.banman.get(), + *m_node.scheduler, *m_node.chainman, *m_node.mempool, + false); { CConnman::Options options; options.m_msgproc = m_node.peerman.get(); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d18182c07d..9ae7b921b3 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1127,5 +1127,3 @@ CTxMemPool::EpochGuard::~EpochGuard() ++pool.m_epoch; pool.m_has_epoch_guard = false; } - -SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {} diff --git a/src/txmempool.h b/src/txmempool.h index 15797cbc00..9a1aa9bc2b 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -15,13 +15,13 @@ #include <amount.h> #include <coins.h> -#include <crypto/siphash.h> #include <indirectmap.h> #include <optional.h> #include <policy/feerate.h> #include <primitives/transaction.h> #include <sync.h> #include <random.h> +#include <util/hasher.h> #include <boost/multi_index_container.hpp> #include <boost/multi_index/hashed_index.hpp> @@ -398,20 +398,6 @@ enum class MemPoolRemovalReason { REPLACED, //!< Removed for replacement }; -class SaltedTxidHasher -{ -private: - /** Salt */ - const uint64_t k0, k1; - -public: - SaltedTxidHasher(); - - size_t operator()(const uint256& txid) const { - return SipHashUint256(k0, k1, txid); - } -}; - /** * CTxMemPool stores valid-according-to-the-current-best-chain transactions * that may be included in the next block. diff --git a/src/util/hasher.cpp b/src/util/hasher.cpp new file mode 100644 index 0000000000..5900daf050 --- /dev/null +++ b/src/util/hasher.cpp @@ -0,0 +1,19 @@ +// Copyright (c) 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 <random.h> +#include <util/hasher.h> + +#include <limits> + +SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {} + +SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {} + +SaltedSipHasher::SaltedSipHasher() : m_k0(GetRand(std::numeric_limits<uint64_t>::max())), m_k1(GetRand(std::numeric_limits<uint64_t>::max())) {} + +size_t SaltedSipHasher::operator()(const Span<const unsigned char>& script) const +{ + return CSipHasher(m_k0, m_k1).Write(script.data(), script.size()).Finalize(); +} diff --git a/src/util/hasher.h b/src/util/hasher.h new file mode 100644 index 0000000000..fa2fea30d8 --- /dev/null +++ b/src/util/hasher.h @@ -0,0 +1,99 @@ +// Copyright (c) 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_UTIL_HASHER_H +#define BITCOIN_UTIL_HASHER_H + +#include <crypto/siphash.h> +#include <primitives/transaction.h> +#include <uint256.h> + +class SaltedTxidHasher +{ +private: + /** Salt */ + const uint64_t k0, k1; + +public: + SaltedTxidHasher(); + + size_t operator()(const uint256& txid) const { + return SipHashUint256(k0, k1, txid); + } +}; + +class SaltedOutpointHasher +{ +private: + /** Salt */ + const uint64_t k0, k1; + +public: + SaltedOutpointHasher(); + + /** + * This *must* return size_t. With Boost 1.46 on 32-bit systems the + * unordered_map will behave unpredictably if the custom hasher returns a + * uint64_t, resulting in failures when syncing the chain (#4634). + * + * Having the hash noexcept allows libstdc++'s unordered_map to recalculate + * the hash during rehash, so it does not have to cache the value. This + * reduces node's memory by sizeof(size_t). The required recalculation has + * a slight performance penalty (around 1.6%), but this is compensated by + * memory savings of about 9% which allow for a larger dbcache setting. + * + * @see https://gcc.gnu.org/onlinedocs/gcc-9.2.0/libstdc++/manual/manual/unordered_associative.html + */ + size_t operator()(const COutPoint& id) const noexcept { + return SipHashUint256Extra(k0, k1, id.hash, id.n); + } +}; + +struct FilterHeaderHasher +{ + size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } +}; + +/** + * We're hashing a nonce into the entries themselves, so we don't need extra + * blinding in the set hash computation. + * + * This may exhibit platform endian dependent behavior but because these are + * nonced hashes (random) and this state is only ever used locally it is safe. + * All that matters is local consistency. + */ +class SignatureCacheHasher +{ +public: + template <uint8_t hash_select> + uint32_t operator()(const uint256& key) const + { + static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); + uint32_t u; + std::memcpy(&u, key.begin()+4*hash_select, 4); + return u; + } +}; + +struct BlockHasher +{ + // this used to call `GetCheapHash()` in uint256, which was later moved; the + // cheap hash function simply calls ReadLE64() however, so the end result is + // identical + size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } +}; + +class SaltedSipHasher +{ +private: + /** Salt */ + const uint64_t m_k0, m_k1; + +public: + SaltedSipHasher(); + + size_t operator()(const Span<const unsigned char>& script) const; +}; + +#endif // BITCOIN_UTIL_HASHER_H diff --git a/src/validation.h b/src/validation.h index d10b260d8a..1b4e40cd53 100644 --- a/src/validation.h +++ b/src/validation.h @@ -23,6 +23,7 @@ #include <txdb.h> #include <versionbits.h> #include <serialize.h> +#include <util/hasher.h> #include <atomic> #include <map> @@ -93,14 +94,6 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3; // Setting the target to >= 550 MiB will make it likely we can respect the target. static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024; -struct BlockHasher -{ - // this used to call `GetCheapHash()` in uint256, which was later moved; the - // cheap hash function simply calls ReadLE64() however, so the end result is - // identical - size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } -}; - /** Current sync state passed to tip changed callbacks. */ enum class SynchronizationState { INIT_REINDEX, diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index cec46a0fbb..8f6b69bc78 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -304,7 +304,7 @@ private: /* the HD chain data model (external chain counters) */ CHDChain m_hd_chain; - std::unordered_map<CKeyID, CHDChain, KeyIDHasher> m_inactive_hd_chains; + std::unordered_map<CKeyID, CHDChain, SaltedSipHasher> m_inactive_hd_chains; /* HD derive new child key (on internal or external chain) */ void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan index 29221a94f2..3a04418e8b 100644 --- a/test/sanitizer_suppressions/tsan +++ b/test/sanitizer_suppressions/tsan @@ -13,7 +13,7 @@ mutex:CConnman::ThreadOpenConnections mutex:CConnman::ThreadOpenAddedConnections mutex:CConnman::SocketHandler mutex:UpdateTip -mutex:PeerManager::UpdatedBlockTip +mutex:PeerManagerImpl::UpdatedBlockTip mutex:g_best_block_mutex # race (TODO fix) |