From 57f661c37b9aa50a6642b00116fb363c16058b10 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 21 Jan 2026 01:11:13 +0700 Subject: [PATCH 01/16] refactor: code-move only CChainLockHandler from chainlock.h to handler.h --- src/Makefile.am | 2 + src/chainlock/chainlock.cpp | 483 +---------------------------------- src/chainlock/chainlock.h | 130 +--------- src/chainlock/handler.cpp | 486 ++++++++++++++++++++++++++++++++++++ src/chainlock/handler.h | 138 ++++++++++ 5 files changed, 628 insertions(+), 611 deletions(-) create mode 100644 src/chainlock/handler.cpp create mode 100644 src/chainlock/handler.h diff --git a/src/Makefile.am b/src/Makefile.am index 47f3c6bd55fc..711ee2ad98e9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -174,6 +174,7 @@ BITCOIN_CORE_H = \ blockfilter.h \ chain.h \ chainlock/chainlock.h \ + chainlock/handler.h \ chainlock/clsig.h \ chainlock/signing.h \ chainparams.h \ @@ -499,6 +500,7 @@ libbitcoin_node_a_SOURCES = \ chain.cpp \ chainlock/chainlock.cpp \ chainlock/clsig.cpp \ + chainlock/handler.cpp \ chainlock/signing.cpp \ coinjoin/coinjoin.cpp \ coinjoin/server.cpp \ diff --git a/src/chainlock/chainlock.cpp b/src/chainlock/chainlock.cpp index bbfb3c9f83af..e6f3343e11ac 100644 --- a/src/chainlock/chainlock.cpp +++ b/src/chainlock/chainlock.cpp @@ -1,486 +1,5 @@ -// Copyright (c) 2019-2025 The Dash Core developers +// Copyright (c) 2019-2026 The Dash Core developers // Distributed under the MIT/X11 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 - -// Forward declaration to break dependency over node/transaction.h -namespace node { -CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, - const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock); -} // namespace node - -using node::GetTransaction; - -namespace llmq { -namespace { -static constexpr auto CLEANUP_SEEN_TIMEOUT{24h}; -//! How long to wait for islocks until we consider a block with non-islocked TXs to be safe to sign -static constexpr auto WAIT_FOR_ISLOCK_TIMEOUT{10min}; -} // anonymous namespace - -bool AreChainLocksEnabled(const CSporkManager& sporkman) -{ - return sporkman.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED); -} - -CChainLocksHandler::CChainLocksHandler(CChainState& chainstate, CQuorumManager& _qman, CSporkManager& sporkman, - CTxMemPool& _mempool, const CMasternodeSync& mn_sync) : - m_chainstate{chainstate}, - qman{_qman}, - spork_manager{sporkman}, - mempool{_mempool}, - m_mn_sync{mn_sync}, - scheduler{std::make_unique()}, - scheduler_thread{ - std::make_unique(std::thread(util::TraceThread, "cl-schdlr", [&] { scheduler->serviceQueue(); }))} -{ -} - -CChainLocksHandler::~CChainLocksHandler() -{ - scheduler->stop(); - scheduler_thread->join(); -} - -void CChainLocksHandler::Start(const llmq::CInstantSendManager& isman) -{ - scheduler->scheduleEvery( - [&]() { - auto signer = m_signer.load(std::memory_order_acquire); - CheckActiveState(); - EnforceBestChainLock(); - Cleanup(); - // regularly retry signing the current chaintip as it might have failed before due to missing islocks - if (signer) { - signer->TrySignChainTip(isman); - } - }, - std::chrono::seconds{5}); -} - -void CChainLocksHandler::Stop() -{ - scheduler->stop(); -} - -bool CChainLocksHandler::AlreadyHave(const CInv& inv) const -{ - LOCK(cs); - return seenChainLocks.count(inv.hash) != 0; -} - -bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const -{ - LOCK(cs); - - if (hash != bestChainLockHash) { - // we only propagate the best one and ditch all the old ones - return false; - } - - ret = bestChainLock; - return true; -} - -chainlock::ChainLockSig CChainLocksHandler::GetBestChainLock() const -{ - LOCK(cs); - return bestChainLock; -} - -void CChainLocksHandler::UpdateTxFirstSeenMap(const Uint256HashSet& tx, const int64_t& time) -{ - AssertLockNotHeld(cs); - LOCK(cs); - for (const auto& txid : tx) { - txFirstSeenTime.emplace(txid, time); - } -} - -MessageProcessingResult CChainLocksHandler::ProcessNewChainLock(const NodeId from, const chainlock::ChainLockSig& clsig, - const uint256& hash) -{ - CheckActiveState(); - - { - LOCK(cs); - if (!seenChainLocks.emplace(hash, GetTime()).second) { - return {}; - } - - if (!bestChainLock.IsNull() && clsig.getHeight() <= bestChainLock.getHeight()) { - // no need to process older/same CLSIGs - return {}; - } - } - - if (const auto ret = VerifyChainLock(clsig); ret != VerifyRecSigStatus::Valid) { - LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- invalid CLSIG (%s), status=%d peer=%d\n", __func__, - clsig.ToString(), ToUnderlying(ret), from); - if (from != -1) { - return MisbehavingError{10}; - } - return {}; - } - - const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(clsig.getBlockHash())); - const CInv clsig_inv(MSG_CLSIG, hash); - - { - LOCK(cs); - // newer chainlock could be processed via another thread while we were not holding the lock, re-verify - if (!bestChainLock.IsNull() && clsig.getHeight() <= bestChainLock.getHeight()) { - // no need to process older/same CLSIGs - return {}; - } - bestChainLockHash = hash; - bestChainLock = clsig; - - if (pindex) { - if (pindex->nHeight != clsig.getHeight()) { - // Should not happen, same as the conflict check from above. - LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n", - __func__, clsig.ToString(), pindex->nHeight); - // Note: not relaying clsig here - return {}; - } - bestChainLockWithKnownBlock = bestChainLock; - bestChainLockBlockIndex = pindex; - } else { - // We don't know the block/header for this CLSIG yet, so bail out for now and when the - // block/header later comes in, we will enforce the correct chain. We still relay further. - return clsig_inv; - } - } - - scheduler->scheduleFromNow( - [&]() { - CheckActiveState(); - EnforceBestChainLock(); - }, - std::chrono::seconds{0}); - - LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n", __func__, - clsig.ToString(), from); - - return clsig_inv; -} - -void CChainLocksHandler::AcceptedBlockHeader(gsl::not_null pindexNew) -{ - LOCK(cs); - - if (pindexNew->GetBlockHash() == bestChainLock.getBlockHash()) { - LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", - __func__, pindexNew->GetBlockHash().ToString()); - - if (bestChainLock.getHeight() != pindexNew->nHeight) { - // Should not happen, same as the conflict check from ProcessNewChainLock. - LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n", - __func__, bestChainLock.ToString(), pindexNew->nHeight); - return; - } - - // when EnforceBestChainLock is called later, it might end up invalidating other chains but not activating the - // CLSIG locked chain. This happens when only the header is known but the block is still missing yet. The usual - // block processing logic will handle this when the block arrives - bestChainLockWithKnownBlock = bestChainLock; - bestChainLockBlockIndex = pindexNew; - } -} - -void CChainLocksHandler::UpdatedBlockTip(const llmq::CInstantSendManager& isman) -{ - // don't call TrySignChainTip directly but instead let the scheduler call it. This way we ensure that cs_main is - // never locked and TrySignChainTip is not called twice in parallel. Also avoids recursive calls due to - // EnforceBestChainLock switching chains. - // atomic[If tryLockChainTipScheduled is false, do (set it to true] and schedule signing). - if (bool expected = false; tryLockChainTipScheduled.compare_exchange_strong(expected, true)) { - scheduler->scheduleFromNow( - [&]() { - auto signer = m_signer.load(std::memory_order_acquire); - CheckActiveState(); - EnforceBestChainLock(); - Cleanup(); - if (signer) { - signer->TrySignChainTip(isman); - } - tryLockChainTipScheduled = false; - }, - std::chrono::seconds{0}); - } -} - -void CChainLocksHandler::CheckActiveState() -{ - bool oldIsEnabled = isEnabled; - isEnabled = AreChainLocksEnabled(spork_manager); - - if (!oldIsEnabled && isEnabled) { - // ChainLocks got activated just recently, but it's possible that it was already running before, leaving - // us with some stale values which we should not try to enforce anymore (there probably was a good reason - // to disable spork19) - LOCK(cs); - bestChainLockHash = uint256(); - bestChainLock = bestChainLockWithKnownBlock = chainlock::ChainLockSig(); - bestChainLockBlockIndex = lastNotifyChainLockBlockIndex = nullptr; - } -} - -void CChainLocksHandler::TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) -{ - if (tx->IsCoinBase() || tx->vin.empty()) { - return; - } - - LOCK(cs); - txFirstSeenTime.emplace(tx->GetHash(), nAcceptTime); -} - -void CChainLocksHandler::BlockConnected(const std::shared_ptr& pblock, gsl::not_null pindex) -{ - if (!m_mn_sync.IsBlockchainSynced()) { - return; - } - - // We listen for BlockConnected so that we can collect all TX ids of all included TXs of newly received blocks - int64_t curTime = GetTime().count(); - { - LOCK(cs); - for (const auto& tx : pblock->vtx) { - if (!tx->IsCoinBase() && !tx->vin.empty()) { - txFirstSeenTime.emplace(tx->GetHash(), curTime); - } - } - } - - // We need this information later when we try to sign a new tip, so that we can determine if all included TXs are safe. - if (auto signer = m_signer.load(std::memory_order_acquire); signer) { - signer->UpdateBlockHashTxidMap(pindex->GetBlockHash(), pblock->vtx); - } -} - -void CChainLocksHandler::BlockDisconnected(const std::shared_ptr& pblock, - gsl::not_null pindexDisconnected) -{ - if (auto signer = m_signer.load(std::memory_order_acquire); signer) { - signer->EraseFromBlockHashTxidMap(pindexDisconnected->GetBlockHash()); - } -} - -int32_t CChainLocksHandler::GetBestChainLockHeight() const -{ - AssertLockNotHeld(cs); - LOCK(cs); - return bestChainLock.getHeight(); -} - -bool CChainLocksHandler::IsTxSafeForMining(const uint256& txid) const -{ - auto tx_age{0s}; - { - LOCK(cs); - auto it = txFirstSeenTime.find(txid); - if (it != txFirstSeenTime.end()) { - tx_age = GetTime() - it->second; - } - } - - return tx_age >= WAIT_FOR_ISLOCK_TIMEOUT; -} - -// WARNING: cs_main and cs should not be held! -// This should also not be called from validation signals, as this might result in recursive calls -void CChainLocksHandler::EnforceBestChainLock() -{ - AssertLockNotHeld(cs); - AssertLockNotHeld(cs_main); - - std::shared_ptr clsig; - const CBlockIndex* pindex; - const CBlockIndex* currentBestChainLockBlockIndex; - { - LOCK(cs); - - if (!IsEnabled()) { - return; - } - - clsig = std::make_shared(bestChainLockWithKnownBlock); - pindex = currentBestChainLockBlockIndex = this->bestChainLockBlockIndex; - - if (currentBestChainLockBlockIndex == nullptr) { - // we don't have the header/block, so we can't do anything right now - return; - } - } - - BlockValidationState dummy_state; - - // Go backwards through the chain referenced by clsig until we find a block that is part of the main chain. - // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig - // and mark all of them as conflicting. - LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s)\n", __func__, - pindex->GetBlockHash().ToString(), clsig->ToString()); - m_chainstate.EnforceBlock(dummy_state, pindex); - - - if (/*activateNeeded =*/WITH_LOCK(::cs_main, return m_chainstate.m_chain.Tip()->GetAncestor( - currentBestChainLockBlockIndex->nHeight)) != - currentBestChainLockBlockIndex) { - if (!m_chainstate.ActivateBestChain(dummy_state)) { - LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, dummy_state.ToString()); - return; - } - LOCK(::cs_main); - if (m_chainstate.m_chain.Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight) != - currentBestChainLockBlockIndex) { - return; - } - } - - { - LOCK(cs); - if (lastNotifyChainLockBlockIndex == currentBestChainLockBlockIndex) return; - lastNotifyChainLockBlockIndex = currentBestChainLockBlockIndex; - } - - GetMainSignals().NotifyChainLock(currentBestChainLockBlockIndex, clsig, clsig->ToString()); - uiInterface.NotifyChainLock(clsig->getBlockHash().ToString(), clsig->getHeight()); - ::g_stats_client->gauge("chainlocks.blockHeight", clsig->getHeight(), 1.0f); -} - -VerifyRecSigStatus CChainLocksHandler::VerifyChainLock(const chainlock::ChainLockSig& clsig) const -{ - const auto llmqType = Params().GetConsensus().llmqTypeChainLocks; - const uint256 nRequestId = chainlock::GenSigRequestId(clsig.getHeight()); - - return llmq::VerifyRecoveredSig(llmqType, m_chainstate.m_chain, qman, clsig.getHeight(), nRequestId, - clsig.getBlockHash(), clsig.getSig()); -} - -bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash) const -{ - AssertLockNotHeld(cs); - LOCK(cs); - - if (!IsEnabled()) { - return false; - } - - if (bestChainLockBlockIndex == nullptr) { - return false; - } - - if (nHeight > bestChainLockBlockIndex->nHeight) { - return false; - } - - if (nHeight == bestChainLockBlockIndex->nHeight) { - return blockHash == bestChainLockBlockIndex->GetBlockHash(); - } - - const auto* pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight); - return (pAncestor != nullptr) && pAncestor->GetBlockHash() == blockHash; -} - -bool CChainLocksHandler::HasConflictingChainLock(int nHeight, const uint256& blockHash) const -{ - AssertLockNotHeld(cs); - LOCK(cs); - - if (!IsEnabled()) { - return false; - } - - if (bestChainLockBlockIndex == nullptr) { - return false; - } - - if (nHeight > bestChainLockBlockIndex->nHeight) { - return false; - } - - if (nHeight == bestChainLockBlockIndex->nHeight) { - return blockHash != bestChainLockBlockIndex->GetBlockHash(); - } - - const auto* pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight); - assert(pAncestor); - return pAncestor->GetBlockHash() != blockHash; -} - -void CChainLocksHandler::Cleanup() -{ - constexpr auto CLEANUP_INTERVAL{30s}; - if (!m_mn_sync.IsBlockchainSynced()) { - return; - } - - if (!cleanupThrottler.TryCleanup(CLEANUP_INTERVAL)) { - return; - } - - { - LOCK(cs); - for (auto it = seenChainLocks.begin(); it != seenChainLocks.end();) { - if (GetTime() - it->second >= CLEANUP_SEEN_TIMEOUT) { - it = seenChainLocks.erase(it); - } else { - ++it; - } - } - } - - if (auto signer = m_signer.load(std::memory_order_acquire); signer) { - const auto cleanup_txes{signer->Cleanup()}; - LOCK(cs); - for (const auto& tx : cleanup_txes) { - for (const auto& txid : *tx) { - txFirstSeenTime.erase(txid); - } - } - } - - LOCK(::cs_main); - LOCK2(mempool.cs, cs); // need mempool.cs due to GetTransaction calls - for (auto it = txFirstSeenTime.begin(); it != txFirstSeenTime.end();) { - uint256 hashBlock; - if (auto tx = GetTransaction(nullptr, &mempool, it->first, Params().GetConsensus(), hashBlock); !tx) { - // tx has vanished, probably due to conflicts - it = txFirstSeenTime.erase(it); - } else if (!hashBlock.IsNull()) { - const auto* pindex = m_chainstate.m_blockman.LookupBlockIndex(hashBlock); - assert(pindex); // GetTransaction gave us that hashBlock, it should resolve to a valid block index - if (m_chainstate.m_chain.Tip()->GetAncestor(pindex->nHeight) == pindex && - m_chainstate.m_chain.Height() - pindex->nHeight > chainlock::TX_CONFIRM_THRESHOLD) { - // tx is sufficiently deep, we can stop tracking it - it = txFirstSeenTime.erase(it); - } else { - ++it; - } - } else { - ++it; - } - } -} -} // namespace llmq diff --git a/src/chainlock/chainlock.h b/src/chainlock/chainlock.h index 0a4ae9822089..a7594101b652 100644 --- a/src/chainlock/chainlock.h +++ b/src/chainlock/chainlock.h @@ -5,134 +5,6 @@ #ifndef BITCOIN_CHAINLOCK_CHAINLOCK_H #define BITCOIN_CHAINLOCK_CHAINLOCK_H -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -class CBlock; -class CBlockIndex; -class CChainState; -class CMasternodeSync; -class CScheduler; -class CSporkManager; -class CTxMemPool; - -namespace llmq { -class CInstantSendManager; -class CQuorumManager; -class CSigningManager; -enum class VerifyRecSigStatus : uint8_t; - -class CChainLocksHandler final : public chainlock::ChainLockSignerParent -{ -private: - CChainState& m_chainstate; - CQuorumManager& qman; - CSporkManager& spork_manager; - CTxMemPool& mempool; - const CMasternodeSync& m_mn_sync; - std::unique_ptr scheduler; - std::unique_ptr scheduler_thread; - - std::atomic m_signer{nullptr}; - - mutable Mutex cs; - std::atomic tryLockChainTipScheduled{false}; - std::atomic isEnabled{false}; - - uint256 bestChainLockHash GUARDED_BY(cs); - chainlock::ChainLockSig bestChainLock GUARDED_BY(cs); - - chainlock::ChainLockSig bestChainLockWithKnownBlock GUARDED_BY(cs); - const CBlockIndex* bestChainLockBlockIndex GUARDED_BY(cs){nullptr}; - const CBlockIndex* lastNotifyChainLockBlockIndex GUARDED_BY(cs){nullptr}; - - Uint256HashMap txFirstSeenTime GUARDED_BY(cs); - - std::map seenChainLocks GUARDED_BY(cs); - - CleanupThrottler cleanupThrottler; - -public: - CChainLocksHandler() = delete; - CChainLocksHandler(const CChainLocksHandler&) = delete; - CChainLocksHandler& operator=(const CChainLocksHandler&) = delete; - explicit CChainLocksHandler(CChainState& chainstate, CQuorumManager& _qman, CSporkManager& sporkman, - CTxMemPool& _mempool, const CMasternodeSync& mn_sync); - ~CChainLocksHandler(); - - void ConnectSigner(gsl::not_null signer) - { - // Prohibit double initialization - assert(m_signer.load(std::memory_order_acquire) == nullptr); - m_signer.store(signer, std::memory_order_release); - } - void DisconnectSigner() { m_signer.store(nullptr, std::memory_order_release); } - - void Start(const llmq::CInstantSendManager& isman); - void Stop(); - - bool AlreadyHave(const CInv& inv) const - EXCLUSIVE_LOCKS_REQUIRED(!cs); - bool GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const - EXCLUSIVE_LOCKS_REQUIRED(!cs); - chainlock::ChainLockSig GetBestChainLock() const - EXCLUSIVE_LOCKS_REQUIRED(!cs); - void UpdateTxFirstSeenMap(const Uint256HashSet& tx, const int64_t& time) override EXCLUSIVE_LOCKS_REQUIRED(!cs); - - [[nodiscard]] MessageProcessingResult ProcessNewChainLock(NodeId from, const chainlock::ChainLockSig& clsig, - const uint256& hash) override - EXCLUSIVE_LOCKS_REQUIRED(!cs); - - void AcceptedBlockHeader(gsl::not_null pindexNew) - EXCLUSIVE_LOCKS_REQUIRED(!cs); - void UpdatedBlockTip(const llmq::CInstantSendManager& isman); - void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) - EXCLUSIVE_LOCKS_REQUIRED(!cs); - void BlockConnected(const std::shared_ptr& pblock, gsl::not_null pindex) - EXCLUSIVE_LOCKS_REQUIRED(!cs); - void BlockDisconnected(const std::shared_ptr& pblock, gsl::not_null pindexDisconnected) - EXCLUSIVE_LOCKS_REQUIRED(!cs); - void CheckActiveState() - EXCLUSIVE_LOCKS_REQUIRED(!cs); - void EnforceBestChainLock() - EXCLUSIVE_LOCKS_REQUIRED(!cs); - - bool HasChainLock(int nHeight, const uint256& blockHash) const override - EXCLUSIVE_LOCKS_REQUIRED(!cs); - bool HasConflictingChainLock(int nHeight, const uint256& blockHash) const override - EXCLUSIVE_LOCKS_REQUIRED(!cs); - VerifyRecSigStatus VerifyChainLock(const chainlock::ChainLockSig& clsig) const; - - [[nodiscard]] int32_t GetBestChainLockHeight() const override - EXCLUSIVE_LOCKS_REQUIRED(!cs); - bool IsTxSafeForMining(const uint256& txid) const override - EXCLUSIVE_LOCKS_REQUIRED(!cs); - [[nodiscard]] bool IsEnabled() const override { return isEnabled; } - -private: - void Cleanup() - EXCLUSIVE_LOCKS_REQUIRED(!cs); -}; - -bool AreChainLocksEnabled(const CSporkManager& sporkman); -} // namespace llmq +#include #endif // BITCOIN_CHAINLOCK_CHAINLOCK_H diff --git a/src/chainlock/handler.cpp b/src/chainlock/handler.cpp new file mode 100644 index 000000000000..bbfb3c9f83af --- /dev/null +++ b/src/chainlock/handler.cpp @@ -0,0 +1,486 @@ +// Copyright (c) 2019-2025 The Dash Core developers +// Distributed under the MIT/X11 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 + +// Forward declaration to break dependency over node/transaction.h +namespace node { +CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, + const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock); +} // namespace node + +using node::GetTransaction; + +namespace llmq { +namespace { +static constexpr auto CLEANUP_SEEN_TIMEOUT{24h}; +//! How long to wait for islocks until we consider a block with non-islocked TXs to be safe to sign +static constexpr auto WAIT_FOR_ISLOCK_TIMEOUT{10min}; +} // anonymous namespace + +bool AreChainLocksEnabled(const CSporkManager& sporkman) +{ + return sporkman.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED); +} + +CChainLocksHandler::CChainLocksHandler(CChainState& chainstate, CQuorumManager& _qman, CSporkManager& sporkman, + CTxMemPool& _mempool, const CMasternodeSync& mn_sync) : + m_chainstate{chainstate}, + qman{_qman}, + spork_manager{sporkman}, + mempool{_mempool}, + m_mn_sync{mn_sync}, + scheduler{std::make_unique()}, + scheduler_thread{ + std::make_unique(std::thread(util::TraceThread, "cl-schdlr", [&] { scheduler->serviceQueue(); }))} +{ +} + +CChainLocksHandler::~CChainLocksHandler() +{ + scheduler->stop(); + scheduler_thread->join(); +} + +void CChainLocksHandler::Start(const llmq::CInstantSendManager& isman) +{ + scheduler->scheduleEvery( + [&]() { + auto signer = m_signer.load(std::memory_order_acquire); + CheckActiveState(); + EnforceBestChainLock(); + Cleanup(); + // regularly retry signing the current chaintip as it might have failed before due to missing islocks + if (signer) { + signer->TrySignChainTip(isman); + } + }, + std::chrono::seconds{5}); +} + +void CChainLocksHandler::Stop() +{ + scheduler->stop(); +} + +bool CChainLocksHandler::AlreadyHave(const CInv& inv) const +{ + LOCK(cs); + return seenChainLocks.count(inv.hash) != 0; +} + +bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const +{ + LOCK(cs); + + if (hash != bestChainLockHash) { + // we only propagate the best one and ditch all the old ones + return false; + } + + ret = bestChainLock; + return true; +} + +chainlock::ChainLockSig CChainLocksHandler::GetBestChainLock() const +{ + LOCK(cs); + return bestChainLock; +} + +void CChainLocksHandler::UpdateTxFirstSeenMap(const Uint256HashSet& tx, const int64_t& time) +{ + AssertLockNotHeld(cs); + LOCK(cs); + for (const auto& txid : tx) { + txFirstSeenTime.emplace(txid, time); + } +} + +MessageProcessingResult CChainLocksHandler::ProcessNewChainLock(const NodeId from, const chainlock::ChainLockSig& clsig, + const uint256& hash) +{ + CheckActiveState(); + + { + LOCK(cs); + if (!seenChainLocks.emplace(hash, GetTime()).second) { + return {}; + } + + if (!bestChainLock.IsNull() && clsig.getHeight() <= bestChainLock.getHeight()) { + // no need to process older/same CLSIGs + return {}; + } + } + + if (const auto ret = VerifyChainLock(clsig); ret != VerifyRecSigStatus::Valid) { + LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- invalid CLSIG (%s), status=%d peer=%d\n", __func__, + clsig.ToString(), ToUnderlying(ret), from); + if (from != -1) { + return MisbehavingError{10}; + } + return {}; + } + + const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(clsig.getBlockHash())); + const CInv clsig_inv(MSG_CLSIG, hash); + + { + LOCK(cs); + // newer chainlock could be processed via another thread while we were not holding the lock, re-verify + if (!bestChainLock.IsNull() && clsig.getHeight() <= bestChainLock.getHeight()) { + // no need to process older/same CLSIGs + return {}; + } + bestChainLockHash = hash; + bestChainLock = clsig; + + if (pindex) { + if (pindex->nHeight != clsig.getHeight()) { + // Should not happen, same as the conflict check from above. + LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n", + __func__, clsig.ToString(), pindex->nHeight); + // Note: not relaying clsig here + return {}; + } + bestChainLockWithKnownBlock = bestChainLock; + bestChainLockBlockIndex = pindex; + } else { + // We don't know the block/header for this CLSIG yet, so bail out for now and when the + // block/header later comes in, we will enforce the correct chain. We still relay further. + return clsig_inv; + } + } + + scheduler->scheduleFromNow( + [&]() { + CheckActiveState(); + EnforceBestChainLock(); + }, + std::chrono::seconds{0}); + + LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n", __func__, + clsig.ToString(), from); + + return clsig_inv; +} + +void CChainLocksHandler::AcceptedBlockHeader(gsl::not_null pindexNew) +{ + LOCK(cs); + + if (pindexNew->GetBlockHash() == bestChainLock.getBlockHash()) { + LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", + __func__, pindexNew->GetBlockHash().ToString()); + + if (bestChainLock.getHeight() != pindexNew->nHeight) { + // Should not happen, same as the conflict check from ProcessNewChainLock. + LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n", + __func__, bestChainLock.ToString(), pindexNew->nHeight); + return; + } + + // when EnforceBestChainLock is called later, it might end up invalidating other chains but not activating the + // CLSIG locked chain. This happens when only the header is known but the block is still missing yet. The usual + // block processing logic will handle this when the block arrives + bestChainLockWithKnownBlock = bestChainLock; + bestChainLockBlockIndex = pindexNew; + } +} + +void CChainLocksHandler::UpdatedBlockTip(const llmq::CInstantSendManager& isman) +{ + // don't call TrySignChainTip directly but instead let the scheduler call it. This way we ensure that cs_main is + // never locked and TrySignChainTip is not called twice in parallel. Also avoids recursive calls due to + // EnforceBestChainLock switching chains. + // atomic[If tryLockChainTipScheduled is false, do (set it to true] and schedule signing). + if (bool expected = false; tryLockChainTipScheduled.compare_exchange_strong(expected, true)) { + scheduler->scheduleFromNow( + [&]() { + auto signer = m_signer.load(std::memory_order_acquire); + CheckActiveState(); + EnforceBestChainLock(); + Cleanup(); + if (signer) { + signer->TrySignChainTip(isman); + } + tryLockChainTipScheduled = false; + }, + std::chrono::seconds{0}); + } +} + +void CChainLocksHandler::CheckActiveState() +{ + bool oldIsEnabled = isEnabled; + isEnabled = AreChainLocksEnabled(spork_manager); + + if (!oldIsEnabled && isEnabled) { + // ChainLocks got activated just recently, but it's possible that it was already running before, leaving + // us with some stale values which we should not try to enforce anymore (there probably was a good reason + // to disable spork19) + LOCK(cs); + bestChainLockHash = uint256(); + bestChainLock = bestChainLockWithKnownBlock = chainlock::ChainLockSig(); + bestChainLockBlockIndex = lastNotifyChainLockBlockIndex = nullptr; + } +} + +void CChainLocksHandler::TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) +{ + if (tx->IsCoinBase() || tx->vin.empty()) { + return; + } + + LOCK(cs); + txFirstSeenTime.emplace(tx->GetHash(), nAcceptTime); +} + +void CChainLocksHandler::BlockConnected(const std::shared_ptr& pblock, gsl::not_null pindex) +{ + if (!m_mn_sync.IsBlockchainSynced()) { + return; + } + + // We listen for BlockConnected so that we can collect all TX ids of all included TXs of newly received blocks + int64_t curTime = GetTime().count(); + { + LOCK(cs); + for (const auto& tx : pblock->vtx) { + if (!tx->IsCoinBase() && !tx->vin.empty()) { + txFirstSeenTime.emplace(tx->GetHash(), curTime); + } + } + } + + // We need this information later when we try to sign a new tip, so that we can determine if all included TXs are safe. + if (auto signer = m_signer.load(std::memory_order_acquire); signer) { + signer->UpdateBlockHashTxidMap(pindex->GetBlockHash(), pblock->vtx); + } +} + +void CChainLocksHandler::BlockDisconnected(const std::shared_ptr& pblock, + gsl::not_null pindexDisconnected) +{ + if (auto signer = m_signer.load(std::memory_order_acquire); signer) { + signer->EraseFromBlockHashTxidMap(pindexDisconnected->GetBlockHash()); + } +} + +int32_t CChainLocksHandler::GetBestChainLockHeight() const +{ + AssertLockNotHeld(cs); + LOCK(cs); + return bestChainLock.getHeight(); +} + +bool CChainLocksHandler::IsTxSafeForMining(const uint256& txid) const +{ + auto tx_age{0s}; + { + LOCK(cs); + auto it = txFirstSeenTime.find(txid); + if (it != txFirstSeenTime.end()) { + tx_age = GetTime() - it->second; + } + } + + return tx_age >= WAIT_FOR_ISLOCK_TIMEOUT; +} + +// WARNING: cs_main and cs should not be held! +// This should also not be called from validation signals, as this might result in recursive calls +void CChainLocksHandler::EnforceBestChainLock() +{ + AssertLockNotHeld(cs); + AssertLockNotHeld(cs_main); + + std::shared_ptr clsig; + const CBlockIndex* pindex; + const CBlockIndex* currentBestChainLockBlockIndex; + { + LOCK(cs); + + if (!IsEnabled()) { + return; + } + + clsig = std::make_shared(bestChainLockWithKnownBlock); + pindex = currentBestChainLockBlockIndex = this->bestChainLockBlockIndex; + + if (currentBestChainLockBlockIndex == nullptr) { + // we don't have the header/block, so we can't do anything right now + return; + } + } + + BlockValidationState dummy_state; + + // Go backwards through the chain referenced by clsig until we find a block that is part of the main chain. + // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig + // and mark all of them as conflicting. + LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s)\n", __func__, + pindex->GetBlockHash().ToString(), clsig->ToString()); + m_chainstate.EnforceBlock(dummy_state, pindex); + + + if (/*activateNeeded =*/WITH_LOCK(::cs_main, return m_chainstate.m_chain.Tip()->GetAncestor( + currentBestChainLockBlockIndex->nHeight)) != + currentBestChainLockBlockIndex) { + if (!m_chainstate.ActivateBestChain(dummy_state)) { + LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, dummy_state.ToString()); + return; + } + LOCK(::cs_main); + if (m_chainstate.m_chain.Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight) != + currentBestChainLockBlockIndex) { + return; + } + } + + { + LOCK(cs); + if (lastNotifyChainLockBlockIndex == currentBestChainLockBlockIndex) return; + lastNotifyChainLockBlockIndex = currentBestChainLockBlockIndex; + } + + GetMainSignals().NotifyChainLock(currentBestChainLockBlockIndex, clsig, clsig->ToString()); + uiInterface.NotifyChainLock(clsig->getBlockHash().ToString(), clsig->getHeight()); + ::g_stats_client->gauge("chainlocks.blockHeight", clsig->getHeight(), 1.0f); +} + +VerifyRecSigStatus CChainLocksHandler::VerifyChainLock(const chainlock::ChainLockSig& clsig) const +{ + const auto llmqType = Params().GetConsensus().llmqTypeChainLocks; + const uint256 nRequestId = chainlock::GenSigRequestId(clsig.getHeight()); + + return llmq::VerifyRecoveredSig(llmqType, m_chainstate.m_chain, qman, clsig.getHeight(), nRequestId, + clsig.getBlockHash(), clsig.getSig()); +} + +bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash) const +{ + AssertLockNotHeld(cs); + LOCK(cs); + + if (!IsEnabled()) { + return false; + } + + if (bestChainLockBlockIndex == nullptr) { + return false; + } + + if (nHeight > bestChainLockBlockIndex->nHeight) { + return false; + } + + if (nHeight == bestChainLockBlockIndex->nHeight) { + return blockHash == bestChainLockBlockIndex->GetBlockHash(); + } + + const auto* pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight); + return (pAncestor != nullptr) && pAncestor->GetBlockHash() == blockHash; +} + +bool CChainLocksHandler::HasConflictingChainLock(int nHeight, const uint256& blockHash) const +{ + AssertLockNotHeld(cs); + LOCK(cs); + + if (!IsEnabled()) { + return false; + } + + if (bestChainLockBlockIndex == nullptr) { + return false; + } + + if (nHeight > bestChainLockBlockIndex->nHeight) { + return false; + } + + if (nHeight == bestChainLockBlockIndex->nHeight) { + return blockHash != bestChainLockBlockIndex->GetBlockHash(); + } + + const auto* pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight); + assert(pAncestor); + return pAncestor->GetBlockHash() != blockHash; +} + +void CChainLocksHandler::Cleanup() +{ + constexpr auto CLEANUP_INTERVAL{30s}; + if (!m_mn_sync.IsBlockchainSynced()) { + return; + } + + if (!cleanupThrottler.TryCleanup(CLEANUP_INTERVAL)) { + return; + } + + { + LOCK(cs); + for (auto it = seenChainLocks.begin(); it != seenChainLocks.end();) { + if (GetTime() - it->second >= CLEANUP_SEEN_TIMEOUT) { + it = seenChainLocks.erase(it); + } else { + ++it; + } + } + } + + if (auto signer = m_signer.load(std::memory_order_acquire); signer) { + const auto cleanup_txes{signer->Cleanup()}; + LOCK(cs); + for (const auto& tx : cleanup_txes) { + for (const auto& txid : *tx) { + txFirstSeenTime.erase(txid); + } + } + } + + LOCK(::cs_main); + LOCK2(mempool.cs, cs); // need mempool.cs due to GetTransaction calls + for (auto it = txFirstSeenTime.begin(); it != txFirstSeenTime.end();) { + uint256 hashBlock; + if (auto tx = GetTransaction(nullptr, &mempool, it->first, Params().GetConsensus(), hashBlock); !tx) { + // tx has vanished, probably due to conflicts + it = txFirstSeenTime.erase(it); + } else if (!hashBlock.IsNull()) { + const auto* pindex = m_chainstate.m_blockman.LookupBlockIndex(hashBlock); + assert(pindex); // GetTransaction gave us that hashBlock, it should resolve to a valid block index + if (m_chainstate.m_chain.Tip()->GetAncestor(pindex->nHeight) == pindex && + m_chainstate.m_chain.Height() - pindex->nHeight > chainlock::TX_CONFIRM_THRESHOLD) { + // tx is sufficiently deep, we can stop tracking it + it = txFirstSeenTime.erase(it); + } else { + ++it; + } + } else { + ++it; + } + } +} +} // namespace llmq diff --git a/src/chainlock/handler.h b/src/chainlock/handler.h new file mode 100644 index 000000000000..b25d4f0fc78c --- /dev/null +++ b/src/chainlock/handler.h @@ -0,0 +1,138 @@ +// Copyright (c) 2019-2025 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CHAINLOCK_HANDLER_H +#define BITCOIN_CHAINLOCK_HANDLER_H + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +class CBlock; +class CBlockIndex; +class CChainState; +class CMasternodeSync; +class CScheduler; +class CSporkManager; +class CTxMemPool; + +namespace llmq { +class CInstantSendManager; +class CQuorumManager; +class CSigningManager; +enum class VerifyRecSigStatus : uint8_t; + +class CChainLocksHandler final : public chainlock::ChainLockSignerParent +{ +private: + CChainState& m_chainstate; + CQuorumManager& qman; + CSporkManager& spork_manager; + CTxMemPool& mempool; + const CMasternodeSync& m_mn_sync; + std::unique_ptr scheduler; + std::unique_ptr scheduler_thread; + + std::atomic m_signer{nullptr}; + + mutable Mutex cs; + std::atomic tryLockChainTipScheduled{false}; + std::atomic isEnabled{false}; + + uint256 bestChainLockHash GUARDED_BY(cs); + chainlock::ChainLockSig bestChainLock GUARDED_BY(cs); + + chainlock::ChainLockSig bestChainLockWithKnownBlock GUARDED_BY(cs); + const CBlockIndex* bestChainLockBlockIndex GUARDED_BY(cs){nullptr}; + const CBlockIndex* lastNotifyChainLockBlockIndex GUARDED_BY(cs){nullptr}; + + Uint256HashMap txFirstSeenTime GUARDED_BY(cs); + + std::map seenChainLocks GUARDED_BY(cs); + + CleanupThrottler cleanupThrottler; + +public: + CChainLocksHandler() = delete; + CChainLocksHandler(const CChainLocksHandler&) = delete; + CChainLocksHandler& operator=(const CChainLocksHandler&) = delete; + explicit CChainLocksHandler(CChainState& chainstate, CQuorumManager& _qman, CSporkManager& sporkman, + CTxMemPool& _mempool, const CMasternodeSync& mn_sync); + ~CChainLocksHandler(); + + void ConnectSigner(gsl::not_null signer) + { + // Prohibit double initialization + assert(m_signer.load(std::memory_order_acquire) == nullptr); + m_signer.store(signer, std::memory_order_release); + } + void DisconnectSigner() { m_signer.store(nullptr, std::memory_order_release); } + + void Start(const llmq::CInstantSendManager& isman); + void Stop(); + + bool AlreadyHave(const CInv& inv) const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + bool GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + chainlock::ChainLockSig GetBestChainLock() const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + void UpdateTxFirstSeenMap(const Uint256HashSet& tx, const int64_t& time) override EXCLUSIVE_LOCKS_REQUIRED(!cs); + + [[nodiscard]] MessageProcessingResult ProcessNewChainLock(NodeId from, const chainlock::ChainLockSig& clsig, + const uint256& hash) override + EXCLUSIVE_LOCKS_REQUIRED(!cs); + + void AcceptedBlockHeader(gsl::not_null pindexNew) + EXCLUSIVE_LOCKS_REQUIRED(!cs); + void UpdatedBlockTip(const llmq::CInstantSendManager& isman); + void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) + EXCLUSIVE_LOCKS_REQUIRED(!cs); + void BlockConnected(const std::shared_ptr& pblock, gsl::not_null pindex) + EXCLUSIVE_LOCKS_REQUIRED(!cs); + void BlockDisconnected(const std::shared_ptr& pblock, gsl::not_null pindexDisconnected) + EXCLUSIVE_LOCKS_REQUIRED(!cs); + void CheckActiveState() + EXCLUSIVE_LOCKS_REQUIRED(!cs); + void EnforceBestChainLock() + EXCLUSIVE_LOCKS_REQUIRED(!cs); + + bool HasChainLock(int nHeight, const uint256& blockHash) const override + EXCLUSIVE_LOCKS_REQUIRED(!cs); + bool HasConflictingChainLock(int nHeight, const uint256& blockHash) const override + EXCLUSIVE_LOCKS_REQUIRED(!cs); + VerifyRecSigStatus VerifyChainLock(const chainlock::ChainLockSig& clsig) const; + + [[nodiscard]] int32_t GetBestChainLockHeight() const override + EXCLUSIVE_LOCKS_REQUIRED(!cs); + bool IsTxSafeForMining(const uint256& txid) const override + EXCLUSIVE_LOCKS_REQUIRED(!cs); + [[nodiscard]] bool IsEnabled() const override { return isEnabled; } + +private: + void Cleanup() + EXCLUSIVE_LOCKS_REQUIRED(!cs); +}; + +bool AreChainLocksEnabled(const CSporkManager& sporkman); +} // namespace llmq + +#endif // BITCOIN_CHAINLOCK_HANDLER_H From 3bb34334b3d9318b339698bd736c31586d60de31 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 19 Jan 2026 00:08:07 +0700 Subject: [PATCH 02/16] refactor: split out lightweight chainlock::Chainlocks and widely used it interface chainlock::Chainlocks has a knowledge about chain state and about the best known chainlock, but doesn't enforce chainlock, have no dependency on mempool and transactions. It doesn't have any permanent state save on disk (no dependency on evodb). Though, it depends on SporkManager which could enable / disable chainlocks completely and chainlock::Chainlocks provide an interface over it. llmq::CChainLocksHandler (chainlock::ChainlocksHandler in the future commits) does change chain state by enforcing best tip, it tracks transactions and clhandler have running bacground thread. Also clhandler is responsible for processing newly signed chainlock and related network messages --- src/active/context.cpp | 8 +- src/active/context.h | 3 +- src/bench/rpc_blockchain.cpp | 4 +- src/chainlock/chainlock.cpp | 153 ++++++++++++++++++ src/chainlock/chainlock.h | 55 ++++++- src/chainlock/handler.cpp | 204 ++++-------------------- src/chainlock/handler.h | 28 +--- src/chainlock/signing.cpp | 21 +-- src/chainlock/signing.h | 10 +- src/coinjoin/coinjoin.cpp | 18 +-- src/coinjoin/coinjoin.h | 13 +- src/dsnotificationinterface.cpp | 13 +- src/dsnotificationinterface.h | 9 +- src/evo/chainhelper.cpp | 12 +- src/evo/chainhelper.h | 10 +- src/evo/specialtxman.cpp | 19 ++- src/evo/specialtxman.h | 8 +- src/init.cpp | 9 +- src/instantsend/instantsend.cpp | 12 +- src/instantsend/instantsend.h | 7 +- src/instantsend/signing.cpp | 6 +- src/instantsend/signing.h | 6 +- src/llmq/context.cpp | 10 +- src/llmq/context.h | 4 +- src/net_processing.cpp | 16 +- src/net_processing.h | 2 + src/node/chainstate.cpp | 8 +- src/node/chainstate.h | 3 + src/node/context.cpp | 1 + src/node/context.h | 3 + src/node/interfaces.cpp | 4 +- src/node/miner.cpp | 10 +- src/node/miner.h | 2 + src/rest.cpp | 11 +- src/rpc/blockchain.cpp | 28 ++-- src/rpc/blockchain.h | 6 +- src/rpc/mining.cpp | 8 +- src/rpc/quorums.cpp | 6 +- src/rpc/rawtransaction.cpp | 18 ++- src/test/coinjoin_inouts_tests.cpp | 5 +- src/test/util/setup_common.cpp | 7 +- src/validation.cpp | 4 +- src/validation.h | 6 +- test/lint/lint-circular-dependencies.py | 11 +- 44 files changed, 450 insertions(+), 351 deletions(-) diff --git a/src/active/context.cpp b/src/active/context.cpp index d025c61b6149..6af78d66d37e 100644 --- a/src/active/context.cpp +++ b/src/active/context.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -25,7 +25,7 @@ ActiveContext::ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman, CGovernanceManager& govman, - CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman, + CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumManager& qman, llmq::CQuorumSnapshotManager& qsnapman, llmq::CSigningManager& sigman, @@ -44,9 +44,9 @@ ActiveContext::ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman ehf_sighandler{std::make_unique(chainman, mnhfman, sigman, *shareman, qman)}, qman_handler{std::make_unique(bls_worker, connman, dmnman, qman, qsnapman, *nodeman, chainman, mn_sync, sporkman, sync_map, quorums_recovery, quorums_watch)}, - cl_signer{std::make_unique(chainman.ActiveChainstate(), clhandler, sigman, *shareman, + cl_signer{std::make_unique(chainman.ActiveChainstate(), chainlocks, clhandler, sigman, *shareman, sporkman, mn_sync)}, - is_signer{std::make_unique(chainman.ActiveChainstate(), clhandler, isman, sigman, + is_signer{std::make_unique(chainman.ActiveChainstate(), chainlocks, isman, sigman, *shareman, qman, sporkman, mempool, mn_sync)} { qdkgsman->InitializeHandlers([&](const Consensus::LLMQParams& llmq_params, diff --git a/src/active/context.h b/src/active/context.h index 9b3d5896ec07..e9070bd8412e 100644 --- a/src/active/context.h +++ b/src/active/context.h @@ -29,6 +29,7 @@ class CTxMemPool; class GovernanceSigner; class PeerManager; namespace chainlock { +class Chainlocks; class ChainLockSigner; } // namespace chainlock namespace instantsend { @@ -63,7 +64,7 @@ struct ActiveContext final : public CValidationInterface { ActiveContext& operator=(const ActiveContext&) = delete; explicit ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman, CConnman& connman, CDeterministicMNManager& dmnman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CMNHFManager& mnhfman, CSporkManager& sporkman, CTxMemPool& mempool, + CMNHFManager& mnhfman, CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumManager& qman, llmq::CQuorumSnapshotManager& qsnapman, llmq::CSigningManager& sigman, PeerManager& peerman, diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp index 9ed1fffb2f78..c60558009edd 100644 --- a/src/bench/rpc_blockchain.cpp +++ b/src/bench/rpc_blockchain.cpp @@ -45,7 +45,7 @@ static void BlockToJsonVerbose(benchmark::Bench& bench) TestBlockAndIndex data; const LLMQContext& llmq_ctx = *data.testing_setup->m_node.llmq_ctx; bench.run([&] { - auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); + auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *data.testing_setup->m_node.chainlocks, *llmq_ctx.isman, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); ankerl::nanobench::doNotOptimizeAway(univalue); }); } @@ -56,7 +56,7 @@ static void BlockToJsonVerboseWrite(benchmark::Bench& bench) { TestBlockAndIndex data; const LLMQContext& llmq_ctx = *data.testing_setup->m_node.llmq_ctx; - auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); + auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *data.testing_setup->m_node.chainlocks, *llmq_ctx.isman, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); bench.run([&] { auto str = univalue.write(); ankerl::nanobench::doNotOptimizeAway(str); diff --git a/src/chainlock/chainlock.cpp b/src/chainlock/chainlock.cpp index e6f3343e11ac..b3d6e6c60d7e 100644 --- a/src/chainlock/chainlock.cpp +++ b/src/chainlock/chainlock.cpp @@ -2,4 +2,157 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include +#include + +namespace chainlock +{ + +Chainlocks::Chainlocks(const CSporkManager& sporkman) : m_sporks(sporkman) {} + +bool Chainlocks::IsEnabled() const +{ + return m_sporks.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED); +} + +chainlock::ChainLockSig Chainlocks::GetBestChainLock() const +{ + LOCK(cs); + return bestChainLock; +} + +int32_t Chainlocks::GetBestChainLockHeight() const +{ + LOCK(cs); + return bestChainLock.getHeight(); +} + +bool Chainlocks::HasChainLock(int nHeight, const uint256& blockHash) const +{ + LOCK(cs); + + if (!IsEnabled()) { + return false; + } + + if (bestChainLockBlockIndex == nullptr) { + return false; + } + + if (nHeight > bestChainLockBlockIndex->nHeight) { + return false; + } + + if (nHeight == bestChainLockBlockIndex->nHeight) { + return blockHash == bestChainLockBlockIndex->GetBlockHash(); + } + + const auto* pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight); + return (pAncestor != nullptr) && pAncestor->GetBlockHash() == blockHash; +} + +bool Chainlocks::HasConflictingChainLock(int nHeight, const uint256& blockHash) const +{ + LOCK(cs); + + if (!IsEnabled()) { + return false; + } + + if (bestChainLockBlockIndex == nullptr) { + return false; + } + + if (nHeight > bestChainLockBlockIndex->nHeight) { + return false; + } + + if (nHeight == bestChainLockBlockIndex->nHeight) { + return blockHash != bestChainLockBlockIndex->GetBlockHash(); + } + + const auto* pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight); + assert(pAncestor); + return pAncestor->GetBlockHash() != blockHash; +} + +void Chainlocks::ResetChainlock() +{ + LOCK(cs); + bestChainLockHash = uint256{}; + bestChainLock = bestChainLockWithKnownBlock = chainlock::ChainLockSig{}; + bestChainLockBlockIndex = nullptr; +} + +bool Chainlocks::UpdateBestChainlock(const uint256& hash, const chainlock::ChainLockSig& clsig, const CBlockIndex* pindex) +{ + LOCK(cs); + + if (clsig.getHeight() <= bestChainLock.getHeight()) { + // no need to process older/same CLSIGs + return false; + } + + bestChainLockHash = hash; + bestChainLock = clsig; + + if (pindex) { + if (pindex->nHeight != clsig.getHeight()) { + // Should not happen, same as the conflict check from above. + LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n", + __func__, clsig.ToString(), pindex->nHeight); + // Note: not relaying clsig here + return false; + } + bestChainLockWithKnownBlock = clsig; + bestChainLockBlockIndex = pindex; + } + // We don't know the block/header for this CLSIG yet, so bail out for now and when the + // block/header later comes in, we will enforce the correct chain. We still relay further. + return true; +} + +std::pair Chainlocks::GetBestChainlockWithPindex() const +{ + LOCK(cs); + return {bestChainLockWithKnownBlock, bestChainLockBlockIndex}; +} + +bool Chainlocks::GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const +{ + LOCK(cs); + + if (bestChainLockHash != hash) { + // we only propagate the best one and ditch all the old ones + return false; + } + ret = bestChainLock; + return true; +} + +void Chainlocks::AcceptedBlockHeader(gsl::not_null pindexNew) +{ + LOCK(cs); + + if (pindexNew->GetBlockHash() == bestChainLock.getBlockHash()) { + LogPrint(BCLog::CHAINLOCKS, "Chainlocks::%s -- block header %s came in late, updating and enforcing\n", + __func__, pindexNew->GetBlockHash().ToString()); + + if (bestChainLock.getHeight() != pindexNew->nHeight) { + // Should not happen, same as the conflict check from ProcessNewChainLock. + LogPrintf("Chainlocks::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n", + __func__, bestChainLock.ToString(), pindexNew->nHeight); + return; + } + + // when EnforceBestChainLock is called later, it might end up invalidating other chains but not activating the + // CLSIG locked chain. This happens when only the header is known but the block is still missing yet. The usual + // block processing logic will handle this when the block arrives + bestChainLockWithKnownBlock = bestChainLock; + bestChainLockBlockIndex = pindexNew; + } +} + +} // namespace chainlock:: diff --git a/src/chainlock/chainlock.h b/src/chainlock/chainlock.h index a7594101b652..21c9f2b73005 100644 --- a/src/chainlock/chainlock.h +++ b/src/chainlock/chainlock.h @@ -5,6 +5,59 @@ #ifndef BITCOIN_CHAINLOCK_CHAINLOCK_H #define BITCOIN_CHAINLOCK_CHAINLOCK_H -#include +#include +#include +#include +#include +#include + +class CSporkManager; + +namespace chainlock +{ + +class Chainlocks +{ +private: + const CSporkManager& m_sporks; + + mutable Mutex cs; + const CBlockIndex* bestChainLockBlockIndex GUARDED_BY(cs){nullptr}; + + uint256 bestChainLockHash GUARDED_BY(cs); + chainlock::ChainLockSig bestChainLock GUARDED_BY(cs); + + chainlock::ChainLockSig bestChainLockWithKnownBlock GUARDED_BY(cs); +public: + + Chainlocks(const CSporkManager& sporkman); + + [[nodiscard]] bool IsEnabled() const; + + [[nodiscard]] chainlock::ChainLockSig GetBestChainLock() const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + + [[nodiscard]] int32_t GetBestChainLockHeight() const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + + [[nodiscard]] bool HasChainLock(int nHeight, const uint256& blockHash) const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + + [[nodiscard]] bool HasConflictingChainLock(int nHeight, const uint256& blockHash) const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + + bool UpdateBestChainlock(const uint256& hash, const chainlock::ChainLockSig& clsig, const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs); + + std::pair GetBestChainlockWithPindex() const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + + bool GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + + void AcceptedBlockHeader(gsl::not_null pindexNew) EXCLUSIVE_LOCKS_REQUIRED(!cs); + + void ResetChainlock() EXCLUSIVE_LOCKS_REQUIRED(!cs); +}; +} // namespace chainlock #endif // BITCOIN_CHAINLOCK_CHAINLOCK_H diff --git a/src/chainlock/handler.cpp b/src/chainlock/handler.cpp index bbfb3c9f83af..c6b27ad0122e 100644 --- a/src/chainlock/handler.cpp +++ b/src/chainlock/handler.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include +#include #include #include @@ -19,7 +19,6 @@ #include #include #include -#include #include // Forward declaration to break dependency over node/transaction.h @@ -37,16 +36,11 @@ static constexpr auto CLEANUP_SEEN_TIMEOUT{24h}; static constexpr auto WAIT_FOR_ISLOCK_TIMEOUT{10min}; } // anonymous namespace -bool AreChainLocksEnabled(const CSporkManager& sporkman) -{ - return sporkman.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED); -} - -CChainLocksHandler::CChainLocksHandler(CChainState& chainstate, CQuorumManager& _qman, CSporkManager& sporkman, +CChainLocksHandler::CChainLocksHandler(chainlock::Chainlocks& chainlocks, CChainState& chainstate, CQuorumManager& _qman, CTxMemPool& _mempool, const CMasternodeSync& mn_sync) : + m_chainlocks{chainlocks}, m_chainstate{chainstate}, qman{_qman}, - spork_manager{sporkman}, mempool{_mempool}, m_mn_sync{mn_sync}, scheduler{std::make_unique()}, @@ -88,25 +82,6 @@ bool CChainLocksHandler::AlreadyHave(const CInv& inv) const return seenChainLocks.count(inv.hash) != 0; } -bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const -{ - LOCK(cs); - - if (hash != bestChainLockHash) { - // we only propagate the best one and ditch all the old ones - return false; - } - - ret = bestChainLock; - return true; -} - -chainlock::ChainLockSig CChainLocksHandler::GetBestChainLock() const -{ - LOCK(cs); - return bestChainLock; -} - void CChainLocksHandler::UpdateTxFirstSeenMap(const Uint256HashSet& tx, const int64_t& time) { AssertLockNotHeld(cs); @@ -127,7 +102,8 @@ MessageProcessingResult CChainLocksHandler::ProcessNewChainLock(const NodeId fro return {}; } - if (!bestChainLock.IsNull() && clsig.getHeight() <= bestChainLock.getHeight()) { + // height is expect to check twice: preliminary (for optimization) and inside UpdateBestsChainlock (as mutex is not kept during validation) + if (clsig.getHeight() <= m_chainlocks.GetBestChainLockHeight()) { // no need to process older/same CLSIGs return {}; } @@ -143,69 +119,22 @@ MessageProcessingResult CChainLocksHandler::ProcessNewChainLock(const NodeId fro } const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainstate.m_blockman.LookupBlockIndex(clsig.getBlockHash())); - const CInv clsig_inv(MSG_CLSIG, hash); - - { - LOCK(cs); - // newer chainlock could be processed via another thread while we were not holding the lock, re-verify - if (!bestChainLock.IsNull() && clsig.getHeight() <= bestChainLock.getHeight()) { - // no need to process older/same CLSIGs - return {}; - } - bestChainLockHash = hash; - bestChainLock = clsig; - - if (pindex) { - if (pindex->nHeight != clsig.getHeight()) { - // Should not happen, same as the conflict check from above. - LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n", - __func__, clsig.ToString(), pindex->nHeight); - // Note: not relaying clsig here - return {}; - } - bestChainLockWithKnownBlock = bestChainLock; - bestChainLockBlockIndex = pindex; - } else { - // We don't know the block/header for this CLSIG yet, so bail out for now and when the - // block/header later comes in, we will enforce the correct chain. We still relay further. - return clsig_inv; - } - } - - scheduler->scheduleFromNow( - [&]() { - CheckActiveState(); - EnforceBestChainLock(); - }, - std::chrono::seconds{0}); - - LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n", __func__, - clsig.ToString(), from); - return clsig_inv; -} + if (!m_chainlocks.UpdateBestChainlock(hash, clsig, pindex)) return {}; -void CChainLocksHandler::AcceptedBlockHeader(gsl::not_null pindexNew) -{ - LOCK(cs); - - if (pindexNew->GetBlockHash() == bestChainLock.getBlockHash()) { - LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", - __func__, pindexNew->GetBlockHash().ToString()); - - if (bestChainLock.getHeight() != pindexNew->nHeight) { - // Should not happen, same as the conflict check from ProcessNewChainLock. - LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n", - __func__, bestChainLock.ToString(), pindexNew->nHeight); - return; - } + if (pindex) { + scheduler->scheduleFromNow( + [&]() { + CheckActiveState(); + EnforceBestChainLock(); + }, + std::chrono::seconds{0}); - // when EnforceBestChainLock is called later, it might end up invalidating other chains but not activating the - // CLSIG locked chain. This happens when only the header is known but the block is still missing yet. The usual - // block processing logic will handle this when the block arrives - bestChainLockWithKnownBlock = bestChainLock; - bestChainLockBlockIndex = pindexNew; + LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n", __func__, + clsig.ToString(), from); } + const CInv clsig_inv(MSG_CLSIG, hash); + return clsig_inv; } void CChainLocksHandler::UpdatedBlockTip(const llmq::CInstantSendManager& isman) @@ -233,16 +162,15 @@ void CChainLocksHandler::UpdatedBlockTip(const llmq::CInstantSendManager& isman) void CChainLocksHandler::CheckActiveState() { bool oldIsEnabled = isEnabled; - isEnabled = AreChainLocksEnabled(spork_manager); + isEnabled = m_chainlocks.IsEnabled(); if (!oldIsEnabled && isEnabled) { // ChainLocks got activated just recently, but it's possible that it was already running before, leaving // us with some stale values which we should not try to enforce anymore (there probably was a good reason // to disable spork19) + m_chainlocks.ResetChainlock(); LOCK(cs); - bestChainLockHash = uint256(); - bestChainLock = bestChainLockWithKnownBlock = chainlock::ChainLockSig(); - bestChainLockBlockIndex = lastNotifyChainLockBlockIndex = nullptr; + lastNotifyChainLockBlockIndex = nullptr; } } @@ -287,13 +215,6 @@ void CChainLocksHandler::BlockDisconnected(const std::shared_ptr& } } -int32_t CChainLocksHandler::GetBestChainLockHeight() const -{ - AssertLockNotHeld(cs); - LOCK(cs); - return bestChainLock.getHeight(); -} - bool CChainLocksHandler::IsTxSafeForMining(const uint256& txid) const { auto tx_age{0s}; @@ -312,26 +233,18 @@ bool CChainLocksHandler::IsTxSafeForMining(const uint256& txid) const // This should also not be called from validation signals, as this might result in recursive calls void CChainLocksHandler::EnforceBestChainLock() { + if (!isEnabled) { + return; + } + AssertLockNotHeld(cs); AssertLockNotHeld(cs_main); - std::shared_ptr clsig; - const CBlockIndex* pindex; - const CBlockIndex* currentBestChainLockBlockIndex; - { - LOCK(cs); - if (!IsEnabled()) { - return; - } - - clsig = std::make_shared(bestChainLockWithKnownBlock); - pindex = currentBestChainLockBlockIndex = this->bestChainLockBlockIndex; - - if (currentBestChainLockBlockIndex == nullptr) { - // we don't have the header/block, so we can't do anything right now - return; - } + auto [clsig, currentBestChainLockBlockIndex] = m_chainlocks.GetBestChainlockWithPindex(); + if (currentBestChainLockBlockIndex == nullptr) { + // we don't have the header/block, so we can't do anything right now + return; } BlockValidationState dummy_state; @@ -340,8 +253,8 @@ void CChainLocksHandler::EnforceBestChainLock() // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig // and mark all of them as conflicting. LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s)\n", __func__, - pindex->GetBlockHash().ToString(), clsig->ToString()); - m_chainstate.EnforceBlock(dummy_state, pindex); + currentBestChainLockBlockIndex->GetBlockHash().ToString(), clsig.ToString()); + m_chainstate.EnforceBlock(dummy_state, currentBestChainLockBlockIndex); if (/*activateNeeded =*/WITH_LOCK(::cs_main, return m_chainstate.m_chain.Tip()->GetAncestor( @@ -364,9 +277,9 @@ void CChainLocksHandler::EnforceBestChainLock() lastNotifyChainLockBlockIndex = currentBestChainLockBlockIndex; } - GetMainSignals().NotifyChainLock(currentBestChainLockBlockIndex, clsig, clsig->ToString()); - uiInterface.NotifyChainLock(clsig->getBlockHash().ToString(), clsig->getHeight()); - ::g_stats_client->gauge("chainlocks.blockHeight", clsig->getHeight(), 1.0f); + GetMainSignals().NotifyChainLock(currentBestChainLockBlockIndex, std::make_shared(clsig), clsig.ToString()); + uiInterface.NotifyChainLock(clsig.getBlockHash().ToString(), clsig.getHeight()); + ::g_stats_client->gauge("chainlocks.blockHeight", clsig.getHeight(), 1.0f); } VerifyRecSigStatus CChainLocksHandler::VerifyChainLock(const chainlock::ChainLockSig& clsig) const @@ -378,57 +291,6 @@ VerifyRecSigStatus CChainLocksHandler::VerifyChainLock(const chainlock::ChainLoc clsig.getBlockHash(), clsig.getSig()); } -bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash) const -{ - AssertLockNotHeld(cs); - LOCK(cs); - - if (!IsEnabled()) { - return false; - } - - if (bestChainLockBlockIndex == nullptr) { - return false; - } - - if (nHeight > bestChainLockBlockIndex->nHeight) { - return false; - } - - if (nHeight == bestChainLockBlockIndex->nHeight) { - return blockHash == bestChainLockBlockIndex->GetBlockHash(); - } - - const auto* pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight); - return (pAncestor != nullptr) && pAncestor->GetBlockHash() == blockHash; -} - -bool CChainLocksHandler::HasConflictingChainLock(int nHeight, const uint256& blockHash) const -{ - AssertLockNotHeld(cs); - LOCK(cs); - - if (!IsEnabled()) { - return false; - } - - if (bestChainLockBlockIndex == nullptr) { - return false; - } - - if (nHeight > bestChainLockBlockIndex->nHeight) { - return false; - } - - if (nHeight == bestChainLockBlockIndex->nHeight) { - return blockHash != bestChainLockBlockIndex->GetBlockHash(); - } - - const auto* pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight); - assert(pAncestor); - return pAncestor->GetBlockHash() != blockHash; -} - void CChainLocksHandler::Cleanup() { constexpr auto CLEANUP_INTERVAL{30s}; diff --git a/src/chainlock/handler.h b/src/chainlock/handler.h index b25d4f0fc78c..06af1f6883e0 100644 --- a/src/chainlock/handler.h +++ b/src/chainlock/handler.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_CHAINLOCK_HANDLER_H #define BITCOIN_CHAINLOCK_HANDLER_H +#include #include #include @@ -31,7 +32,6 @@ class CBlockIndex; class CChainState; class CMasternodeSync; class CScheduler; -class CSporkManager; class CTxMemPool; namespace llmq { @@ -43,9 +43,10 @@ enum class VerifyRecSigStatus : uint8_t; class CChainLocksHandler final : public chainlock::ChainLockSignerParent { private: + chainlock::Chainlocks& m_chainlocks; + CChainState& m_chainstate; CQuorumManager& qman; - CSporkManager& spork_manager; CTxMemPool& mempool; const CMasternodeSync& m_mn_sync; std::unique_ptr scheduler; @@ -57,13 +58,7 @@ class CChainLocksHandler final : public chainlock::ChainLockSignerParent std::atomic tryLockChainTipScheduled{false}; std::atomic isEnabled{false}; - uint256 bestChainLockHash GUARDED_BY(cs); - chainlock::ChainLockSig bestChainLock GUARDED_BY(cs); - - chainlock::ChainLockSig bestChainLockWithKnownBlock GUARDED_BY(cs); - const CBlockIndex* bestChainLockBlockIndex GUARDED_BY(cs){nullptr}; const CBlockIndex* lastNotifyChainLockBlockIndex GUARDED_BY(cs){nullptr}; - Uint256HashMap txFirstSeenTime GUARDED_BY(cs); std::map seenChainLocks GUARDED_BY(cs); @@ -74,7 +69,7 @@ class CChainLocksHandler final : public chainlock::ChainLockSignerParent CChainLocksHandler() = delete; CChainLocksHandler(const CChainLocksHandler&) = delete; CChainLocksHandler& operator=(const CChainLocksHandler&) = delete; - explicit CChainLocksHandler(CChainState& chainstate, CQuorumManager& _qman, CSporkManager& sporkman, + explicit CChainLocksHandler(chainlock::Chainlocks& chainlocks, CChainState& chainstate, CQuorumManager& _qman, CTxMemPool& _mempool, const CMasternodeSync& mn_sync); ~CChainLocksHandler(); @@ -91,18 +86,12 @@ class CChainLocksHandler final : public chainlock::ChainLockSignerParent bool AlreadyHave(const CInv& inv) const EXCLUSIVE_LOCKS_REQUIRED(!cs); - bool GetChainLockByHash(const uint256& hash, chainlock::ChainLockSig& ret) const - EXCLUSIVE_LOCKS_REQUIRED(!cs); - chainlock::ChainLockSig GetBestChainLock() const - EXCLUSIVE_LOCKS_REQUIRED(!cs); void UpdateTxFirstSeenMap(const Uint256HashSet& tx, const int64_t& time) override EXCLUSIVE_LOCKS_REQUIRED(!cs); [[nodiscard]] MessageProcessingResult ProcessNewChainLock(NodeId from, const chainlock::ChainLockSig& clsig, const uint256& hash) override EXCLUSIVE_LOCKS_REQUIRED(!cs); - void AcceptedBlockHeader(gsl::not_null pindexNew) - EXCLUSIVE_LOCKS_REQUIRED(!cs); void UpdatedBlockTip(const llmq::CInstantSendManager& isman); void TransactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) EXCLUSIVE_LOCKS_REQUIRED(!cs); @@ -115,24 +104,15 @@ class CChainLocksHandler final : public chainlock::ChainLockSignerParent void EnforceBestChainLock() EXCLUSIVE_LOCKS_REQUIRED(!cs); - bool HasChainLock(int nHeight, const uint256& blockHash) const override - EXCLUSIVE_LOCKS_REQUIRED(!cs); - bool HasConflictingChainLock(int nHeight, const uint256& blockHash) const override - EXCLUSIVE_LOCKS_REQUIRED(!cs); VerifyRecSigStatus VerifyChainLock(const chainlock::ChainLockSig& clsig) const; - [[nodiscard]] int32_t GetBestChainLockHeight() const override - EXCLUSIVE_LOCKS_REQUIRED(!cs); bool IsTxSafeForMining(const uint256& txid) const override EXCLUSIVE_LOCKS_REQUIRED(!cs); - [[nodiscard]] bool IsEnabled() const override { return isEnabled; } private: void Cleanup() EXCLUSIVE_LOCKS_REQUIRED(!cs); }; - -bool AreChainLocksEnabled(const CSporkManager& sporkman); } // namespace llmq #endif // BITCOIN_CHAINLOCK_HANDLER_H diff --git a/src/chainlock/signing.cpp b/src/chainlock/signing.cpp index 0eba67843312..95cc50129258 100644 --- a/src/chainlock/signing.cpp +++ b/src/chainlock/signing.cpp @@ -16,10 +16,11 @@ using node::ReadBlockFromDisk; namespace chainlock { -ChainLockSigner::ChainLockSigner(CChainState& chainstate, ChainLockSignerParent& clhandler, +ChainLockSigner::ChainLockSigner(CChainState& chainstate, const chainlock::Chainlocks& chainlocks, ChainLockSignerParent& clhandler, llmq::CSigningManager& sigman, llmq::CSigSharesManager& shareman, CSporkManager& sporkman, const CMasternodeSync& mn_sync) : m_chainstate{chainstate}, + m_chainlocks{chainlocks}, m_clhandler{clhandler}, m_sigman{sigman}, m_shareman{shareman}, @@ -46,7 +47,7 @@ void ChainLockSigner::TrySignChainTip(const llmq::CInstantSendManager& isman) return; } - if (!m_clhandler.IsEnabled()) { + if (!m_chainlocks.IsEnabled()) { return; } @@ -75,12 +76,12 @@ void ChainLockSigner::TrySignChainTip(const llmq::CInstantSendManager& isman) } } - if (m_clhandler.GetBestChainLockHeight() >= pindex->nHeight) { + if (m_chainlocks.GetBestChainLockHeight() >= pindex->nHeight) { // already got the same CLSIG or a better one return; } - if (m_clhandler.HasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) { + if (m_chainlocks.HasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) { // don't sign if another conflicting CLSIG is already present. EnforceBestChainLock will later enforce // the correct chain. return; @@ -102,7 +103,7 @@ void ChainLockSigner::TrySignChainTip(const llmq::CInstantSendManager& isman) LogPrint(BCLog::CHAINLOCKS, "%s -- tip and previous %d blocks all safe\n", __func__, TX_CONFIRM_THRESHOLD); break; } - if (m_clhandler.HasChainLock(pindexWalk->nHeight, pindexWalk->GetBlockHash())) { + if (m_chainlocks.HasChainLock(pindexWalk->nHeight, pindexWalk->GetBlockHash())) { // we don't care about islocks for TXs that are ChainLocked already LogPrint(BCLog::CHAINLOCKS, "%s -- chainlock at height %d\n", __func__, pindexWalk->nHeight); break; @@ -130,7 +131,7 @@ void ChainLockSigner::TrySignChainTip(const llmq::CInstantSendManager& isman) uint256 requestId = GenSigRequestId(pindex->nHeight); uint256 msgHash = pindex->GetBlockHash(); - if (m_clhandler.GetBestChainLockHeight() >= pindex->nHeight) { + if (m_chainlocks.GetBestChainLockHeight() >= pindex->nHeight) { // might have happened while we didn't hold cs return; } @@ -222,7 +223,7 @@ ChainLockSigner::BlockTxs::mapped_type ChainLockSigner::GetBlockTxs(const uint25 MessageProcessingResult ChainLockSigner::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig) { - if (!m_clhandler.IsEnabled()) { + if (!m_chainlocks.IsEnabled()) { return {}; } @@ -234,7 +235,7 @@ MessageProcessingResult ChainLockSigner::HandleNewRecoveredSig(const llmq::CReco // this is not what we signed, so lets not create a CLSIG for it return {}; } - if (m_clhandler.GetBestChainLockHeight() >= lastSignedHeight) { + if (m_chainlocks.GetBestChainLockHeight() >= lastSignedHeight) { // already got the same or a better CLSIG through the CLSIG message return {}; } @@ -253,10 +254,10 @@ std::vector> ChainLockSigner::Cleanup() const auto* pindex = m_chainstate.m_blockman.LookupBlockIndex(it->first); if (!pindex) { it = blockTxs.erase(it); - } else if (m_clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash())) { + } else if (m_chainlocks.HasChainLock(pindex->nHeight, pindex->GetBlockHash())) { removed.push_back(it->second); it = blockTxs.erase(it); - } else if (m_clhandler.HasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) { + } else if (m_chainlocks.HasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) { it = blockTxs.erase(it); } else { ++it; diff --git a/src/chainlock/signing.h b/src/chainlock/signing.h index a6fac9de17d6..1bc06ce2bed2 100644 --- a/src/chainlock/signing.h +++ b/src/chainlock/signing.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_CHAINLOCK_SIGNING_H #define BITCOIN_CHAINLOCK_SIGNING_H +#include #include #include @@ -28,10 +29,6 @@ class ChainLockSignerParent public: virtual ~ChainLockSignerParent() = default; - virtual int32_t GetBestChainLockHeight() const = 0; - virtual bool HasChainLock(int nHeight, const uint256& blockHash) const = 0; - virtual bool HasConflictingChainLock(int nHeight, const uint256& blockHash) const = 0; - virtual bool IsEnabled() const = 0; virtual bool IsTxSafeForMining(const uint256& txid) const = 0; [[nodiscard]] virtual MessageProcessingResult ProcessNewChainLock(NodeId from, const ChainLockSig& clsig, const uint256& hash) = 0; virtual void UpdateTxFirstSeenMap(const Uint256HashSet& tx, const int64_t& time) = 0; @@ -41,7 +38,8 @@ class ChainLockSigner final : public llmq::CRecoveredSigsListener { private: CChainState& m_chainstate; - ChainLockSignerParent& m_clhandler; + const chainlock::Chainlocks& m_chainlocks; + ChainLockSignerParent& m_clhandler; // TODO: fully replace to Chainlocks llmq::CSigningManager& m_sigman; llmq::CSigSharesManager& m_shareman; CSporkManager& m_sporkman; @@ -66,7 +64,7 @@ class ChainLockSigner final : public llmq::CRecoveredSigsListener ChainLockSigner() = delete; ChainLockSigner(const ChainLockSigner&) = delete; ChainLockSigner& operator=(const ChainLockSigner&) = delete; - explicit ChainLockSigner(CChainState& chainstate, ChainLockSignerParent& clhandler, llmq::CSigningManager& sigman, + explicit ChainLockSigner(CChainState& chainstate, const chainlock::Chainlocks& chainlocks, ChainLockSignerParent& clhandler, llmq::CSigningManager& sigman, llmq::CSigSharesManager& shareman, CSporkManager& sporkman, const CMasternodeSync& mn_sync); ~ChainLockSigner(); diff --git a/src/coinjoin/coinjoin.cpp b/src/coinjoin/coinjoin.cpp index 1d129d8521e6..78fbff078e28 100644 --- a/src/coinjoin/coinjoin.cpp +++ b/src/coinjoin/coinjoin.cpp @@ -81,12 +81,12 @@ bool CCoinJoinBroadcastTx::CheckSignature(const CBLSPublicKey& blsPubKey) const return true; } -bool CCoinJoinBroadcastTx::IsExpired(const CBlockIndex* pindex, const llmq::CChainLocksHandler& clhandler) const +bool CCoinJoinBroadcastTx::IsExpired(const CBlockIndex* pindex, const chainlock::Chainlocks& chainlocks) const { - // expire confirmed DSTXes after ~1h since confirmation or chainlocked confirmation + // expire confirmed DSTXes after ~1h since confirmation if (!nConfirmedHeight.has_value() || pindex->nHeight < *nConfirmedHeight) return false; // not mined yet if (pindex->nHeight - *nConfirmedHeight > 24) return true; // mined more than an hour ago - return clhandler.HasChainLock(pindex->nHeight, *pindex->phashBlock); + return chainlocks.HasChainLock(pindex->nHeight, *pindex->phashBlock); } bool CCoinJoinBroadcastTx::IsValidStructure() const @@ -417,13 +417,13 @@ CCoinJoinBroadcastTx CDSTXManager::GetDSTX(const uint256& hash) return (it == mapDSTX.end()) ? CCoinJoinBroadcastTx() : it->second; } -void CDSTXManager::CheckDSTXes(const CBlockIndex* pindex, const llmq::CChainLocksHandler& clhandler) +void CDSTXManager::CheckDSTXes(const CBlockIndex* pindex, const chainlock::Chainlocks& chainlocks) { AssertLockNotHeld(cs_mapdstx); LOCK(cs_mapdstx); auto it = mapDSTX.begin(); while (it != mapDSTX.end()) { - if (it->second.IsExpired(pindex, clhandler)) { + if (it->second.IsExpired(pindex, chainlocks)) { mapDSTX.erase(it++); } else { ++it; @@ -432,17 +432,17 @@ void CDSTXManager::CheckDSTXes(const CBlockIndex* pindex, const llmq::CChainLock LogPrint(BCLog::COINJOIN, "CoinJoin::CheckDSTXes -- mapDSTX.size()=%llu\n", mapDSTX.size()); } -void CDSTXManager::UpdatedBlockTip(const CBlockIndex* pindex, const llmq::CChainLocksHandler& clhandler, const CMasternodeSync& mn_sync) +void CDSTXManager::UpdatedBlockTip(const CBlockIndex* pindex, const chainlock::Chainlocks& chainlocks, const CMasternodeSync& mn_sync) { if (pindex && mn_sync.IsBlockchainSynced()) { - CheckDSTXes(pindex, clhandler); + CheckDSTXes(pindex, chainlocks); } } -void CDSTXManager::NotifyChainLock(const CBlockIndex* pindex, const llmq::CChainLocksHandler& clhandler, const CMasternodeSync& mn_sync) +void CDSTXManager::NotifyChainLock(const CBlockIndex* pindex, const chainlock::Chainlocks& chainlocks, const CMasternodeSync& mn_sync) { if (pindex && mn_sync.IsBlockchainSynced()) { - CheckDSTXes(pindex, clhandler); + CheckDSTXes(pindex, chainlocks); } } diff --git a/src/coinjoin/coinjoin.h b/src/coinjoin/coinjoin.h index 461aa16ec991..549cfc89a56c 100644 --- a/src/coinjoin/coinjoin.h +++ b/src/coinjoin/coinjoin.h @@ -29,8 +29,11 @@ class ChainstateManager; class CMasternodeSync; class CTxMemPool; +namespace chainlock { +class Chainlocks; +} // namespace chainlock + namespace llmq { -class CChainLocksHandler; class CInstantSendManager; } // namespace llmq @@ -280,7 +283,7 @@ class CCoinJoinBroadcastTx // Used only for unit tests [[nodiscard]] std::optional GetConfirmedHeight() const { return nConfirmedHeight; } void SetConfirmedHeight(std::optional nConfirmedHeightIn) { assert(nConfirmedHeightIn == std::nullopt || *nConfirmedHeightIn > 0); nConfirmedHeight = nConfirmedHeightIn; } - bool IsExpired(const CBlockIndex* pindex, const llmq::CChainLocksHandler& clhandler) const; + bool IsExpired(const CBlockIndex* pindex, const chainlock::Chainlocks& chainlocks) const; [[nodiscard]] bool IsValidStructure() const; }; @@ -380,10 +383,10 @@ class CDSTXManager void AddDSTX(const CCoinJoinBroadcastTx& dstx) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx); CCoinJoinBroadcastTx GetDSTX(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx); - void UpdatedBlockTip(const CBlockIndex* pindex, const llmq::CChainLocksHandler& clhandler, + void UpdatedBlockTip(const CBlockIndex* pindex, const chainlock::Chainlocks& chainlocks, const CMasternodeSync& mn_sync) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx); - void NotifyChainLock(const CBlockIndex* pindex, const llmq::CChainLocksHandler& clhandler, + void NotifyChainLock(const CBlockIndex* pindex, const chainlock::Chainlocks& chainlocks, const CMasternodeSync& mn_sync) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx); @@ -394,7 +397,7 @@ class CDSTXManager EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx); private: - void CheckDSTXes(const CBlockIndex* pindex, const llmq::CChainLocksHandler& clhandler) + void CheckDSTXes(const CBlockIndex* pindex, const chainlock::Chainlocks& chainlocks) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx); void UpdateDSTXConfirmedHeight(const CTransactionRef& tx, std::optional nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_mapdstx); diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index d3de81ca2031..c7c64e9b8d66 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -26,14 +27,16 @@ CDSNotificationInterface::CDSNotificationInterface(CConnman& connman, CGovernanceManager& govman, const ChainstateManager& chainman, const std::unique_ptr& dmnman, - const std::unique_ptr& llmq_ctx) : + const std::unique_ptr& llmq_ctx, + chainlock::Chainlocks& chainlocks) : m_connman{connman}, m_dstxman{dstxman}, m_mn_sync{mn_sync}, m_govman{govman}, m_chainman{chainman}, m_dmnman{dmnman}, - m_llmq_ctx{llmq_ctx} + m_llmq_ctx{llmq_ctx}, + m_chainlocks{chainlocks} { } @@ -47,7 +50,7 @@ void CDSNotificationInterface::InitializeCurrentBlockTip() void CDSNotificationInterface::AcceptedBlockHeader(const CBlockIndex *pindexNew) { - Assert(m_llmq_ctx)->clhandler->AcceptedBlockHeader(pindexNew); + m_chainlocks.AcceptedBlockHeader(pindexNew); m_mn_sync.AcceptedBlockHeader(pindexNew); } @@ -74,7 +77,7 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con if (fInitialDownload) return; - m_dstxman.UpdatedBlockTip(pindexNew, *Assert(m_llmq_ctx)->clhandler, m_mn_sync); + m_dstxman.UpdatedBlockTip(pindexNew, m_chainlocks, m_mn_sync); m_llmq_ctx->isman->UpdatedBlockTip(pindexNew); m_llmq_ctx->clhandler->UpdatedBlockTip(*m_llmq_ctx->isman); @@ -124,7 +127,7 @@ void CDSNotificationInterface::NotifyChainLock(const CBlockIndex* pindex, const std::shared_ptr& clsig) { Assert(m_llmq_ctx)->isman->NotifyChainLock(pindex); - m_dstxman.NotifyChainLock(pindex, *m_llmq_ctx->clhandler, m_mn_sync); + m_dstxman.NotifyChainLock(pindex, m_chainlocks, m_mn_sync); } std::unique_ptr g_ds_notification_interface; diff --git a/src/dsnotificationinterface.h b/src/dsnotificationinterface.h index f8ae3b02560e..c540a329961a 100644 --- a/src/dsnotificationinterface.h +++ b/src/dsnotificationinterface.h @@ -15,6 +15,11 @@ class ChainstateManager; class CMasternodeSync; struct LLMQContext; +namespace chainlock +{ +class Chainlocks; +} // namespace chainlock + class CDSNotificationInterface : public CValidationInterface { public: @@ -27,7 +32,8 @@ class CDSNotificationInterface : public CValidationInterface CGovernanceManager& govman, const ChainstateManager& chainman, const std::unique_ptr& dmnman, - const std::unique_ptr& llmq_ctx); + const std::unique_ptr& llmq_ctx, + chainlock::Chainlocks& chainlocks); virtual ~CDSNotificationInterface(); // a small helper to initialize current block height in sub-modules on startup @@ -55,6 +61,7 @@ class CDSNotificationInterface : public CValidationInterface const ChainstateManager& m_chainman; const std::unique_ptr& m_dmnman; const std::unique_ptr& m_llmq_ctx; + chainlock::Chainlocks& m_chainlocks; }; extern std::unique_ptr g_ds_notification_interface; diff --git a/src/evo/chainhelper.cpp b/src/evo/chainhelper.cpp index 9c11fc4e6a3c..9850f2f91ada 100644 --- a/src/evo/chainhelper.cpp +++ b/src/evo/chainhelper.cpp @@ -17,12 +17,12 @@ CChainstateHelper::CChainstateHelper(CCreditPoolManager& cpoolman, CDeterministi llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, const Consensus::Params& consensus_params, const CMasternodeSync& mn_sync, const CSporkManager& sporkman, - const llmq::CChainLocksHandler& clhandler, const llmq::CQuorumManager& qman) : + const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman) : isman{isman}, - clhandler{clhandler}, + m_chainlocks{chainlocks}, mn_payments{std::make_unique(dmnman, govman, chainman, consensus_params, mn_sync, sporkman)}, special_tx{std::make_unique(cpoolman, dmnman, mnhfman, qblockman, qsnapman, chainman, - consensus_params, clhandler, qman)} + consensus_params, chainlocks, qman)} {} CChainstateHelper::~CChainstateHelper() = default; @@ -30,15 +30,15 @@ CChainstateHelper::~CChainstateHelper() = default; /** Passthrough functions to CChainLocksHandler */ bool CChainstateHelper::HasConflictingChainLock(int nHeight, const uint256& blockHash) const { - return clhandler.HasConflictingChainLock(nHeight, blockHash); + return m_chainlocks.HasConflictingChainLock(nHeight, blockHash); } bool CChainstateHelper::HasChainLock(int nHeight, const uint256& blockHash) const { - return clhandler.HasChainLock(nHeight, blockHash); + return m_chainlocks.HasChainLock(nHeight, blockHash); } -int32_t CChainstateHelper::GetBestChainLockHeight() const { return clhandler.GetBestChainLockHeight(); } +int32_t CChainstateHelper::GetBestChainLockHeight() const { return m_chainlocks.GetBestChainLockHeight(); } /** Passthrough functions to CInstantSendManager */ std::optional> CChainstateHelper::ConflictingISLockIfAny( diff --git a/src/evo/chainhelper.h b/src/evo/chainhelper.h index 66bee994652f..117448542c8d 100644 --- a/src/evo/chainhelper.h +++ b/src/evo/chainhelper.h @@ -20,9 +20,9 @@ class CSporkManager; class CTransaction; class uint256; +namespace chainlock { class Chainlocks; } namespace Consensus { struct Params; } namespace llmq { -class CChainLocksHandler; class CInstantSendManager; class CQuorumBlockProcessor; class CQuorumManager; @@ -33,7 +33,9 @@ class CChainstateHelper { private: llmq::CInstantSendManager& isman; - const llmq::CChainLocksHandler& clhandler; + +public: + const chainlock::Chainlocks& m_chainlocks; public: CChainstateHelper() = delete; @@ -44,10 +46,10 @@ class CChainstateHelper llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, const Consensus::Params& consensus_params, const CMasternodeSync& mn_sync, const CSporkManager& sporkman, - const llmq::CChainLocksHandler& clhandler, const llmq::CQuorumManager& qman); + const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman); ~CChainstateHelper(); - /** Passthrough functions to CChainLocksHandler */ + /** Passthrough functions to chainlock::Chainlocks*/ bool HasConflictingChainLock(int nHeight, const uint256& blockHash) const; bool HasChainLock(int nHeight, const uint256& blockHash) const; int32_t GetBestChainLockHeight() const; diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 3cdb9e7e79d8..9e630ee91382 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -28,7 +28,9 @@ #include static bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex, - const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state) + const Consensus::Params& m_consensus_params, + const CChain& chain, const llmq::CQuorumManager& qman, + const chainlock::Chainlocks& chainlocks, BlockValidationState& state) { if (cbTx.nVersion < CCbTx::Version::CLSIG_AND_BALANCE) { return true; @@ -38,7 +40,7 @@ static bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex, static const CBlockIndex* cached_pindex GUARDED_BY(cached_mutex){nullptr}; static std::optional> cached_chainlock GUARDED_BY(cached_mutex){std::nullopt}; - auto best_clsig = chainlock_handler.GetBestChainLock(); + auto best_clsig = chainlocks.GetBestChainLock(); if (best_clsig.getHeight() == pindex->nHeight - 1 && cbTx.bestCLHeightDiff == 0 && cbTx.bestCLSignature == best_clsig.getSig()) { // matches our best clsig which still hold values for the previous block @@ -78,9 +80,14 @@ static bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex, return true; } uint256 curBlockCoinbaseCLBlockHash = pindex->GetAncestor(curBlockCoinbaseCLHeight)->GetBlockHash(); - if (chainlock_handler.VerifyChainLock( - chainlock::ChainLockSig(curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, cbTx.bestCLSignature)) != - llmq::VerifyRecSigStatus::Valid) { + // TODO: remove duplicated code with CChainLocksHandler::VerifyChainLock + const auto llmqType = m_consensus_params.llmqTypeChainLocks; + chainlock::ChainLockSig clsig{curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, cbTx.bestCLSignature}; + const uint256 nRequestId = chainlock::GenSigRequestId(clsig.getHeight()); + + llmq::VerifyRecSigStatus ret = llmq::VerifyRecoveredSig(llmqType, chain, qman, clsig.getHeight(), nRequestId, + clsig.getBlockHash(), clsig.getSig()); + if (ret != llmq::VerifyRecSigStatus::Valid) { return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid-clsig"); } LOCK(cached_mutex); @@ -657,7 +664,7 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(const CBlock& block, const CB LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootQuorums: %.2fms [%.2fs]\n", 0.001 * (nTime6_2 - nTime6_1), nTimeMerkleQuorums * 0.000001); - if (!CheckCbTxBestChainlock(*opt_cbTx, pindex, m_clhandler, state)) { + if (!CheckCbTxBestChainlock(*opt_cbTx, pindex, m_consensus_params, m_chainman.ActiveChain(), m_qman, m_chainlocks, state)) { // pass the state returned by the function above return false; } diff --git a/src/evo/specialtxman.h b/src/evo/specialtxman.h index 4980c11324aa..f76f2a26dee4 100644 --- a/src/evo/specialtxman.h +++ b/src/evo/specialtxman.h @@ -25,9 +25,9 @@ class CMNHFManager; class TxValidationState; struct MNListUpdates; +namespace chainlock { class Chainlocks; } namespace Consensus { struct Params; } namespace llmq { -class CChainLocksHandler; class CQuorumBlockProcessor; class CQuorumManager; class CQuorumSnapshotManager; @@ -45,14 +45,14 @@ class CSpecialTxProcessor llmq::CQuorumSnapshotManager& m_qsnapman; const ChainstateManager& m_chainman; const Consensus::Params& m_consensus_params; - const llmq::CChainLocksHandler& m_clhandler; + const chainlock::Chainlocks& m_chainlocks; const llmq::CQuorumManager& m_qman; public: explicit CSpecialTxProcessor(CCreditPoolManager& cpoolman, CDeterministicMNManager& dmnman, CMNHFManager& mnhfman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, const Consensus::Params& consensus_params, - const llmq::CChainLocksHandler& clhandler, const llmq::CQuorumManager& qman) : + const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman) : m_cpoolman(cpoolman), m_dmnman{dmnman}, m_mnhfman{mnhfman}, @@ -60,7 +60,7 @@ class CSpecialTxProcessor m_qsnapman{qsnapman}, m_chainman(chainman), m_consensus_params{consensus_params}, - m_clhandler{clhandler}, + m_chainlocks{chainlocks}, m_qman{qman} { } diff --git a/src/init.cpp b/src/init.cpp index f97d98f8a766..07134f91ebfa 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1692,6 +1693,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) assert(!node.sporkman); node.sporkman = std::make_unique(); + node.chainlocks = std::make_unique(*node.sporkman); std::vector vSporkAddresses; if (args.IsArgSet("-sporkaddr")) { @@ -1989,6 +1991,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) *node.mn_metaman, *node.mn_sync, *node.sporkman, + *node.chainlocks, node.chain_helper, node.cpoolman, node.dmnman, @@ -2168,12 +2171,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) assert(!node.peerman); node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(), *node.dstxman, chainman, *node.mempool, *node.mn_metaman, *node.mn_sync, - *node.govman, *node.sporkman, node.active_ctx, node.dmnman, + *node.govman, *node.sporkman, *node.chainlocks, node.active_ctx, node.dmnman, node.cj_walletman, node.llmq_ctx, node.observer_ctx, ignores_incoming_txs); RegisterValidationInterface(node.peerman.get()); g_ds_notification_interface = std::make_unique( - *node.connman, *node.dstxman, *node.mn_sync, *node.govman, chainman, node.dmnman, node.llmq_ctx + *node.connman, *node.dstxman, *node.mn_sync, *node.govman, chainman, node.dmnman, node.llmq_ctx, *node.chainlocks ); RegisterValidationInterface(g_ds_notification_interface.get()); @@ -2192,7 +2195,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } // Will init later in ThreadImport node.active_ctx = std::make_unique(*node.llmq_ctx->bls_worker, chainman, *node.connman, *node.dmnman, *node.govman, *node.mn_metaman, - *node.mnhf_manager, *node.sporkman, *node.mempool, *node.llmq_ctx->clhandler, *node.llmq_ctx->isman, + *node.mnhf_manager, *node.sporkman, *node.chainlocks, *node.mempool, *node.llmq_ctx->clhandler, *node.llmq_ctx->isman, *node.llmq_ctx->quorum_block_processor, *node.llmq_ctx->qman, *node.llmq_ctx->qsnapman, *node.llmq_ctx->sigman, *node.peerman, *node.mn_sync, operator_sk, sync_map, dash_db_params, quorums_recovery, quorums_watch); RegisterValidationInterface(node.active_ctx.get()); diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 495eefd68119..7a28b7f0c376 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -48,11 +48,11 @@ Uint256HashSet GetIdsFromLockable(const std::vector& vec) } } // anonymous namespace -CInstantSendManager::CInstantSendManager(CChainLocksHandler& _clhandler, CChainState& chainstate, +CInstantSendManager::CInstantSendManager(const chainlock::Chainlocks& chainlocks, CChainState& chainstate, CSigningManager& _sigman, CSporkManager& sporkman, CTxMemPool& _mempool, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params) : db{db_params}, - clhandler{_clhandler}, + m_chainlocks{chainlocks}, m_chainstate{chainstate}, sigman{_sigman}, spork_manager{sporkman}, @@ -158,7 +158,7 @@ std::variant CInstantSendManager::Proc } // Let's see if the TX that was locked by this islock is already mined in a ChainLocked block. If yes, // we can simply ignore the islock, as the ChainLock implies locking of all TXs in that chain - if (minedHeight.has_value() && clhandler.HasChainLock(*minedHeight, hashBlock)) { + if (minedHeight.has_value() && m_chainlocks.HasChainLock(*minedHeight, hashBlock)) { LogPrint(BCLog::INSTANTSEND, /* Continued */ "CInstantSendManager::%s -- txlock=%s, islock=%s: dropping islock as it already got a " "ChainLock in block %s, peer=%d\n", @@ -261,7 +261,7 @@ void CInstantSendManager::BlockConnected(const std::shared_ptr& pb CacheTipHeight(pindex); if (m_mn_sync.IsBlockchainSynced()) { - const bool has_chainlock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); + const bool has_chainlock = m_chainlocks.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); for (const auto& tx : pblock->vtx) { if (tx->IsCoinBase() || tx->vin.empty()) { // coinbase and TXs with no inputs can't be locked @@ -437,7 +437,7 @@ void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew) bool fDIP0008Active = pindexNew->pprev && pindexNew->pprev->nHeight >= Params().GetConsensus().DIP0008Height; - if (AreChainLocksEnabled(spork_manager) && fDIP0008Active) { + if (m_chainlocks.IsEnabled() && fDIP0008Active) { // Nothing to do here. We should keep all islocks and let chainlocks handle them. return; } @@ -554,7 +554,7 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const bool hasChainLockedConflict = false; for (const auto& p : conflicts) { const auto* pindex = p.first; - if (clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash())) { + if (m_chainlocks.HasChainLock(pindex->nHeight, pindex->GetBlockHash())) { hasChainLockedConflict = true; break; } diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index 22421c8fc34f..8c64d6e784b4 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -37,6 +37,8 @@ namespace util { struct DbWrapperParams; } // namespace util +namespace chainlock { class Chainlocks; } + namespace instantsend { class InstantSendSigner; @@ -52,7 +54,6 @@ struct PendingState { } // namespace instantsend namespace llmq { -class CChainLocksHandler; class CSigningManager; class CInstantSendManager final : public instantsend::InstantSendSignerParent @@ -60,7 +61,7 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent private: instantsend::CInstantSendDb db; - CChainLocksHandler& clhandler; + const chainlock::Chainlocks& m_chainlocks; CChainState& m_chainstate; CSigningManager& sigman; CSporkManager& spork_manager; @@ -105,7 +106,7 @@ class CInstantSendManager final : public instantsend::InstantSendSignerParent CInstantSendManager() = delete; CInstantSendManager(const CInstantSendManager&) = delete; CInstantSendManager& operator=(const CInstantSendManager&) = delete; - explicit CInstantSendManager(CChainLocksHandler& _clhandler, CChainState& chainstate, CSigningManager& _sigman, + explicit CInstantSendManager(const chainlock::Chainlocks& chainlocks, CChainState& chainstate, CSigningManager& _sigman, CSporkManager& sporkman, CTxMemPool& _mempool, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params); ~CInstantSendManager(); diff --git a/src/instantsend/signing.cpp b/src/instantsend/signing.cpp index 783b5757ca22..40dcc58d7589 100644 --- a/src/instantsend/signing.cpp +++ b/src/instantsend/signing.cpp @@ -28,12 +28,12 @@ using node::fReindex; using node::GetTransaction; namespace instantsend { -InstantSendSigner::InstantSendSigner(CChainState& chainstate, llmq::CChainLocksHandler& clhandler, +InstantSendSigner::InstantSendSigner(CChainState& chainstate, const chainlock::Chainlocks& chainlocks, InstantSendSignerParent& isman, llmq::CSigningManager& sigman, llmq::CSigSharesManager& shareman, llmq::CQuorumManager& qman, CSporkManager& sporkman, CTxMemPool& mempool, const CMasternodeSync& mn_sync) : m_chainstate{chainstate}, - m_clhandler{clhandler}, + m_chainlocks{chainlocks}, m_isman{isman}, m_sigman{sigman}, m_shareman{shareman}, @@ -225,7 +225,7 @@ bool InstantSendSigner::CheckCanLock(const COutPoint& outpoint, bool printDebug, const int nTxAge = tipHeight - *blockHeight + 1; - if (nTxAge < nInstantSendConfirmationsRequired && !m_clhandler.HasChainLock(*blockHeight, hashBlock)) { + if (nTxAge < nInstantSendConfirmationsRequired && !m_chainlocks.HasChainLock(*blockHeight, hashBlock)) { if (printDebug) { LogPrint(BCLog::INSTANTSEND, "%s -- txid=%s: outpoint %s too new and not ChainLocked. nTxAge=%d, nInstantSendConfirmationsRequired=%d\n", __func__, txHash.ToString(), outpoint.ToStringShort(), nTxAge, nInstantSendConfirmationsRequired); diff --git a/src/instantsend/signing.h b/src/instantsend/signing.h index d954daaae535..0de2d10c240c 100644 --- a/src/instantsend/signing.h +++ b/src/instantsend/signing.h @@ -18,8 +18,8 @@ struct MessageProcessingResult; namespace Consensus { struct Params; } // namespace Consensus +namespace chainlock { class Chainlocks; } namespace llmq { -class CChainLocksHandler; class CInstantSendManager; class CSigningManager; class CSigSharesManager; @@ -44,7 +44,7 @@ class InstantSendSigner final : public llmq::CRecoveredSigsListener { private: CChainState& m_chainstate; - llmq::CChainLocksHandler& m_clhandler; + const chainlock::Chainlocks& m_chainlocks; InstantSendSignerParent& m_isman; llmq::CSigningManager& m_sigman; llmq::CSigSharesManager& m_shareman; @@ -76,7 +76,7 @@ class InstantSendSigner final : public llmq::CRecoveredSigsListener InstantSendSigner() = delete; InstantSendSigner(const InstantSendSigner&) = delete; InstantSendSigner& operator=(const InstantSendSigner&) = delete; - explicit InstantSendSigner(CChainState& chainstate, llmq::CChainLocksHandler& clhandler, + explicit InstantSendSigner(CChainState& chainstate, const chainlock::Chainlocks& chainlocks, InstantSendSignerParent& isman, llmq::CSigningManager& sigman, llmq::CSigSharesManager& shareman, llmq::CQuorumManager& qman, CSporkManager& sporkman, CTxMemPool& mempool, const CMasternodeSync& mn_sync); diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index dd3c4a4b3a3d..03946f785aca 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -5,7 +5,8 @@ #include #include -#include +#include +#include #include #include #include @@ -13,7 +14,7 @@ #include #include -LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, CTxMemPool& mempool, +LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, const ChainstateManager& chainman, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params, int8_t bls_threads, int64_t max_recsigs_age) : bls_worker{std::make_shared()}, @@ -23,8 +24,9 @@ LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSpork qman{std::make_unique(*bls_worker, dmnman, evo_db, *quorum_block_processor, *qsnapman, chainman, db_params)}, sigman{std::make_unique(*qman, db_params, max_recsigs_age)}, - clhandler{std::make_unique(chainman.ActiveChainstate(), *qman, sporkman, mempool, mn_sync)}, - isman{std::make_unique(*clhandler, chainman.ActiveChainstate(), *sigman, sporkman, + // TODO: move-out clhandler from LLMQContext so far as it has nothing to do with llmq's context + clhandler{std::make_unique(chainlocks, chainman.ActiveChainstate(), *qman, mempool, mn_sync)}, + isman{std::make_unique(chainlocks, chainman.ActiveChainstate(), *sigman, sporkman, mempool, mn_sync, db_params)} { // Have to start it early to let VerifyDB check ChainLock signatures in coinbase diff --git a/src/llmq/context.h b/src/llmq/context.h index a3bae616a319..ae92ad3cb0c7 100644 --- a/src/llmq/context.h +++ b/src/llmq/context.h @@ -19,6 +19,8 @@ class CSporkManager; class CTxMemPool; class PeerManager; +namespace chainlock { class Chainlocks; } + namespace llmq { class CChainLocksHandler; class CInstantSendManager; @@ -36,7 +38,7 @@ struct LLMQContext { LLMQContext() = delete; LLMQContext(const LLMQContext&) = delete; LLMQContext& operator=(const LLMQContext&) = delete; - explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, CTxMemPool& mempool, + explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, const ChainstateManager& chainman, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params, int8_t bls_threads, int64_t max_recsigs_age); ~LLMQContext(); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index e5b83dc0f88f..61f7a09dd6a2 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -586,7 +587,7 @@ class PeerManagerImpl final : public PeerManager PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman, BanMan* banman, CDSTXManager& dstxman, ChainstateManager& chainman, CTxMemPool& pool, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CGovernanceManager& govman, - CSporkManager& sporkman, + CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, const std::unique_ptr& active_ctx, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, @@ -820,6 +821,7 @@ class PeerManagerImpl final : public PeerManager CMasternodeSync& m_mn_sync; CGovernanceManager& m_govman; CSporkManager& m_sporkman; + const chainlock::Chainlocks& m_chainlocks; /** The height of the best chain */ std::atomic m_best_height{-1}; @@ -2042,20 +2044,21 @@ std::unique_ptr PeerManager::make(const CChainParams& chainparams, BanMan* banman, CDSTXManager& dstxman, ChainstateManager& chainman, CTxMemPool& pool, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CGovernanceManager& govman, - CSporkManager& sporkman, + CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, const std::unique_ptr& active_ctx, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, const std::unique_ptr& llmq_ctx, const std::unique_ptr& observer_ctx, bool ignore_incoming_txs) { - return std::make_unique(chainparams, connman, addrman, banman, dstxman, chainman, pool, mn_metaman, mn_sync, govman, sporkman, active_ctx, dmnman, cj_walletman, llmq_ctx, observer_ctx, ignore_incoming_txs); + return std::make_unique(chainparams, connman, addrman, banman, dstxman, chainman, pool, mn_metaman, mn_sync, govman, sporkman, chainlocks, active_ctx, dmnman, cj_walletman, llmq_ctx, observer_ctx, ignore_incoming_txs); } PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman, BanMan* banman, CDSTXManager& dstxman, ChainstateManager& chainman, CTxMemPool& pool, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CGovernanceManager& govman, CSporkManager& sporkman, + const chainlock::Chainlocks& chainlocks, const std::unique_ptr& active_ctx, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, @@ -2077,6 +2080,7 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn m_mn_sync(mn_sync), m_govman(govman), m_sporkman(sporkman), + m_chainlocks(chainlocks), m_ignore_incoming_txs(ignore_incoming_txs) { // While Erlay support is incomplete, it must be enabled explicitly via -txreconciliation. @@ -2971,7 +2975,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (!push && (inv.type == MSG_CLSIG)) { chainlock::ChainLockSig o; - if (m_llmq_ctx->clhandler->GetChainLockByHash(inv.hash, o)) { + if (m_chainlocks.GetChainLockByHash(inv.hash, o)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::CLSIG, o)); push = true; } @@ -5479,7 +5483,7 @@ void PeerManagerImpl::ProcessMessage( PostProcessMessage(ProcessPlatformBanMessage(pfrom.GetId(), msg_type, vRecv), pfrom.GetId()); if (msg_type == NetMsgType::CLSIG) { - if (llmq::AreChainLocksEnabled(m_sporkman)) { + if (m_chainlocks.IsEnabled()) { chainlock::ChainLockSig clsig; vRecv >> clsig; const uint256& hash = ::SerializeHash(clsig); @@ -6313,7 +6317,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) } // Send an inv for the best ChainLock we have - const auto& clsig = m_llmq_ctx->clhandler->GetBestChainLock(); + const auto& clsig = m_chainlocks.GetBestChainLock(); if (!clsig.IsNull()) { uint256 chainlockHash{::SerializeHash(clsig)}; tx_relay->m_tx_inventory_known_filter.insert(chainlockHash); diff --git a/src/net_processing.h b/src/net_processing.h index 54795ab803c0..e2e4d72fa0e4 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -34,6 +34,7 @@ struct LLMQContext; namespace llmq { struct ObserverContext; } // namespace llmq +namespace chainlock { class Chainlocks; } /** Default for -maxorphantxsize, maximum size in megabytes the orphan map can grow before entries are removed */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE = 10; // this allows around 100 TXs of max size (and many more of normal size) @@ -106,6 +107,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface, publ BanMan* banman, CDSTXManager& dstxman, ChainstateManager& chainman, CTxMemPool& pool, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CGovernanceManager& govman, CSporkManager& sporkman, + const chainlock::Chainlocks& chainlocks, const std::unique_ptr& active_ctx, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index e60eaace5630..602b673941a0 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -40,6 +40,7 @@ std::optional LoadChainstate(bool fReset, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CSporkManager& sporkman, + chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, std::unique_ptr& cpoolman, std::unique_ptr& dmnman, @@ -87,7 +88,7 @@ std::optional LoadChainstate(bool fReset, pblocktree.reset(); pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, block_tree_db_in_memory, fReset)); - DashChainstateSetup(chainman, govman, mn_metaman, mn_sync, sporkman, chain_helper, cpoolman, + DashChainstateSetup(chainman, govman, mn_metaman, mn_sync, sporkman, chainlocks, chain_helper, cpoolman, dmnman, evodb, mnhf_manager, llmq_ctx, mempool, data_dir, dash_dbs_in_memory, /*llmq_dbs_wipe=*/fReset || fReindexChainState, bls_threads, max_recsigs_age, consensus_params); @@ -214,6 +215,7 @@ void DashChainstateSetup(ChainstateManager& chainman, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CSporkManager& sporkman, + chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, std::unique_ptr& cpoolman, std::unique_ptr& dmnman, @@ -239,7 +241,7 @@ void DashChainstateSetup(ChainstateManager& chainman, llmq_ctx->Stop(); } llmq_ctx.reset(); - llmq_ctx = std::make_unique(*dmnman, *evodb, sporkman, *mempool, chainman, mn_sync, + llmq_ctx = std::make_unique(*dmnman, *evodb, sporkman, chainlocks, *mempool, chainman, mn_sync, util::DbWrapperParams{.path = data_dir, .memory = llmq_dbs_in_memory, .wipe = llmq_dbs_wipe}, bls_threads, max_recsigs_age); mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); @@ -248,7 +250,7 @@ void DashChainstateSetup(ChainstateManager& chainman, chain_helper.reset(); chain_helper = std::make_unique(*cpoolman, *dmnman, *mnhf_manager, govman, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), - *(llmq_ctx->qsnapman), chainman, consensus_params, mn_sync, sporkman, *(llmq_ctx->clhandler), + *(llmq_ctx->qsnapman), chainman, consensus_params, mn_sync, sporkman, chainlocks, *(llmq_ctx->qman)); } diff --git a/src/node/chainstate.h b/src/node/chainstate.h index 0022b7637d87..4bd42375f51f 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -25,6 +25,7 @@ class CSporkManager; class CTxMemPool; struct LLMQContext; +namespace chainlock { class Chainlocks; } namespace Consensus { struct Params; } // namespace Consensus @@ -84,6 +85,7 @@ std::optional LoadChainstate(bool fReset, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CSporkManager& sporkman, + chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, std::unique_ptr& cpoolman, std::unique_ptr& dmnman, @@ -115,6 +117,7 @@ void DashChainstateSetup(ChainstateManager& chainman, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CSporkManager& sporkman, + chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, std::unique_ptr& cpoolman, std::unique_ptr& dmnman, diff --git a/src/node/context.cpp b/src/node/context.cpp index f786b71d9aa3..4f9c485fc34a 100644 --- a/src/node/context.cpp +++ b/src/node/context.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/src/node/context.h b/src/node/context.h index f2d1bdbb9722..12ff8af84246 100644 --- a/src/node/context.h +++ b/src/node/context.h @@ -39,6 +39,8 @@ namespace llmq { struct ObserverContext; } // namespace llmq +namespace chainlock { class Chainlocks; } + namespace interfaces { class Chain; class ChainClient; @@ -94,6 +96,7 @@ struct NodeContext { std::unique_ptr mnhf_manager; std::unique_ptr netfulfilledman; std::unique_ptr sporkman; + std::unique_ptr chainlocks; //! Dash contexts std::unique_ptr active_ctx; std::unique_ptr llmq_ctx; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 88b7edf3bb56..cb3894db59db 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -1074,8 +1074,8 @@ class ChainImpl : public Chain } bool hasChainLock(int height, const uint256& hash) override { - if (m_node.llmq_ctx == nullptr || m_node.llmq_ctx->clhandler == nullptr) return false; - return m_node.llmq_ctx->clhandler->HasChainLock(height, hash); + if (m_node.chainlocks == nullptr) return false; + return m_node.chainlocks->HasChainLock(height, hash); } std::vector listMNCollaterials(const std::vector>& outputs) override { diff --git a/src/node/miner.cpp b/src/node/miner.cpp index a90b88ba3613..0cfae68f5389 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -76,6 +77,7 @@ BlockAssembler::BlockAssembler(CChainState& chainstate, const NodeContext& node, m_chainstate(chainstate), m_evoDb(*Assert(node.evodb)), m_mnhfman(*Assert(node.mnhf_manager)), + m_chainlocks(*Assert(node.chainlocks)), m_clhandler(*Assert(Assert(node.llmq_ctx)->clhandler)), m_isman(*Assert(Assert(node.llmq_ctx)->isman)), chainparams(params), @@ -121,10 +123,10 @@ void BlockAssembler::resetBlock() } // Helper to calculate best chainlock -static bool CalcCbTxBestChainlock(const llmq::CChainLocksHandler& chainlock_handler, const CBlockIndex* pindexPrev, +static bool CalcCbTxBestChainlock(const chainlock::Chainlocks& chainlocks, const CBlockIndex* pindexPrev, uint32_t& bestCLHeightDiff, CBLSSignature& bestCLSignature) { - auto best_clsig = chainlock_handler.GetBestChainLock(); + auto best_clsig = chainlocks.GetBestChainLock(); if (best_clsig.getHeight() < Params().GetConsensus().DeploymentHeight(Consensus::DEPLOYMENT_V19)) { // We don't want legacy BLS ChainLocks in CbTx (can happen on regtest/devenets) best_clsig = chainlock::ChainLockSig{}; @@ -299,7 +301,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc throw std::runtime_error(strprintf("%s: CalcCbTxMerkleRootQuorums failed: %s", __func__, state.ToString())); } if (fV20Active_context) { - if (CalcCbTxBestChainlock(m_clhandler, pindexPrev, cbTx.bestCLHeightDiff, cbTx.bestCLSignature)) { + if (CalcCbTxBestChainlock(m_chainlocks, pindexPrev, cbTx.bestCLHeightDiff, cbTx.bestCLSignature)) { LogPrintf("CreateNewBlock() h[%d] CbTx bestCLHeightDiff[%d] CLSig[%s]\n", nHeight, cbTx.bestCLHeightDiff, cbTx.bestCLSignature.ToString()); } else { // not an error @@ -334,7 +336,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(*pblock->vtx[0]); BlockValidationState state; - if (!TestBlockValidity(state, m_clhandler, m_evoDb, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) { + if (!TestBlockValidity(state, m_chainlocks, m_evoDb, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) { throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString())); } int64_t nTime2 = GetTimeMicros(); diff --git a/src/node/miner.h b/src/node/miner.h index eea4d69884bd..9d022fe78591 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -29,6 +29,7 @@ class CMNHFManager; class CScript; struct LLMQContext; +namespace chainlock { class Chainlocks; } namespace Consensus { struct Params; }; namespace llmq { class CChainLocksHandler; @@ -172,6 +173,7 @@ class BlockAssembler CChainState& m_chainstate; CEvoDB& m_evoDb; CMNHFManager& m_mnhfman; + const chainlock::Chainlocks& m_chainlocks; llmq::CChainLocksHandler& m_clhandler; llmq::CInstantSendManager& m_isman; const CChainParams& chainparams; diff --git a/src/rest.cpp b/src/rest.cpp index 68f5fc79425d..207a2f247400 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -282,12 +282,12 @@ static bool rest_headers(const CoreContext& context, return true; } case RESTResponseFormat::JSON: { - const LLMQContext* llmq_ctx = GetLLMQContext(context, req); - if (!llmq_ctx) return false; + const NodeContext* const node = GetNodeContext(context, req); + if (!node || !node->chainlocks) return false; UniValue jsonHeaders(UniValue::VARR); for (const CBlockIndex *pindex : headers) { - jsonHeaders.push_back(blockheaderToJSON(tip, pindex, *llmq_ctx->clhandler)); + jsonHeaders.push_back(blockheaderToJSON(tip, pindex, *node->chainlocks)); } std::string strJSON = jsonHeaders.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); @@ -356,10 +356,13 @@ static bool rest_block(const CoreContext& context, } case RESTResponseFormat::JSON: { + const NodeContext* const node = GetNodeContext(context, req); + if (!node || !node->chainlocks) return false; + const LLMQContext* llmq_ctx = GetLLMQContext(context, req); if (!llmq_ctx) return false; - UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip, pblockindex, *llmq_ctx->clhandler, *llmq_ctx->isman, tx_verbosity); + UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip, pblockindex, *node->chainlocks, *llmq_ctx->isman, tx_verbosity); std::string strJSON = objBlock.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 203e20da3ba2..ab74d75431b9 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -82,7 +82,7 @@ static GlobalMutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange); -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, const CTxMemPool& mempool, const CChainState& active_chainstate, const llmq::CChainLocksHandler& clhandler, const llmq::CInstantSendManager& isman, UniValue& entry, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS); +extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, const CTxMemPool& mempool, const CChainState& active_chainstate, const chainlock::Chainlocks& chainlocks, const llmq::CInstantSendManager& isman, UniValue& entry, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS); /* Calculate the difficulty for a given block index. */ @@ -131,7 +131,7 @@ static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateMan } } -UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex, const llmq::CChainLocksHandler& clhandler) +UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex, const chainlock::Chainlocks& chainlocks) { // Serialize passed information without accessing chain state of the active chain! AssertLockNotHeld(cs_main); // For performance reasons @@ -158,14 +158,14 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex if (pnext) result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); - result.pushKV("chainlock", clhandler.HasChainLock(blockindex->nHeight, blockindex->GetBlockHash())); + result.pushKV("chainlock", chainlocks.HasChainLock(blockindex->nHeight, blockindex->GetBlockHash())); return result; } -UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, const llmq::CChainLocksHandler& clhandler, const llmq::CInstantSendManager& isman, TxVerbosity verbosity) +UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, const chainlock::Chainlocks& chainlocks, const llmq::CInstantSendManager& isman, TxVerbosity verbosity) { - UniValue result = blockheaderToJSON(tip, blockindex, clhandler); + UniValue result = blockheaderToJSON(tip, blockindex, chainlocks); result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION)); UniValue txs(UniValue::VARR); @@ -268,8 +268,8 @@ static RPCHelpMan getbestchainlock() { const NodeContext& node = EnsureAnyNodeContext(request.context); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - const chainlock::ChainLockSig clsig = llmq_ctx.clhandler->GetBestChainLock(); + CHECK_NONFATAL(node.chainlocks); + const chainlock::ChainLockSig clsig = node.chainlocks->GetBestChainLock(); if (clsig.IsNull()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to find any ChainLock"); } @@ -667,8 +667,8 @@ static RPCHelpMan getblockheader() return strHex; } - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - return blockheaderToJSON(tip, pblockindex, *llmq_ctx.clhandler); + CHECK_NONFATAL(node.chainlocks); + return blockheaderToJSON(tip, pblockindex, *node.chainlocks); }, }; } @@ -766,10 +766,10 @@ static RPCHelpMan getblockheaders() return arrHeaders; } - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + CHECK_NONFATAL(node.chainlocks); for (; pblockindex; pblockindex = active_chain.Next(pblockindex)) { - arrHeaders.push_back(blockheaderToJSON(tip, pblockindex, *llmq_ctx.clhandler)); + arrHeaders.push_back(blockheaderToJSON(tip, pblockindex, *node.chainlocks)); if (--nCount <= 0) break; } @@ -1026,6 +1026,7 @@ static RPCHelpMan getblock() } const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + CHECK_NONFATAL(node.chainlocks); TxVerbosity tx_verbosity; if (verbosity == 1) { tx_verbosity = TxVerbosity::SHOW_TXID; @@ -1035,7 +1036,7 @@ static RPCHelpMan getblock() tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT; } - return blockToJSON(chainman.m_blockman, block, tip, pblockindex, *llmq_ctx.clhandler, *llmq_ctx.isman, tx_verbosity); + return blockToJSON(chainman.m_blockman, block, tip, pblockindex, *node.chainlocks, *llmq_ctx.isman, tx_verbosity); }, }; } @@ -2290,6 +2291,7 @@ static RPCHelpMan getspecialtxes() const CTxMemPool& mempool = EnsureMemPool(node); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + CHECK_NONFATAL(node.chainlocks); const uint256 blockhash(ParseHashV(request.params[0], "blockhash")); @@ -2348,7 +2350,7 @@ static RPCHelpMan getspecialtxes() case 2 : { UniValue objTx(UniValue::VOBJ); - TxToJSON(*tx, blockhash, mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, objTx); + TxToJSON(*tx, blockhash, mempool, chainman.ActiveChainstate(), *node.chainlocks, *llmq_ctx.isman, objTx); result.push_back(objTx); break; } diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index f84cb6794fbe..7c2051bd1d2e 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -23,11 +23,11 @@ class CBlock; class CBlockIndex; class CChainState; class CCoinsView; +namespace chainlock { class Chainlocks; } namespace kernel { enum class CoinStatsHashType : uint8_t; } namespace llmq { -class CChainLocksHandler; class CInstantSendManager; } // namespace llmq namespace node { @@ -51,10 +51,10 @@ double GetDifficulty(const CBlockIndex* blockindex); void RPCNotifyBlockChange(const CBlockIndex*); /** Block description to JSON */ -UniValue blockToJSON(node::BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, const llmq::CChainLocksHandler& clhandler, const llmq::CInstantSendManager& isman, TxVerbosity verbosity) LOCKS_EXCLUDED(cs_main); +UniValue blockToJSON(node::BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, const chainlock::Chainlocks& chainlocks, const llmq::CInstantSendManager& isman, TxVerbosity verbosity) LOCKS_EXCLUDED(cs_main); /** Block header to JSON */ -UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex, const llmq::CChainLocksHandler& clhandler) LOCKS_EXCLUDED(cs_main); +UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex, const chainlock::Chainlocks& chainlocks) LOCKS_EXCLUDED(cs_main); /** Used by getblockstats to get feerates at different percentiles by weight */ void CalculatePercentilesBySize(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector>& scores, int64_t total_size); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index cb39dae14489..c05badf975b8 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -5,7 +5,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include #include #include #include @@ -357,7 +356,6 @@ static RPCHelpMan generateblock() } const CChainParams& chainparams(Params()); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); ChainstateManager& chainman = EnsureChainman(node); CChainState& active_chainstate = chainman.ActiveChainstate(); @@ -383,7 +381,7 @@ static RPCHelpMan generateblock() LOCK(cs_main); BlockValidationState state; - if (!TestBlockValidity(state, *llmq_ctx.clhandler, *CHECK_NONFATAL(node.evodb), chainparams, active_chainstate, + if (!TestBlockValidity(state, *CHECK_NONFATAL(node.chainlocks), *CHECK_NONFATAL(node.evodb), chainparams, active_chainstate, block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) { throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.GetRejectReason())); } @@ -699,14 +697,12 @@ static RPCHelpMan getblocktemplate() return "duplicate-inconclusive"; } - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - CBlockIndex* const pindexPrev = active_chain.Tip(); // TestBlockValidity only supports blocks built on the current Tip if (block.hashPrevBlock != pindexPrev->GetBlockHash()) return "inconclusive-not-best-prevblk"; BlockValidationState state; - TestBlockValidity(state, *llmq_ctx.clhandler, *CHECK_NONFATAL(node.evodb), Params(), active_chainstate, + TestBlockValidity(state, *CHECK_NONFATAL(node.chainlocks), *CHECK_NONFATAL(node.evodb), Params(), active_chainstate, block, pindexPrev, false, true); return BIP22ValidationResult(state); } diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index f82ec32def8d..a223ba06c707 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -1217,7 +1218,8 @@ static RPCHelpMan submitchainlock() } const NodeContext& node = EnsureAnyNodeContext(request.context); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - const int32_t bestCLHeight = llmq_ctx.clhandler->GetBestChainLock().getHeight(); + CHECK_NONFATAL(node.chainlocks); + const int32_t bestCLHeight = node.chainlocks->GetBestChainLock().getHeight(); if (nBlockHeight <= bestCLHeight) return bestCLHeight; CBLSSignature sig; @@ -1240,7 +1242,7 @@ static RPCHelpMan submitchainlock() PeerManager& peerman = EnsurePeerman(node); peerman.PostProcessMessage(llmq_ctx.clhandler->ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig))); - return llmq_ctx.clhandler->GetBestChainLock().getHeight(); + return node.chainlocks->GetBestChainLock().getHeight(); }, }; } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 5db203c01832..554f4bd3d53e 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -69,7 +69,7 @@ using node::GetTransaction; using node::NodeContext; using node::PSBTAnalysis; -void TxToJSON(const CTransaction& tx, const uint256 hashBlock, const CTxMemPool& mempool, const CChainState& active_chainstate, const llmq::CChainLocksHandler& clhandler, const llmq::CInstantSendManager& isman, UniValue& entry, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS) +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, const CTxMemPool& mempool, const CChainState& active_chainstate, const chainlock::Chainlocks& chainlocks, const llmq::CInstantSendManager& isman, UniValue& entry, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS) { CHECK_NONFATAL(verbosity >= TxVerbosity::SHOW_DETAILS); LOCK(::cs_main); @@ -117,7 +117,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, const CTxMemPool entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight); entry.pushKV("time", pindex->GetBlockTime()); entry.pushKV("blocktime", pindex->GetBlockTime()); - chainLock = clhandler.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); + chainLock = chainlocks.HasChainLock(pindex->nHeight, pindex->GetBlockHash()); } else { entry.pushKV("height", -1); entry.pushKV("confirmations", 0); @@ -329,10 +329,11 @@ static RPCHelpMan getrawtransaction() const LLMQContext& llmq_ctx = EnsureLLMQContext(node); const CTxMemPool& mempool = EnsureMemPool(node); + CHECK_NONFATAL(node.chainlocks); UniValue result(UniValue::VOBJ); if (blockindex) result.pushKV("in_active_chain", in_active_chain); - TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, result); + TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *node.chainlocks, *llmq_ctx.isman, result); return result; }, }; @@ -390,6 +391,7 @@ static RPCHelpMan getrawtransactionmulti() { const NodeContext& node{EnsureAnyNodeContext(request.context)}; const ChainstateManager& chainman{EnsureChainman(node)}; const LLMQContext& llmq_ctx{EnsureLLMQContext(node)}; + CHECK_NONFATAL(node.chainlocks); CTxMemPool& mempool{EnsureMemPool(node)}; if (transactions.size() > 100) { @@ -424,7 +426,7 @@ static RPCHelpMan getrawtransactionmulti() { result.pushKV(txid_str, "None"); } else if (fVerbose) { UniValue tx_data{UniValue::VOBJ}; - TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.isman, tx_data); + TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *node.chainlocks, *llmq_ctx.isman, tx_data); result.pushKV(txid_str, tx_data); } else { result.pushKV(txid_str, EncodeHexTx(*tx)); @@ -548,9 +550,9 @@ static RPCHelpMan gettxchainlocks() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { const NodeContext& node = EnsureAnyNodeContext(request.context); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); const ChainstateManager& chainman = EnsureChainman(node); const CChainState& active_chainstate = chainman.ActiveChainstate(); + CHECK_NONFATAL(node.chainlocks); UniValue result_arr(UniValue::VARR); UniValue txids = request.params[0].get_array(); @@ -592,7 +594,7 @@ static RPCHelpMan gettxchainlocks() } } if (height != -1) { - chainLock = llmq_ctx.clhandler->HasChainLock(height, hash_block); + chainLock = node.chainlocks->HasChainLock(height, hash_block); } result.pushKV("height", height); result.pushKV("chainlock", chainLock); @@ -635,7 +637,7 @@ static RPCHelpMan getassetunlockstatuses() { const NodeContext& node = EnsureAnyNodeContext(request.context); const CTxMemPool& mempool = EnsureMemPool(node); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + CHECK_NONFATAL(node.chainlocks); const ChainstateManager& chainman = EnsureChainman(node); UniValue result_arr(UniValue::VARR); @@ -667,7 +669,7 @@ static RPCHelpMan getassetunlockstatuses() } else { const auto pBlockIndexBestCL = [&]() -> const CBlockIndex* { - const auto best_clsig = llmq_ctx.clhandler->GetBestChainLock(); + const auto best_clsig = node.chainlocks->GetBestChainLock(); if (!best_clsig.IsNull()) { return pTipBlockIndex->GetAncestor(best_clsig.getHeight()); } diff --git a/src/test/coinjoin_inouts_tests.cpp b/src/test/coinjoin_inouts_tests.cpp index 94bbedc3caa8..520057f46f56 100644 --- a/src/test/coinjoin_inouts_tests.cpp +++ b/src/test/coinjoin_inouts_tests.cpp @@ -10,10 +10,9 @@ #include #include -#include #include #include -#include +#include #include