diff --git a/src/evo/chainhelper.cpp b/src/evo/chainhelper.cpp index 7bcd66d40c6e..8e92000851e1 100644 --- a/src/evo/chainhelper.cpp +++ b/src/evo/chainhelper.cpp @@ -22,18 +22,18 @@ CChainstateHelper::CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmnman, const CMasternodeSync& mn_sync, llmq::CInstantSendManager& isman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, - const Consensus::Params& consensus_params, const chainlock::Chainlocks& chainlocks, - const llmq::CQuorumManager& qman) : + const node::BlockManager& blockman, const Consensus::Params& consensus_params, + const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman) : isman{isman}, mn_sync{mn_sync}, m_dmnman{dmnman}, credit_pool_manager{std::make_unique(evodb, chainman)}, m_chainlocks{chainlocks}, - ehf_manager{std::make_unique(evodb, chainman)}, + ehf_manager{std::make_unique(evodb, consensus_params)}, superblocks{std::make_unique()}, mn_payments{std::make_unique(dmnman, *superblocks, consensus_params)}, special_tx{std::make_unique(*credit_pool_manager, dmnman, *ehf_manager, qblockman, qsnapman, - chainman, consensus_params, chainlocks, qman)} + chainman, blockman, consensus_params, chainlocks, qman)} {} CChainstateHelper::~CChainstateHelper() = default; diff --git a/src/evo/chainhelper.h b/src/evo/chainhelper.h index 0036475fd282..a501dd82ceaf 100644 --- a/src/evo/chainhelper.h +++ b/src/evo/chainhelper.h @@ -37,6 +37,9 @@ class CQuorumBlockProcessor; class CQuorumManager; class CQuorumSnapshotManager; } // namespace llmq +namespace node { +class BlockManager; +} // namespace node class CChainstateHelper { private: @@ -59,8 +62,8 @@ class CChainstateHelper explicit CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmnman, const CMasternodeSync& mn_sync, llmq::CInstantSendManager& isman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, - const Consensus::Params& consensus_params, const chainlock::Chainlocks& chainlocks, - const llmq::CQuorumManager& qman); + const node::BlockManager& blockman, const Consensus::Params& consensus_params, + const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman); ~CChainstateHelper(); bool IsSuperblockValidationRequired(const CBlockIndex* const pindex); diff --git a/src/evo/creditpool.h b/src/evo/creditpool.h index 610c193eac74..891449f24f1f 100644 --- a/src/evo/creditpool.h +++ b/src/evo/creditpool.h @@ -6,6 +6,7 @@ #define BITCOIN_EVO_CREDITPOOL_H #include +#include #include #include #include @@ -14,8 +15,6 @@ #include #include -#include - #include #include diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index d652efaec732..42ebacf76707 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include @@ -624,7 +623,7 @@ CDeterministicMNManager::~CDeterministicMNManager() = default; bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null pindex, BlockValidationState& state, const CDeterministicMNList& newList, - std::optional& updatesRet) + MNListUpdates& updatesRet) { AssertLockHeld(::cs_main); @@ -692,9 +691,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_nullactive()) { const auto counts{newList.GetCounts()}; @@ -723,7 +720,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null pindex, std::optional& updatesRet) +bool CDeterministicMNManager::UndoBlock(gsl::not_null pindex, MNListUpdates& updatesRet) { int nHeight = pindex->nHeight; uint256 blockHash = pindex->GetBlockHash(); diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 4fb5dee91aef..2fd5cf9433ee 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -15,7 +14,6 @@ #include #include #include -#include #include #include @@ -717,6 +715,10 @@ constexpr int llmq_max_blocks() { return max_blocks; } +/** Outcome of applying a block to the deterministic masternode list: the list + * before and after the block and the diff between them. A block that leaves + * the list untouched yields an empty diff (see diff.HasChanges()); for an + * undone block the lists are only filled when the diff has changes. */ struct MNListUpdates { CDeterministicMNList old_list; @@ -777,9 +779,9 @@ class CDeterministicMNManager ~CDeterministicMNManager(); bool ProcessBlock(const CBlock& block, gsl::not_null pindex, BlockValidationState& state, - const CDeterministicMNList& newList, std::optional& updatesRet) + const CDeterministicMNList& newList, MNListUpdates& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main); - bool UndoBlock(gsl::not_null pindex, std::optional& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(!cs); + bool UndoBlock(gsl::not_null pindex, MNListUpdates& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(!cs); void UpdatedBlockTip(gsl::not_null pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs); diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp index f36e9b0a80ca..7014a6924d0e 100644 --- a/src/evo/mnhftx.cpp +++ b/src/evo/mnhftx.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -45,9 +44,9 @@ CMutableTransaction MNHFTxPayload::PrepareTx() const return tx; } -CMNHFManager::CMNHFManager(CEvoDB& evoDb, const ChainstateManager& chainman) : +CMNHFManager::CMNHFManager(CEvoDB& evoDb, const Consensus::Params& consensus_params) : m_evoDb(evoDb), - m_chainman{chainman} + m_consensus_params{consensus_params} { assert(globalInstance == nullptr); globalInstance = this; @@ -61,7 +60,7 @@ CMNHFManager::~CMNHFManager() CMNHFManager::Signals CMNHFManager::GetSignalsStage(const CBlockIndex* const pindexPrev) { - if (!DeploymentActiveAfter(pindexPrev, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_V20)) return {}; + if (!DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_V20)) return {}; Signals signals_tmp = GetForBlock(pindexPrev); @@ -105,7 +104,7 @@ bool MNHFTxPayload::IsTriviallyValid(TxValidationState& state) const } template -static bool CheckMNHFTxImpl(const ChainstateManager& chainman, GetQuorum&& get_quorum, +static bool CheckMNHFTxImpl(const node::BlockManager& blockman, GetQuorum&& get_quorum, const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) { if (!tx.IsSpecialTxVersion() || tx.nType != TRANSACTION_MNHF_SIGNAL) { @@ -126,7 +125,7 @@ static bool CheckMNHFTxImpl(const ChainstateManager& chainman, GetQuorum&& get_q return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-non-ehf"); } - const CBlockIndex* pindexQuorum = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(mnhfTx.signal.quorumHash)); + const CBlockIndex* pindexQuorum = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(mnhfTx.signal.quorumHash)); if (!pindexQuorum) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-quorum-hash"); } @@ -157,19 +156,19 @@ static bool CheckMNHFTxImpl(const ChainstateManager& chainman, GetQuorum&& get_q return true; } -bool CheckMNHFTx(const ChainstateManager& chainman, const llmq::CQuorumManager& qman, +bool CheckMNHFTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) { - return CheckMNHFTxImpl(chainman, [&](Consensus::LLMQType llmq_type, const uint256& quorum_hash) { + return CheckMNHFTxImpl(blockman, [&](Consensus::LLMQType llmq_type, const uint256& quorum_hash) { return qman.GetQuorum(llmq_type, quorum_hash); }, tx, pindexPrev, state); } -bool CheckMNHFTx(const ChainstateManager& chainman, const llmq::CQuorumManager& qman, const CChain& chain, +bool CheckMNHFTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CChain& chain, const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) { AssertLockHeld(::cs_main); - return CheckMNHFTxImpl(chainman, [&](Consensus::LLMQType llmq_type, const uint256& quorum_hash) NO_THREAD_SAFETY_ANALYSIS { + return CheckMNHFTxImpl(blockman, [&](Consensus::LLMQType llmq_type, const uint256& quorum_hash) NO_THREAD_SAFETY_ANALYSIS { return qman.GetQuorum(llmq_type, quorum_hash, chain); }, tx, pindexPrev, state); } @@ -301,7 +300,7 @@ CMNHFManager::Signals CMNHFManager::GetForBlock(const CBlockIndex* pindex) LogPrintf("re-index EHF signals at block %d\n", pindex_top->nHeight); } CBlock block; - if (!ReadBlockFromDisk(block, pindex_top, m_chainman.GetConsensus())) { + if (!ReadBlockFromDisk(block, pindex_top, m_consensus_params)) { throw std::runtime_error("failed-getehfforblock-read"); } BlockValidationState state; @@ -341,7 +340,7 @@ std::optional CMNHFManager::GetFromCache(const CBlockInde } { LOCK(cs_cache); - if (!DeploymentActiveAt(*pindex, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_V20)) { + if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V20)) { mnhfCache.insert(blockHash, signals); return signals; } @@ -351,7 +350,7 @@ std::optional CMNHFManager::GetFromCache(const CBlockInde mnhfCache.insert(blockHash, signals); return signals; } - if (!DeploymentActiveAt(*pindex, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_MN_RR)) { + if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_MN_RR)) { // before mn_rr activation we are safe if (m_evoDb.Read(std::make_pair(DB_SIGNALS, blockHash), signals)) { LOCK(cs_cache); @@ -366,7 +365,7 @@ void CMNHFManager::AddToCache(const Signals& signals, const CBlockIndex* const p { assert(pindex != nullptr); const uint256& blockHash = pindex->GetBlockHash(); - if (DeploymentActiveAt(*pindex, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_V20) && + if (DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V20) && !m_evoDb.WriteDerived(std::make_pair(DB_SIGNALS_v2, blockHash), signals)) { // A mismatch is local EvoDB corruption, not a statement about the // block. Abort here: some callers (miner, RPC) never pass through a @@ -390,14 +389,14 @@ void CMNHFManager::AddSignal(const CBlockIndex* const pindex, int bit) AddToCache(signals, pindex); } -bool CMNHFManager::ForceSignalDBUpdate() +bool CMNHFManager::ForceSignalDBUpdate(const CBlockIndex* tip) { // force ehf signals db update auto dbTx = m_evoDb.BeginTransaction(); const bool last_legacy = bls::bls_legacy_scheme.load(); bls::bls_legacy_scheme.store(false); - GetSignalsStage(m_chainman.ActiveTip()); + GetSignalsStage(tip); bls::bls_legacy_scheme.store(last_legacy); dbTx->Commit(); diff --git a/src/evo/mnhftx.h b/src/evo/mnhftx.h index b5d721360d49..ead66e3fa876 100644 --- a/src/evo/mnhftx.h +++ b/src/evo/mnhftx.h @@ -24,12 +24,14 @@ class CBlockIndex; class CChain; class CEvoDB; class CTransaction; -class ChainstateManager; class TxValidationState; struct RPCResult; namespace llmq { class CQuorumManager; } +namespace node { +class BlockManager; +} // namespace node // mnhf signal special transaction class MNHFTx @@ -94,12 +96,10 @@ class CMNHFManager : public AbstractEHFManager { private: CEvoDB& m_evoDb; - // TODO: move its functionallity of ProcessBlock, UndoBlock to specialtxman; - // it will help to drop dependency on m_chainman here (and validation.h) - // Secondly, store in database active EHF signals not for each block; + // TODO: store in database active EHF signals not for each block; // but quite opposite: keep only hash of block where signal is added. // TODO: implement migration to a new format - const ChainstateManager& m_chainman; + const Consensus::Params& m_consensus_params; static constexpr size_t MNHFCacheSize = 1000; Mutex cs_cache; @@ -110,7 +110,7 @@ class CMNHFManager : public AbstractEHFManager CMNHFManager() = delete; CMNHFManager(const CMNHFManager&) = delete; CMNHFManager& operator=(const CMNHFManager&) = delete; - explicit CMNHFManager(CEvoDB& evoDb, const ChainstateManager& chainman); + explicit CMNHFManager(CEvoDB& evoDb, const Consensus::Params& consensus_params); ~CMNHFManager() override; /** @@ -135,7 +135,7 @@ class CMNHFManager : public AbstractEHFManager */ void AddSignal(const CBlockIndex* const pindex, int bit) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache); - bool ForceSignalDBUpdate() EXCLUSIVE_LOCKS_REQUIRED(::cs_main, !cs_cache); + bool ForceSignalDBUpdate(const CBlockIndex* tip) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, !cs_cache); private: void AddToCache(const Signals& signals, const CBlockIndex* const pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache); @@ -156,8 +156,8 @@ class CMNHFManager : public AbstractEHFManager }; std::optional extractEHFSignal(const CTransaction& tx); -bool CheckMNHFTx(const ChainstateManager& chainman, const llmq::CQuorumManager& qman, const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state); -bool CheckMNHFTx(const ChainstateManager& chainman, const llmq::CQuorumManager& qman, const CChain& chain, +bool CheckMNHFTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state); +bool CheckMNHFTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CChain& chain, const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); diff --git a/src/evo/providertx_service.cpp b/src/evo/providertx_service.cpp index 47961aba49d5..85a7a204a94a 100644 --- a/src/evo/providertx_service.cpp +++ b/src/evo/providertx_service.cpp @@ -347,7 +347,9 @@ std::optional Preflight(node::NodeContext& node, const CTransac } TxValidationState state; - if (!chain_helper.special_tx->CheckSpecialTx(tx, tip, chainman.ActiveChainstate().CoinsTip(), true, state)) { + const bool is_v24_active{DeploymentActiveAfter(tip, chainman, Consensus::DEPLOYMENT_V24)}; + if (!chain_helper.special_tx->CheckSpecialTx(tx, tip, is_v24_active, chainman.ActiveChainstate().CoinsTip(), true, + state)) { return Error(ProviderTxErrorCode::CONSENSUS_REJECTED, state.ToString(), state.GetRejectReason()); } return std::nullopt; diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index d8b89b43a89b..cc25a24eaa9e 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -20,16 +19,15 @@ #include #include #include -#include #include +#include #include #include #include #include #include #include -#include static bool AddNetInfoEntries(const std::shared_ptr& net_info, NetInfoPurpose purpose, const NetInfoList& entries, BlockValidationState& state) @@ -198,32 +196,30 @@ bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex, const return true; } -static bool CheckSpecialTxInner(CDeterministicMNManager& dmnman, llmq::CQuorumSnapshotManager& qsnapman, - const ChainstateManager& chainman, const llmq::CQuorumManager& qman, - const CChain* chain, - const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache& view, - const std::optional& indexes, bool check_sigs, TxValidationState& state) - EXCLUSIVE_LOCKS_REQUIRED(::cs_main) +bool CSpecialTxProcessor::CheckSpecialTxInner(const CChain* chain, const CTransaction& tx, + const CBlockIndex* pindexPrev, bool is_v24_active, + const CCoinsViewCache& view, const std::optional& indexes, + bool check_sigs, TxValidationState& state) { AssertLockHeld(::cs_main); if (!tx.HasExtraPayloadField()) return true; - if (!DeploymentActiveAfter(pindexPrev, chainman.GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) { + if (!DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_DIP0003)) { return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type-dip3-inactive"); } try { switch (tx.nType) { case TRANSACTION_PROVIDER_REGISTER: - return CheckProRegTx(tx, pindexPrev, dmnman, view, chainman, state, check_sigs); + return CheckProRegTx(tx, pindexPrev, m_dmnman, view, m_consensus_params, is_v24_active, state, check_sigs); case TRANSACTION_PROVIDER_UPDATE_SERVICE: - return CheckProUpServTx(tx, pindexPrev, dmnman, chainman, state, check_sigs); + return CheckProUpServTx(tx, pindexPrev, m_dmnman, m_consensus_params, is_v24_active, state, check_sigs); case TRANSACTION_PROVIDER_UPDATE_REGISTRAR: - return CheckProUpRegTx(tx, pindexPrev, dmnman, view, chainman, state, check_sigs); + return CheckProUpRegTx(tx, pindexPrev, m_dmnman, view, m_consensus_params, is_v24_active, state, check_sigs); case TRANSACTION_PROVIDER_UPDATE_REVOKE: - return CheckProUpRevTx(tx, pindexPrev, dmnman, chainman, state, check_sigs); + return CheckProUpRevTx(tx, pindexPrev, m_dmnman, m_consensus_params, is_v24_active, state, check_sigs); case TRANSACTION_COINBASE: { if (!tx.IsCoinBase()) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-invalid"); @@ -235,15 +231,15 @@ static bool CheckSpecialTxInner(CDeterministicMNManager& dmnman, llmq::CQuorumSn } } case TRANSACTION_QUORUM_COMMITMENT: - return llmq::CheckLLMQCommitment({dmnman, qsnapman, chainman, pindexPrev}, tx, state); + return llmq::CheckLLMQCommitment({m_dmnman, m_qsnapman, m_chainman, pindexPrev}, tx, state); case TRANSACTION_MNHF_SIGNAL: - return chain ? CheckMNHFTx(chainman, qman, *chain, tx, pindexPrev, state) : - CheckMNHFTx(chainman, qman, tx, pindexPrev, state); + return chain ? CheckMNHFTx(m_blockman, m_qman, *chain, tx, pindexPrev, state) : + CheckMNHFTx(m_blockman, m_qman, tx, pindexPrev, state); case TRANSACTION_ASSET_LOCK: - return CheckAssetLockTx(tx, state, DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)); + return CheckAssetLockTx(tx, state, is_v24_active); case TRANSACTION_ASSET_UNLOCK: - return chain ? CheckAssetUnlockTx(chainman.m_blockman, qman, *chain, tx, pindexPrev, indexes, state) : - CheckAssetUnlockTx(chainman.m_blockman, qman, tx, pindexPrev, indexes, state); + return chain ? CheckAssetUnlockTx(m_blockman, m_qman, *chain, tx, pindexPrev, indexes, state) : + CheckAssetUnlockTx(m_blockman, m_qman, tx, pindexPrev, indexes, state); } } catch (const std::exception& e) { LogPrintf("%s -- failed: %s\n", __func__, e.what()); @@ -253,11 +249,11 @@ static bool CheckSpecialTxInner(CDeterministicMNManager& dmnman, llmq::CQuorumSn return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type-check"); } -bool CSpecialTxProcessor::CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache& view, bool check_sigs, TxValidationState& state) +bool CSpecialTxProcessor::CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, bool is_v24_active, + const CCoinsViewCache& view, bool check_sigs, TxValidationState& state) { AssertLockHeld(::cs_main); - return CheckSpecialTxInner(m_dmnman, m_qsnapman, m_chainman, m_qman, nullptr, tx, pindexPrev, view, std::nullopt, check_sigs, - state); + return CheckSpecialTxInner(nullptr, tx, pindexPrev, is_v24_active, view, std::nullopt, check_sigs, state); } static void HandleQuorumCommitment(const llmq::CFinalCommitment& qc, const std::vector& members, @@ -278,18 +274,18 @@ static void HandleQuorumCommitment(const llmq::CFinalCommitment& qc, const std:: } bool CSpecialTxProcessor::BuildNewListFromBlock(const CBlock& block, gsl::not_null pindexPrev, - const CCoinsViewCache& view, bool debugLogs, + bool is_v24_active, const CCoinsViewCache& view, bool debugLogs, BlockValidationState& state, CDeterministicMNList& mnListRet) { AssertLockHeld(cs_main); CDeterministicMNList oldList = m_dmnman.GetListForBlock(pindexPrev); - return RebuildListFromBlock(block, pindexPrev, oldList, view, debugLogs, state, mnListRet); + return RebuildListFromBlock(block, pindexPrev, is_v24_active, oldList, view, debugLogs, state, mnListRet); } bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_null pindexPrev, - const CDeterministicMNList& prevList, const CCoinsViewCache& view, - bool debugLogs, BlockValidationState& state, - CDeterministicMNList& mnListRet) + bool is_v24_active, const CDeterministicMNList& prevList, + const CCoinsViewCache& view, bool debugLogs, BlockValidationState& state, + CDeterministicMNList& mnListRet) { // Verify that prevList either represents an empty/initial state (default-constructed), // or it matches the previous block's hash. @@ -324,9 +320,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul newList.DecreaseScores(); - const bool isMNRewardReallocation{ - DeploymentActiveAfter(pindexPrev, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_MN_RR)}; - const bool is_v24_deployed{DeploymentActiveAfter(pindexPrev, m_chainman, Consensus::DEPLOYMENT_V24)}; + const bool isMNRewardReallocation{DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_MN_RR)}; // we skip the coinbase for (int i = 1; i < static_cast(block.vtx.size()); i++) { @@ -413,7 +407,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul // to each other and two of them could claim one operator key under different encodings. // Re-probe the list as rebuilt so far. AddMN() reports a duplicate by throwing, which // would escape block assembly, so reject cleanly here instead. - if (is_v24_deployed && + if (is_v24_active && newList.HasOperatorKeyUnderAnyScheme(dmn->pdmnState->pubKeyOperator.Get(), /*self=*/uint256())) { return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-key"); } @@ -459,8 +453,9 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul auto newState = std::make_shared(*dmn->pdmnState); const uint16_t current_version{static_cast(newState->nVersion)}; - const uint16_t target_version{is_v24_deployed ? std::max(current_version, opt_proTx->nVersion) : current_version}; - if (is_v24_deployed) { + const uint16_t target_version{is_v24_active ? std::max(current_version, opt_proTx->nVersion) + : current_version}; + if (is_v24_active) { // Extended addresses support in v24 means that the version can be updated newState->nVersion = opt_proTx->nVersion; } @@ -481,7 +476,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul newState->platformHTTPPort = 0; } } - if (is_v24_deployed && !SetStateVersion(*newState, target_version, dmn->nType, state)) { + if (is_v24_active && !SetStateVersion(*newState, target_version, dmn->nType, state)) { return false; } if (newState->IsBanned()) { @@ -500,7 +495,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul // against pindexPrev, so re-check against the list as rebuilt so far: if another // masternode holds this key under either encoding, the re-key in UpdateMN() would throw // out of block assembly. - if (is_v24_deployed && IsSchemeMigration(current_version, target_version) && + if (is_v24_active && IsSchemeMigration(current_version, target_version) && newList.HasOperatorKeyUnderAnyScheme(dmn->pdmnState->pubKeyOperator.Get(), /*self=*/opt_proTx->proTxHash)) { return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-key"); @@ -524,8 +519,8 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul auto newState = std::make_shared(*dmn->pdmnState); const uint16_t old_version{static_cast(newState->nVersion)}; const bool operator_changed{newState->pubKeyOperator != opt_proTx->pubKeyOperator}; - const uint16_t target_version{is_v24_deployed ? std::max(old_version, opt_proTx->nVersion) - : (operator_changed ? opt_proTx->nVersion : old_version)}; + const uint16_t target_version{is_v24_active ? std::max(old_version, opt_proTx->nVersion) + : (operator_changed ? opt_proTx->nVersion : old_version)}; // Per-transaction checks ran against pindexPrev, so an earlier transaction in this same // block is invisible to them. Re-evaluate against the list as rebuilt so far: this update @@ -536,7 +531,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul // routine update is not blocked. { const bool migrating{IsSchemeMigration(old_version, target_version)}; - if (is_v24_deployed && (operator_changed || migrating) && + if (is_v24_active && (operator_changed || migrating) && newList.HasOperatorKeyUnderAnyScheme(opt_proTx->pubKeyOperator.Get(), /*self=*/opt_proTx->proTxHash)) { return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-key"); @@ -588,7 +583,7 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul // preserved (never silently downgraded), so pick the target the same max-based way the registrar // path does and restore it below. Pre-v24 keep the historical reset-to-legacy behaviour. uint16_t target_version{ProTxVersion::LegacyBLS}; - if (is_v24_deployed) { + if (is_v24_active) { target_version = std::max(old_version, opt_proTx->nVersion); } newState->ResetOperatorFields(); @@ -691,8 +686,11 @@ bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_nul return true; } -bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(Chainstate& chainstate, const CBlock& block, const CBlockIndex* pindex, const CCoinsViewCache& view, bool fJustCheck, - bool fCheckCbTxMerkleRoots, BlockValidationState& state, std::optional& updatesRet) +bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(Chainstate& chainstate, const CChain& chain, const CBlock& block, + const CBlockIndex* pindex, bool is_v24_active, + const CCoinsViewCache& view, CAmount blockSubsidy, bool fJustCheck, + bool fCheckCbTxMerkleRoots, BlockValidationState& state, + MNListUpdates& updatesRet) { AssertLockHeld(::cs_main); @@ -752,9 +750,8 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(Chainstate& chainstate, const TxValidationState tx_state; // At this moment CheckSpecialTx() may fail by 2 possible ways: // consensus failures and "TX_BAD_SPECIAL" - if (!CheckSpecialTxInner(m_dmnman, m_qsnapman, m_chainman, m_qman, &chainstate.m_chain, - *ptr_tx, pindex->pprev, view, indexes, - fCheckCbTxMerkleRoots, tx_state)) { + if (!CheckSpecialTxInner(&chain, *ptr_tx, pindex->pprev, is_v24_active, view, indexes, fCheckCbTxMerkleRoots, + tx_state)) { assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS || tx_state.GetResult() == TxValidationResult::TX_BAD_SPECIAL); return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), strprintf("Special Transaction check failed (tx hash %s) %s", ptr_tx->GetHash().ToString(), tx_state.GetDebugMessage())); @@ -766,7 +763,7 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(Chainstate& chainstate, const LogPrint(BCLog::BENCHMARK, " - Loop: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeLoop * 0.000001); if (opt_cbTx.has_value()) { - if (!CheckCreditPoolDiffForBlock(block, pindex, *opt_cbTx, state)) { + if (!CheckCreditPoolDiffForBlock(block, pindex, *opt_cbTx, blockSubsidy, state)) { return error("CSpecialTxProcessor: CheckCreditPoolDiffForBlock for block %s failed with %s", pindex->GetBlockHash().ToString(), state.ToString()); } @@ -789,7 +786,7 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(Chainstate& chainstate, const CDeterministicMNList mn_list; if (DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0003)) { - if (!BuildNewListFromBlock(block, pindex->pprev, view, true, state, mn_list)) { + if (!BuildNewListFromBlock(block, pindex->pprev, is_v24_active, view, true, state, mn_list)) { // pass the state returned by the function above return false; } @@ -800,15 +797,6 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(Chainstate& chainstate, const return false; } } - if (!fJustCheck) { - // Persist the list produced by this chainstate's own connection of - // the snapshot base block (no-op for every other block). Snapshot - // activation may populate the shared MN-list cache with seeded - // state, so completion must not reconstruct this value through that - // cache. Before DIP3 activates, mn_list is the independently - // computed empty list. - chainstate.RecordBackgroundMNListHash(pindex, mn_list); - } int64_t nTime6 = GetTimeMicros(); nTimeDMN += nTime6 - nTime5; @@ -851,8 +839,7 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(Chainstate& chainstate, const LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootQuorums: %.2fms [%.2fs]\n", 0.001 * (nTime6_2 - nTime6_1), nTimeMerkleQuorums * 0.000001); - if (!CheckCbTxBestChainlock(*opt_cbTx, pindex, m_consensus_params, chainstate.m_chain, m_qman, - m_chainlocks, state)) { + if (!CheckCbTxBestChainlock(*opt_cbTx, pindex, m_consensus_params, chain, m_qman, m_chainlocks, state)) { // pass the state returned by the function above return false; } @@ -893,7 +880,7 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(Chainstate& chainstate, const return true; } -bool CSpecialTxProcessor::UndoSpecialTxsInBlock(const Chainstate& chainstate, const CBlock& block, const CBlockIndex* pindex, std::optional& updatesRet) +bool CSpecialTxProcessor::UndoSpecialTxsInBlock(const Chainstate& chainstate, const CBlock& block, const CBlockIndex* pindex, MNListUpdates& updatesRet) { AssertLockHeld(::cs_main); @@ -928,7 +915,7 @@ bool CSpecialTxProcessor::UndoSpecialTxsInBlock(const Chainstate& chainstate, co } bool CSpecialTxProcessor::CheckCreditPoolDiffForBlock(const CBlock& block, const CBlockIndex* pindex, const CCbTx& cbTx, - BlockValidationState& state) + CAmount blockSubsidy, BlockValidationState& state) { AssertLockHeld(::cs_main); @@ -936,7 +923,6 @@ bool CSpecialTxProcessor::CheckCreditPoolDiffForBlock(const CBlock& block, const if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V20)) return true; try { - const CAmount blockSubsidy = GetBlockSubsidy(pindex, m_consensus_params); const auto creditPoolDiff = GetCreditPoolDiffForBlock(m_cpoolman, block, pindex->pprev, m_consensus_params, blockSubsidy, state); if (!creditPoolDiff.has_value()) return false; @@ -991,7 +977,8 @@ static bool CheckHashSig(const ProTx& proTx, const CBLSPublicKey& pubKey, TxVali template std::optional GetValidatedPayload(const CTransaction& tx, gsl::not_null pindexPrev, - const ChainstateManager& chainman, TxValidationState& state) + const Consensus::Params& consensus_params, bool is_v24_active, + TxValidationState& state) { if (tx.nType != ProTx::SPECIALTX_TYPE) { state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type"); @@ -1003,7 +990,8 @@ std::optional GetValidatedPayload(const CTransaction& tx, gsl::not_nullnVersion > DeploymentToProtxVersion(pindexPrev, chainman)) { + const bool is_v19_active{DeploymentActiveAfter(pindexPrev, consensus_params, Consensus::DEPLOYMENT_V19)}; + if (opt_ptx->nVersion > ProTxVersion::GetMax(is_v19_active, is_v24_active)) { state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version"); return std::nullopt; } @@ -1016,16 +1004,16 @@ std::optional GetValidatedPayload(const CTransaction& tx, gsl::not_null pindexPrev, const uint16_t state_version, - const uint16_t tx_version, const ChainstateManager& chainman, TxValidationState& state) +static bool IsVersionChangeValid(const uint16_t state_version, const uint16_t tx_version, bool is_v24_active, + TxValidationState& state) { - if (!DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)) { + if (!is_v24_active) { // New restrictions only apply after v24 deployment return true; } @@ -1038,18 +1026,16 @@ static bool IsVersionChangeValid(gsl::not_null pindexPrev, c return true; } -bool CheckProRegTx(const CTransaction& tx, gsl::not_null pindexPrev, - CDeterministicMNManager& dmnman, const CCoinsViewCache& view, const ChainstateManager& chainman, +bool CheckProRegTx(const CTransaction& tx, gsl::not_null pindexPrev, CDeterministicMNManager& dmnman, + const CCoinsViewCache& view, const Consensus::Params& consensus_params, bool is_v24_active, TxValidationState& state, bool check_sigs) { - const auto opt_ptx = GetValidatedPayload(tx, pindexPrev, chainman, state); + const auto opt_ptx = GetValidatedPayload(tx, pindexPrev, consensus_params, is_v24_active, state); if (!opt_ptx) { // pass the state returned by the function above return false; } - const bool is_v24_active{DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)}; - // No longer allow legacy scheme masternode registration if (is_v24_active && opt_ptx->nVersion < ProTxVersion::BasicBLS) { return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-disallowed"); @@ -1176,10 +1162,11 @@ bool CheckProRegTx(const CTransaction& tx, gsl::not_null pin return true; } -bool CheckProUpServTx(const CTransaction& tx, gsl::not_null pindexPrev, CDeterministicMNManager& dmnman, - const ChainstateManager& chainman, TxValidationState& state, bool check_sigs) +bool CheckProUpServTx(const CTransaction& tx, gsl::not_null pindexPrev, + CDeterministicMNManager& dmnman, const Consensus::Params& consensus_params, bool is_v24_active, + TxValidationState& state, bool check_sigs) { - const auto opt_ptx = GetValidatedPayload(tx, pindexPrev, chainman, state); + const auto opt_ptx = GetValidatedPayload(tx, pindexPrev, consensus_params, is_v24_active, state); if (!opt_ptx) { // pass the state returned by the function above return false; @@ -1204,7 +1191,7 @@ bool CheckProUpServTx(const CTransaction& tx, gsl::not_null return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type-mismatch"); } - if (!IsVersionChangeValid(pindexPrev, dmn->pdmnState->nVersion, opt_ptx->nVersion, chainman, state)) { + if (!IsVersionChangeValid(dmn->pdmnState->nVersion, opt_ptx->nVersion, is_v24_active, state)) { // pass the state returned by the function above return false; } @@ -1213,8 +1200,7 @@ bool CheckProUpServTx(const CTransaction& tx, gsl::not_null // re-encodes its stored key, moving it to the basic-scheme unique-property slot. If another // masternode already holds that key under either encoding, the re-key in UpdateMN() would throw // out of block assembly, so reject the migration cleanly here. - if (DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24) && - IsSchemeMigration(dmn->pdmnState->nVersion, opt_ptx->nVersion) && + if (is_v24_active && IsSchemeMigration(dmn->pdmnState->nVersion, opt_ptx->nVersion) && mnList.HasOperatorKeyUnderAnyScheme(dmn->pdmnState->pubKeyOperator.Get(), /*self=*/opt_ptx->proTxHash)) { return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-key"); } @@ -1267,11 +1253,11 @@ bool CheckProUpServTx(const CTransaction& tx, gsl::not_null return true; } -bool CheckProUpRegTx(const CTransaction& tx, gsl::not_null pindexPrev, - CDeterministicMNManager& dmnman, const CCoinsViewCache& view, const ChainstateManager& chainman, +bool CheckProUpRegTx(const CTransaction& tx, gsl::not_null pindexPrev, CDeterministicMNManager& dmnman, + const CCoinsViewCache& view, const Consensus::Params& consensus_params, bool is_v24_active, TxValidationState& state, bool check_sigs) { - const auto opt_ptx = GetValidatedPayload(tx, pindexPrev, chainman, state); + const auto opt_ptx = GetValidatedPayload(tx, pindexPrev, consensus_params, is_v24_active, state); if (!opt_ptx) { // pass the state returned by the function above return false; @@ -1283,7 +1269,7 @@ bool CheckProUpRegTx(const CTransaction& tx, gsl::not_null p return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash"); } - if (!IsVersionChangeValid(pindexPrev, dmn->pdmnState->nVersion, opt_ptx->nVersion, chainman, state)) { + if (!IsVersionChangeValid(dmn->pdmnState->nVersion, opt_ptx->nVersion, is_v24_active, state)) { // pass the state returned by the function above return false; } @@ -1293,7 +1279,7 @@ bool CheckProUpRegTx(const CTransaction& tx, gsl::not_null p // if that target slot is already held by another masternode -- under either encoding -- so the // re-key in UpdateMN() cannot collide and throw out of block assembly. Scoped to those two cases // so a pre-existing cross-scheme pair's non-migrating routine update is not blocked. - if (DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)) { + if (is_v24_active) { const bool key_changed{!(opt_ptx->pubKeyOperator == dmn->pdmnState->pubKeyOperator)}; const bool migrating{IsSchemeMigration(dmn->pdmnState->nVersion, opt_ptx->nVersion)}; if ((key_changed || migrating) && @@ -1348,10 +1334,11 @@ bool CheckProUpRegTx(const CTransaction& tx, gsl::not_null p return true; } -bool CheckProUpRevTx(const CTransaction& tx, gsl::not_null pindexPrev, CDeterministicMNManager& dmnman, - const ChainstateManager& chainman, TxValidationState& state, bool check_sigs) +bool CheckProUpRevTx(const CTransaction& tx, gsl::not_null pindexPrev, + CDeterministicMNManager& dmnman, const Consensus::Params& consensus_params, bool is_v24_active, + TxValidationState& state, bool check_sigs) { - const auto opt_ptx = GetValidatedPayload(tx, pindexPrev, chainman, state); + const auto opt_ptx = GetValidatedPayload(tx, pindexPrev, consensus_params, is_v24_active, state); if (!opt_ptx) { // pass the state returned by the function above return false; @@ -1363,7 +1350,7 @@ bool CheckProUpRevTx(const CTransaction& tx, gsl::not_null p return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash"); } - if (!IsVersionChangeValid(pindexPrev, dmn->pdmnState->nVersion, opt_ptx->nVersion, chainman, state)) { + if (!IsVersionChangeValid(dmn->pdmnState->nVersion, opt_ptx->nVersion, is_v24_active, state)) { // pass the state returned by the function above return false; } diff --git a/src/evo/specialtxman.h b/src/evo/specialtxman.h index b2e7a33ec62e..3a7f4a3f40b4 100644 --- a/src/evo/specialtxman.h +++ b/src/evo/specialtxman.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_EVO_SPECIALTXMAN_H #define BITCOIN_EVO_SPECIALTXMAN_H +#include #include #include #include @@ -20,6 +21,7 @@ class CCoinsViewCache; class CCreditPoolManager; class CDeterministicMNList; class CDeterministicMNManager; +class CRangesSet; class CTransaction; class ChainstateManager; class Chainstate; @@ -36,6 +38,9 @@ class CQuorumBlockProcessor; class CQuorumManager; class CQuorumSnapshotManager; } // namespace llmq +namespace node { +class BlockManager; +} // namespace node extern RecursiveMutex cs_main; // NOLINT(readability-redundant-declaration) @@ -48,6 +53,7 @@ class CSpecialTxProcessor llmq::CQuorumBlockProcessor& m_qblockman; llmq::CQuorumSnapshotManager& m_qsnapman; const ChainstateManager& m_chainman; + const node::BlockManager& m_blockman; const Consensus::Params& m_consensus_params; const chainlock::Chainlocks& m_chainlocks; const llmq::CQuorumManager& m_qman; @@ -55,43 +61,50 @@ class CSpecialTxProcessor public: explicit CSpecialTxProcessor(CCreditPoolManager& cpoolman, CDeterministicMNManager& dmnman, CMNHFManager& mnhfman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, - const ChainstateManager& chainman, const Consensus::Params& consensus_params, - const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman) : + const ChainstateManager& chainman, const node::BlockManager& blockman, + const Consensus::Params& consensus_params, const chainlock::Chainlocks& chainlocks, + const llmq::CQuorumManager& qman) : m_cpoolman(cpoolman), m_dmnman{dmnman}, m_mnhfman{mnhfman}, m_qblockman{qblockman}, m_qsnapman{qsnapman}, m_chainman(chainman), + m_blockman{blockman}, m_consensus_params{consensus_params}, m_chainlocks{chainlocks}, m_qman{qman} { } - bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache& view, bool check_sigs, TxValidationState& state) + bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, bool is_v24_active, + const CCoinsViewCache& view, bool check_sigs, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - bool ProcessSpecialTxsInBlock(Chainstate& chainstate, const CBlock& block, const CBlockIndex* pindex, const CCoinsViewCache& view, bool fJustCheck, - bool fCheckCbTxMerkleRoots, BlockValidationState& state, std::optional& updatesRet) - EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - bool UndoSpecialTxsInBlock(const Chainstate& chainstate, const CBlock& block, const CBlockIndex* pindex, std::optional& updatesRet) + bool ProcessSpecialTxsInBlock(Chainstate& chainstate, const CChain& chain, const CBlock& block, const CBlockIndex* pindex, + bool is_v24_active, const CCoinsViewCache& view, CAmount blockSubsidy, bool fJustCheck, + bool fCheckCbTxMerkleRoots, BlockValidationState& state, + MNListUpdates& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + bool UndoSpecialTxsInBlock(const Chainstate& chainstate, const CBlock& block, const CBlockIndex* pindex, MNListUpdates& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); // the returned list will not contain the correct block hash (we can't know it yet as the coinbase TX is not updated yet) - bool BuildNewListFromBlock(const CBlock& block, gsl::not_null pindexPrev, + bool BuildNewListFromBlock(const CBlock& block, gsl::not_null pindexPrev, bool is_v24_active, const CCoinsViewCache& view, bool debugLogs, BlockValidationState& state, CDeterministicMNList& mnListRet) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Variant that takes an explicit starting list instead of loading from GetListForBlock // Used for rebuilding diffs from trusted snapshots - bool RebuildListFromBlock(const CBlock& block, gsl::not_null pindexPrev, + bool RebuildListFromBlock(const CBlock& block, gsl::not_null pindexPrev, bool is_v24_active, const CDeterministicMNList& prevList, const CCoinsViewCache& view, bool debugLogs, BlockValidationState& state, CDeterministicMNList& mnListRet); private: + bool CheckSpecialTxInner(const CChain* chain, const CTransaction& tx, const CBlockIndex* pindexPrev, + bool is_v24_active, const CCoinsViewCache& view, const std::optional& indexes, + bool check_sigs, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); bool CheckCreditPoolDiffForBlock(const CBlock& block, const CBlockIndex* pindex, const CCbTx& cbTx, - BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + CAmount blockSubsidy, BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); }; /** @@ -101,23 +114,26 @@ class CSpecialTxProcessor */ template std::optional GetValidatedPayload(const CTransaction& tx, gsl::not_null pindexPrev, - const ChainstateManager& chainman, TxValidationState& state); + const Consensus::Params& consensus_params, bool is_v24_active, + TxValidationState& state); /** Validates the bestCLSignature / bestCLHeightDiff fields embedded in a CbTx payload. */ bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex, const Consensus::Params& consensus_params, const CChain& chain, const llmq::CQuorumManager& qman, const chainlock::Chainlocks& chainlocks, BlockValidationState& state); -bool CheckProRegTx(const CTransaction& tx, gsl::not_null pindexPrev, - CDeterministicMNManager& dmnman, const CCoinsViewCache& view, const ChainstateManager& chainman, +bool CheckProRegTx(const CTransaction& tx, gsl::not_null pindexPrev, CDeterministicMNManager& dmnman, + const CCoinsViewCache& view, const Consensus::Params& consensus_params, bool is_v24_active, TxValidationState& state, bool check_sigs); -bool CheckProUpServTx(const CTransaction& tx, gsl::not_null pindexPrev, CDeterministicMNManager& dmnman, - const ChainstateManager& chainman, TxValidationState& state, bool check_sigs); -bool CheckProUpRegTx(const CTransaction& tx, gsl::not_null pindexPrev, - CDeterministicMNManager& dmnman, const CCoinsViewCache& view, const ChainstateManager& chainman, +bool CheckProUpServTx(const CTransaction& tx, gsl::not_null pindexPrev, + CDeterministicMNManager& dmnman, const Consensus::Params& consensus_params, bool is_v24_active, + TxValidationState& state, bool check_sigs); +bool CheckProUpRegTx(const CTransaction& tx, gsl::not_null pindexPrev, CDeterministicMNManager& dmnman, + const CCoinsViewCache& view, const Consensus::Params& consensus_params, bool is_v24_active, + TxValidationState& state, bool check_sigs); +bool CheckProUpRevTx(const CTransaction& tx, gsl::not_null pindexPrev, + CDeterministicMNManager& dmnman, const Consensus::Params& consensus_params, bool is_v24_active, TxValidationState& state, bool check_sigs); -bool CheckProUpRevTx(const CTransaction& tx, gsl::not_null pindexPrev, CDeterministicMNManager& dmnman, - const ChainstateManager& chainman, TxValidationState& state, bool check_sigs); /** diff --git a/src/init.cpp b/src/init.cpp index 63318d925702..31beb40db1ed 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2455,11 +2455,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) LogPrintf("Verifying and repairing masternode list diffs...\n"); const auto start{SteadyClock::now()}; // Create a callback that wraps CSpecialTxProcessor::BuildNewListFromBlock - auto build_list_func = [&node](const CBlock& block, const CBlockIndex* const pindexPrev, - const CDeterministicMNList& prevList, const CCoinsViewCache& view, - bool debugLogs, BlockValidationState& state, - CDeterministicMNList& mnListRet) -> bool { - return node.chain_helper->special_tx->RebuildListFromBlock(block, pindexPrev, prevList, view, debugLogs, state, mnListRet); + auto build_list_func = + [&node, &chainman](const CBlock& block, const CBlockIndex* const pindexPrev, + const CDeterministicMNList& prevList, const CCoinsViewCache& view, bool debugLogs, + BlockValidationState& state, CDeterministicMNList& mnListRet) -> bool { + const bool is_v24_active{DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)}; + return node.chain_helper->special_tx->RebuildListFromBlock(block, pindexPrev, is_v24_active, prevList, + view, debugLogs, state, mnListRet); }; auto result = node.dmnman->RecalculateAndRepairDiffs(start_index, stop_index, build_list_func, true); diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index e9dbde777940..cff3ee60a735 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -163,8 +163,8 @@ static ChainstateLoadResult CompleteChainstateInitialization(ChainstateManager& // Initialize chain_helper chain_helper.reset(); chain_helper = std::make_unique(evodb, dmnman, *options.mn_sync, *options.isman, *(llmq_ctx->quorum_block_processor), - *(llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), *options.chainlocks, - *(llmq_ctx->qman)); + *(llmq_ctx->qsnapman), chainman, chainman.m_blockman, chainman.GetConsensus(), + *options.chainlocks, *(llmq_ctx->qman)); if (options.reindex) { pblocktree->WriteReindexing(true); @@ -278,7 +278,7 @@ static ChainstateLoadResult CompleteChainstateInitialization(ChainstateManager& } } - if (!chain_helper->ehf_manager->ForceSignalDBUpdate()) { + if (!chain_helper->ehf_manager->ForceSignalDBUpdate(chainman.ActiveTip())) { return {ChainstateLoadStatus::FAILURE, _("Error upgrading evo database for EHF")}; } diff --git a/src/node/miner.cpp b/src/node/miner.cpp index f40d761d46a3..e3ced8ca331d 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -280,7 +280,9 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc BlockValidationState state; CDeterministicMNList mn_list; - if (!m_chain_helper.special_tx->BuildNewListFromBlock(*pblock, pindexPrev, m_chainstate.CoinsTip(), true, state, mn_list)) { + const bool is_v24_active{DeploymentActiveAfter(pindexPrev, m_chainstate.m_chainman, Consensus::DEPLOYMENT_V24)}; + if (!m_chain_helper.special_tx->BuildNewListFromBlock(*pblock, pindexPrev, is_v24_active, + m_chainstate.CoinsTip(), true, state, mn_list)) { throw std::runtime_error(strprintf("%s: BuildNewListFromBlock failed: %s", __func__, state.ToString())); } if (!CalcCbTxMerkleRootMNList(cbTx.merkleRootMNList, mn_list.to_sml(), state)) { @@ -369,6 +371,8 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, unsigned int packageSigOp // - safe TXs in regard to ChainLocks bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) const { + const bool is_v24_active{ + DeploymentActiveAfter(m_chainstate.m_chain.Tip(), m_chainstate.m_chainman, Consensus::DEPLOYMENT_V24)}; for (CTxMemPool::txiter it : package) { if (!IsFinalTx(it->GetTx(), nHeight, m_lock_time_cutoff)) { return false; @@ -385,7 +389,7 @@ bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& packa // signature regardless, which is cheap enough here given how rare MNHF signals are. if (it->GetTx().IsSpecialTxVersion()) { TxValidationState tx_state; - if (!m_chain_helper.special_tx->CheckSpecialTx(it->GetTx(), m_chainstate.m_chain.Tip(), + if (!m_chain_helper.special_tx->CheckSpecialTx(it->GetTx(), m_chainstate.m_chain.Tip(), is_v24_active, m_chainstate.CoinsTip(), /*check_sigs=*/false, tx_state)) { return false; } diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index fbe5d0e046b6..3e267ee14a43 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -1610,11 +1610,13 @@ static UniValue evodb_verify_or_repair_impl(const JSONRPCRequest& request, bool } // Create a callback that wraps CSpecialTxProcessor::RebuildListFromBlock - auto build_list_func = [&chain_helper](const CBlock& block, const CBlockIndex* const pindexPrev, - const CDeterministicMNList& prevList, const CCoinsViewCache& view, - bool debugLogs, BlockValidationState& state, - CDeterministicMNList& mnListRet) -> bool { - return chain_helper.special_tx->RebuildListFromBlock(block, pindexPrev, prevList, view, debugLogs, state, mnListRet); + auto build_list_func = [&chain_helper, &chainman](const CBlock& block, const CBlockIndex* const pindexPrev, + const CDeterministicMNList& prevList, const CCoinsViewCache& view, + bool debugLogs, BlockValidationState& state, + CDeterministicMNList& mnListRet) -> bool { + const bool is_v24_active{DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)}; + return chain_helper.special_tx->RebuildListFromBlock(block, pindexPrev, is_v24_active, prevList, view, + debugLogs, state, mnListRet); }; // Call the dmnman method to do the work diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index e76b07e6059c..7d43bcfd9b4c 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -40,6 +40,11 @@ #include #include +static bool IsV24Active(const ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) +{ + return DeploymentActiveAfter(chainman.ActiveChain().Tip(), chainman, Consensus::DEPLOYMENT_V24); +} + static CMutableTransaction CreateSpendTx(const ChainstateManager& chainman, SimpleUTXOMap& utxos, const CScript& scriptPayout, CAmount amount, const CKey& coinbaseKey) { CMutableTransaction tx; @@ -530,7 +535,8 @@ void FuncProUpRegTxV3OnLegacyValid(TestChainSetup& setup) { LOCK(cs_main); BOOST_CHECK(CheckProUpRegTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, val_state, /*check_sigs=*/true)); + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), val_state, /*check_sigs=*/true)); } BOOST_CHECK(val_state.IsValid()); }; @@ -605,7 +611,8 @@ void FuncProUpRegTxV2CannotBypassV3PayoutCollateralReuse(TestChainSetup& setup) { LOCK(cs_main); BOOST_CHECK(!CheckProUpRegTx(CTransaction(tx_upreg), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, val_state, /*check_sigs=*/true)); + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), val_state, /*check_sigs=*/true)); } BOOST_CHECK_EQUAL(val_state.GetRejectReason(), "bad-protx-payee-reuse"); } @@ -880,7 +887,8 @@ void FuncProRegTxRejectsInvalidDeserializedExtNetInfo(TestChainSetup& setup) { LOCK(cs_main); BOOST_CHECK(!CheckProRegTx(BuildExtNetInfoProRegTx(std::move(net_info)), chainman.ActiveChain().Tip(), - dmnman, chainman.ActiveChainstate().CoinsTip(), chainman, state, + dmnman, chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), state, /*check_sigs=*/false)); } BOOST_CHECK_EQUAL(state.GetResult(), TxValidationResult::TX_BAD_SPECIAL); @@ -946,9 +954,11 @@ void FuncDIP3Protx(TestChainSetup& setup) { LOCK(cs_main); BOOST_REQUIRE(CheckProRegTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, dummy_state, true)); + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), dummy_state, true)); BOOST_REQUIRE(CheckProRegTx(CTransaction(tx2), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, dummy_state, true)); + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), dummy_state, true)); } // But the signature should not verify anymore BOOST_REQUIRE(CheckTransactionSignature(tx, coins)); @@ -1055,9 +1065,11 @@ void FuncDIP3Protx(TestChainSetup& setup) { LOCK(cs_main); BOOST_REQUIRE(CheckProUpRegTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, dummy_state, true)); + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), dummy_state, true)); BOOST_REQUIRE(!CheckProUpRegTx(CTransaction(tx2), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, dummy_state, true)); + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), dummy_state, true)); } BOOST_REQUIRE(CheckTransactionSignature(tx, coins)); BOOST_REQUIRE(!CheckTransactionSignature(tx2, coins)); @@ -1169,7 +1181,8 @@ void FuncProUpServInvalidNType(TestChainSetup& setup) TxValidationState check_state; { LOCK(cs_main); - BOOST_CHECK(!CheckProUpServTx(CTransaction(tx), tip_index(), dmnman, chainman, check_state, /*check_sigs=*/true)); + BOOST_CHECK(!CheckProUpServTx(CTransaction(tx), tip_index(), dmnman, chainman.GetConsensus(), + IsV24Active(chainman), check_state, /*check_sigs=*/true)); } BOOST_CHECK_EQUAL(check_state.GetRejectReason(), "bad-protx-type"); @@ -1194,7 +1207,8 @@ void FuncProUpServInvalidNType(TestChainSetup& setup) TxValidationState check_state; { LOCK(cs_main); - BOOST_CHECK(!CheckProUpServTx(CTransaction(tx), tip_index(), dmnman, chainman, check_state, /*check_sigs=*/true)); + BOOST_CHECK(!CheckProUpServTx(CTransaction(tx), tip_index(), dmnman, chainman.GetConsensus(), + IsV24Active(chainman), check_state, /*check_sigs=*/true)); } BOOST_CHECK_EQUAL(check_state.GetRejectReason(), "bad-protx-type-mismatch"); BOOST_CHECK(check_state.GetResult() == TxValidationResult::TX_CONSENSUS); @@ -1224,7 +1238,8 @@ void FuncProUpServInvalidNType(TestChainSetup& setup) CDeterministicMNList mn_list; LOCK(cs_main); BOOST_CHECK(!chainman.ActiveChainstate().ChainHelper().special_tx->BuildNewListFromBlock( - block, tip_index(), chainman.ActiveChainstate().CoinsTip(), /*debugLogs=*/false, state, mn_list)); + block, tip_index(), IsV24Active(chainman), chainman.ActiveChainstate().CoinsTip(), /*debugLogs=*/false, + state, mn_list)); BOOST_CHECK_EQUAL(state.GetRejectReason(), expected_reason); } @@ -1234,7 +1249,8 @@ void FuncProUpServInvalidNType(TestChainSetup& setup) TxValidationState check_state; { LOCK(cs_main); - BOOST_CHECK_MESSAGE(CheckProUpServTx(CTransaction(tx), tip_index(), dmnman, chainman, check_state, + BOOST_CHECK_MESSAGE(CheckProUpServTx(CTransaction(tx), tip_index(), dmnman, chainman.GetConsensus(), + IsV24Active(chainman), check_state, /*check_sigs=*/true), "unexpected rejection: " << check_state.GetRejectReason()); } @@ -1492,7 +1508,8 @@ void FuncTestMempoolProRegReplacementUpdateConflict(TestChainSetup& setup) BlockValidationState state; CDeterministicMNList mn_list; BOOST_CHECK(!chainman.ActiveChainstate().ChainHelper().special_tx->BuildNewListFromBlock( - hazard_block, tip_index(), chainman.ActiveChainstate().CoinsTip(), /*debugLogs=*/false, state, mn_list)); + hazard_block, tip_index(), IsV24Active(chainman), chainman.ActiveChainstate().CoinsTip(), + /*debugLogs=*/false, state, mn_list)); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-hash"); } @@ -1903,7 +1920,8 @@ void FuncMigrationRejectedWhenKeySquatted(TestChainV24SignalBeforeV19Setup& setu SignTransaction(tx, spent, setup.coinbaseKey); TxValidationState st; LOCK(cs_main); - BOOST_CHECK(!CheckProUpServTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, chainman, st, true)); + BOOST_CHECK(!CheckProUpServTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, chainman.GetConsensus(), + IsV24Active(chainman), st, true)); BOOST_CHECK_EQUAL(st.GetRejectReason(), "bad-protx-dup-key"); } @@ -1927,7 +1945,8 @@ void FuncMigrationRejectedWhenKeySquatted(TestChainV24SignalBeforeV19Setup& setu TxValidationState st; LOCK(cs_main); BOOST_CHECK(!CheckProUpRegTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, st, true)); + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), st, true)); BOOST_CHECK_EQUAL(st.GetRejectReason(), "bad-protx-dup-key"); } }; @@ -1957,8 +1976,9 @@ void FuncProUpServTxMigratesLegacy(TestChainV24SignalBeforeV19Setup& setup) { TxValidationState val_state; LOCK(cs_main); - BOOST_REQUIRE_MESSAGE(CheckProUpServTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, chainman, - val_state, /*check_sigs=*/true), + BOOST_REQUIRE_MESSAGE(CheckProUpServTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, + chainman.GetConsensus(), IsV24Active(chainman), val_state, + /*check_sigs=*/true), "migration ProUpServTx rejected: " << val_state.GetRejectReason()); } setup.ProcessBlock({tx}); @@ -2185,10 +2205,12 @@ void FuncSameMnSameBlockVersionCrossingKeyRotation(TestChainV24SignalBeforeV19Se LOCK(cs_main); TxValidationState s1, s2; BOOST_REQUIRE_MESSAGE(CheckProUpRegTx(CTransaction(tx1), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, s1, true), + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), s1, true), "tx1 rejected standalone: " << s1.GetRejectReason()); BOOST_REQUIRE_MESSAGE(CheckProUpRegTx(CTransaction(tx2), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, s2, true), + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), s2, true), "tx2 rejected standalone: " << s2.GetRejectReason()); } @@ -2206,7 +2228,7 @@ void FuncSameMnSameBlockVersionCrossingKeyRotation(TestChainV24SignalBeforeV19Se auto& chain_helper = *Assert(setup.m_node.chain_helper.get()); try { rebuilt = chain_helper.special_tx->RebuildListFromBlock( - block, chainman.ActiveChain().Tip(), dmnman.GetListAtChainTip(), + block, chainman.ActiveChain().Tip(), IsV24Active(chainman), dmnman.GetListAtChainTip(), chainman.ActiveChainstate().CoinsTip(), /*debugLogs=*/false, block_state, mn_list_ret); } catch (const std::exception& e) { thrown = e.what(); @@ -2291,10 +2313,12 @@ void FuncSameMnSameBlockMigrationConsistent(TestChainV24SignalBeforeV19Setup& se LOCK(cs_main); TxValidationState s1, s2; BOOST_REQUIRE_MESSAGE(CheckProUpRegTx(CTransaction(tx1), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, s1, true), + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), s1, true), "tx1 rejected standalone: " << s1.GetRejectReason()); BOOST_REQUIRE_MESSAGE(CheckProUpRegTx(CTransaction(tx2), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, s2, true), + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), s2, true), "tx2 rejected standalone: " << s2.GetRejectReason()); } @@ -2313,7 +2337,7 @@ void FuncSameMnSameBlockMigrationConsistent(TestChainV24SignalBeforeV19Setup& se auto& chain_helper = *Assert(setup.m_node.chain_helper.get()); BOOST_CHECK_NO_THROW( rebuilt = chain_helper.special_tx->RebuildListFromBlock( - block, chainman.ActiveChain().Tip(), dmnman.GetListAtChainTip(), + block, chainman.ActiveChain().Tip(), IsV24Active(chainman), dmnman.GetListAtChainTip(), chainman.ActiveChainstate().CoinsTip(), /*debugLogs=*/false, block_state, mn_list_ret)); } BOOST_REQUIRE_MESSAGE(rebuilt, "same-key migration across one block was rejected: " << block_state.GetRejectReason()); @@ -2493,10 +2517,12 @@ void FuncSameBlockCrossSchemeKeyPairRejected(TestChainV24SignalBeforeV19Setup& s LOCK(cs_main); TxValidationState s1, s2; BOOST_REQUIRE_MESSAGE(CheckProUpRegTx(CTransaction(tx1), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, s1, true), + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), s1, true), "tx1 rejected standalone: " << s1.GetRejectReason()); BOOST_REQUIRE_MESSAGE(CheckProUpRegTx(CTransaction(tx2), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, s2, true), + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), s2, true), "tx2 rejected standalone: " << s2.GetRejectReason()); } @@ -2518,7 +2544,7 @@ void FuncSameBlockCrossSchemeKeyPairRejected(TestChainV24SignalBeforeV19Setup& s auto& chain_helper = *Assert(setup.m_node.chain_helper.get()); BOOST_CHECK_NO_THROW( rebuilt = chain_helper.special_tx->RebuildListFromBlock( - block, chainman.ActiveChain().Tip(), dmnman.GetListAtChainTip(), + block, chainman.ActiveChain().Tip(), IsV24Active(chainman), dmnman.GetListAtChainTip(), chainman.ActiveChainstate().CoinsTip(), /*debugLogs=*/false, block_state, mn_list_ret)); } BOOST_CHECK_MESSAGE(!rebuilt, "a block claiming one operator key under two encodings was accepted"); @@ -2602,7 +2628,8 @@ void FuncMempoolRejectsCrossSchemeKeyRace(TestChainV24SignalBeforeV19Setup& setu TxValidationState st; LOCK(cs_main); BOOST_REQUIRE_MESSAGE(CheckProUpRegTx(CTransaction(tx_second), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, st, + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), st, /*check_sigs=*/true), "second claim should still pass its consensus check: " << st.GetRejectReason()); } @@ -2676,7 +2703,8 @@ void FuncProUpRegTxRejectsCrossSchemeKeyReuse(TestChainV24SignalBeforeV19Setup& auto check_upreg = [&](const CMutableTransaction& tx, TxValidationState& st) { LOCK(cs_main); return CheckProUpRegTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, st, /*check_sigs=*/true); + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), IsV24Active(chainman), + st, /*check_sigs=*/true); }; // MN-A tries to take MN-B's key under the OTHER encoding, via a v1 payload. This is the reverse @@ -2751,7 +2779,8 @@ void FuncProRegTxRejectsCrossSchemeKeyReuse(TestChainV24SignalBeforeV19Setup& se auto check_proreg = [&](const CMutableTransaction& tx, TxValidationState& st) { LOCK(cs_main); return CheckProRegTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, st, /*check_sigs=*/true); + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), IsV24Active(chainman), st, + /*check_sigs=*/true); }; setup.MineToV19(); @@ -2873,8 +2902,8 @@ void FuncPreV24BehaviourUnchanged(TestChainSetup& setup) { TxValidationState val_state; LOCK(cs_main); - BOOST_CHECK_MESSAGE(CheckProUpServTx(CTransaction(tx_ups), chainman.ActiveChain().Tip(), dmnman, chainman, - val_state, /*check_sigs=*/true), + BOOST_CHECK_MESSAGE(CheckProUpServTx(CTransaction(tx_ups), chainman.ActiveChain().Tip(), dmnman, + chainman.GetConsensus(), IsV24Active(chainman), val_state, /*check_sigs=*/true), "pre-v24 v2 ProUpServTx wrongly rejected: " << val_state.GetRejectReason()); } setup.CreateAndProcessBlock({tx_ups}, coinbase_pk); @@ -2901,7 +2930,8 @@ void FuncPreV24BehaviourUnchanged(TestChainSetup& setup) TxValidationState val_state; LOCK(cs_main); BOOST_CHECK_MESSAGE(CheckProUpRegTx(CTransaction(tx_upreg), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, val_state, + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), val_state, /*check_sigs=*/true), "pre-v24 same-key ProUpRegTx wrongly rejected: " << val_state.GetRejectReason()); } @@ -2968,7 +2998,8 @@ void FuncStaleSpecialTxDoesNotPoisonTemplate(TestChainV24SignalBeforeV19Setup& s TxValidationState val_state; LOCK(cs_main); BOOST_REQUIRE(!CheckProRegTx(CTransaction(tx_stale), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, val_state, + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), val_state, /*check_sigs=*/true)); BOOST_REQUIRE_EQUAL(val_state.GetRejectReason(), "bad-protx-dup-key"); } @@ -3023,7 +3054,8 @@ void FuncProUpRegTxMigratesLegacySameKey(TestChainV24SignalBeforeV19Setup& setup { LOCK(cs_main); BOOST_REQUIRE_MESSAGE(CheckProUpRegTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, val_state, + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), val_state, /*check_sigs=*/true), "same-key migration rejected: " << val_state.GetRejectReason()); } @@ -3052,7 +3084,8 @@ void FuncProUpRegTxMigratesLegacySameKey(TestChainV24SignalBeforeV19Setup& setup { LOCK(cs_main); BOOST_REQUIRE_MESSAGE(CheckProUpRegTx(CTransaction(tx), chainman.ActiveChain().Tip(), dmnman, - chainman.ActiveChainstate().CoinsTip(), chainman, val_state, + chainman.ActiveChainstate().CoinsTip(), chainman.GetConsensus(), + IsV24Active(chainman), val_state, /*check_sigs=*/true), "new-key rotation rejected: " << val_state.GetRejectReason()); } diff --git a/src/test/evo_mnhf_tests.cpp b/src/test/evo_mnhf_tests.cpp index 3db1f8c7c78e..dde064eec1c4 100644 --- a/src/test/evo_mnhf_tests.cpp +++ b/src/test/evo_mnhf_tests.cpp @@ -74,13 +74,13 @@ BOOST_AUTO_TEST_CASE(verify_mnhf_specialtx_tests) { // wrong quorum (we don't have any indeed) const CTransaction tx{CTransaction(CreateMNHFTx(hash, sig, bit))}; - CheckMNHFTx(*chainman, qman, CTransaction(tx), pindex, state); + CheckMNHFTx(chainman->m_blockman, qman, CTransaction(tx), pindex, state); BOOST_CHECK_EQUAL(state.ToString(), "bad-mnhf-quorum-hash"); } { // non EHF fork const CTransaction tx{CTransaction(CreateMNHFTx(hash, sig, 28))}; - CheckMNHFTx(*chainman, qman, CTransaction(tx), pindex, state); + CheckMNHFTx(chainman->m_blockman, qman, CTransaction(tx), pindex, state); BOOST_CHECK_EQUAL(state.ToString(), "bad-mnhf-non-ehf"); } } diff --git a/src/test/evo_trivialvalidation.cpp b/src/test/evo_trivialvalidation.cpp index 16184e0a584a..0f6772806a20 100644 --- a/src/test/evo_trivialvalidation.cpp +++ b/src/test/evo_trivialvalidation.cpp @@ -38,7 +38,9 @@ void TestTxHelper(const CMutableTransaction& tx, gsl::not_null& expected_error, const ChainstateManager& chainman) { TxValidationState dummy_state; - auto opt_payload = GetValidatedPayload(CTransaction{tx}, pindexPrev, chainman, dummy_state); + auto opt_payload = GetValidatedPayload(CTransaction{tx}, pindexPrev, chainman.GetConsensus(), + DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24), + dummy_state); BOOST_CHECK_EQUAL(opt_payload.has_value(), !expected_error.has_value()); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 060cb6517384..93eb7e1bb788 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -605,7 +605,10 @@ CBlock TestChainSetup::CreateBlock( Assert(cbTx.has_value()); BlockValidationState state; CDeterministicMNList mn_list; - if (!chainstate.ChainHelper().special_tx->BuildNewListFromBlock(block, chainstate.m_chain.Tip(), chainstate.CoinsTip(), true, state, mn_list)) { + const CBlockIndex* pindexPrev{chainstate.m_chain.Tip()}; + const bool is_v24_active{DeploymentActiveAfter(pindexPrev, chainstate.m_chainman, Consensus::DEPLOYMENT_V24)}; + if (!chainstate.ChainHelper().special_tx->BuildNewListFromBlock(block, pindexPrev, is_v24_active, + chainstate.CoinsTip(), true, state, mn_list)) { Assert(false); } if (!CalcCbTxMerkleRootMNList(cbTx->merkleRootMNList, mn_list.to_sml(), state)) { diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 50a8f8dc1145..898e27aea66c 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -64,8 +64,8 @@ static void DashChainstateSetup(ChainstateManager& chainman, // Initialize chain_helper node.chain_helper.reset(); node.chain_helper = std::make_unique(*node.evodb, *node.dmnman, *Assert(node.mn_sync), *Assert(node.isman), *(node.llmq_ctx->quorum_block_processor), - *(node.llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), *Assert(node.chainlocks), - *(node.llmq_ctx->qman)); + *(node.llmq_ctx->qsnapman), chainman, chainman.m_blockman, chainman.GetConsensus(), + *Assert(node.chainlocks), *(node.llmq_ctx->qman)); } static void DashChainstateSetupClose(node::NodeContext& node) diff --git a/src/validation.cpp b/src/validation.cpp index 54322912d356..053fe8f4bcc1 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -996,7 +996,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // DoS scoring a node for non-critical errors, e.g. duplicate keys because a TX is received that was already // mined // NOTE: we use UTXO here and do NOT allow mempool txes as masternode collaterals - if (!m_chain_helper.special_tx->CheckSpecialTx(tx, m_active_chainstate.m_chain.Tip(), m_active_chainstate.CoinsTip(), true, state)) + const CBlockIndex* tip{m_active_chainstate.m_chain.Tip()}; + const bool is_v24_active{DeploymentActiveAfter(tip, m_active_chainstate.m_chainman, Consensus::DEPLOYMENT_V24)}; + if (!m_chain_helper.special_tx->CheckSpecialTx(tx, tip, is_v24_active, m_active_chainstate.CoinsTip(), true, state)) return false; if (m_pool.existsProviderTxConflict(tx)) { @@ -2032,8 +2034,8 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn return DISCONNECT_FAILED; } - std::optional mnlist_updates_opt{std::nullopt}; - if (!m_chain_helper->special_tx->UndoSpecialTxsInBlock(*this, block, pindex, mnlist_updates_opt)) { + MNListUpdates mnlist_updates; + if (!m_chain_helper->special_tx->UndoSpecialTxsInBlock(*this, block, pindex, mnlist_updates)) { error("DisconnectBlock(): UndoSpecialTxsInBlock failed"); return DISCONNECT_FAILED; } @@ -2091,10 +2093,9 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn view.SetBestBlock(pindex->pprev->GetBlockHash()); m_evoDb.WriteBestBlock(EvoDbIdentity(), pindex->pprev->GetBlockHash()); - if (this == &m_chainman.ActiveChainstate() && mnlist_updates_opt.has_value()) { - auto& mnlu = mnlist_updates_opt.value(); - GetMainSignals().NotifyMasternodeListChanged(true, mnlu.old_list, mnlu.diff); - uiInterface.NotifyMasternodeListChanged(mnlu.new_list, pindex->pprev); + if (this == &m_chainman.ActiveChainstate() && mnlist_updates.diff.HasChanges()) { + GetMainSignals().NotifyMasternodeListChanged(true, mnlist_updates.old_list, mnlist_updates.diff); + uiInterface.NotifyMasternodeListChanged(mnlist_updates.new_list, pindex->pprev); } auto finish = Now(); @@ -2381,12 +2382,24 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, blockundo.vtxundo.reserve(block.vtx.size() - 1); bool fDIP0001Active_context = DeploymentActiveAt(*pindex, params.GetConsensus(), Consensus::DEPLOYMENT_DIP0001); + const CAmount blockSubsidy = GetBlockSubsidy(pindex, params.GetConsensus()); + const bool is_v24_active{DeploymentActiveAfter(pindex->pprev, m_chainman, Consensus::DEPLOYMENT_V24)}; + // MUST process special txes before updating UTXO to ensure consistency between mempool and block processing - std::optional mnlist_updates_opt{std::nullopt}; - if (!m_chain_helper->special_tx->ProcessSpecialTxsInBlock(*this, block, pindex, view, fJustCheck, fScriptChecks, state, mnlist_updates_opt)) { + MNListUpdates mnlist_updates; + if (!m_chain_helper->special_tx->ProcessSpecialTxsInBlock(*this, m_chain, block, pindex, is_v24_active, view, blockSubsidy, fJustCheck, fScriptChecks, state, mnlist_updates)) { return error("ConnectBlock(DASH): ProcessSpecialTxsInBlock for block %s failed with %s", pindex->GetBlockHash().ToString(), state.ToString()); } + if (!fJustCheck) { + // Persist the list produced by this chainstate's own connection of + // the snapshot base block (no-op for every other block). Snapshot + // activation may populate the shared MN-list cache with seeded + // state, so completion must not reconstruct this value through that + // cache. Before DIP3 activates, new_list is the independently + // computed empty list. + RecordBackgroundMNListHash(pindex, mnlist_updates.new_list); + } const auto time_2_1{SteadyClock::now()}; time_process_special += time_2_1 - time_2; @@ -2526,7 +2539,6 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, // DASH : MODIFIED TO CHECK MASTERNODE PAYMENTS AND SUPERBLOCKS // TODO: resync data (both ways?) and try to reprocess this block later. - CAmount blockSubsidy = GetBlockSubsidy(pindex, params.GetConsensus()); CAmount feeReward = nFees; std::string strError; @@ -2537,7 +2549,6 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, Ticks(time_subsidy), Ticks(time_subsidy) / num_blocks_total); - const bool is_v24_active{DeploymentActiveAfter(pindex->pprev, m_chainman, Consensus::DEPLOYMENT_V24)}; const SuperBlockCheckType check_superblock = !m_chain_helper->IsSuperblockValidationRequired(pindex) ? SuperBlockCheckType::NoCheck : is_v24_active ? SuperBlockCheckType::DisallowDuplicates : SuperBlockCheckType::AllowDuplicates; @@ -2614,10 +2625,9 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, // Block is committed: keep the scheme it switched to (fJustCheck dry runs returned above). bls_scheme_guard.Commit(); - if (this == &m_chainman.ActiveChainstate() && mnlist_updates_opt.has_value()) { - const auto& mnlu = mnlist_updates_opt.value(); - GetMainSignals().NotifyMasternodeListChanged(false, mnlu.old_list, mnlu.diff); - uiInterface.NotifyMasternodeListChanged(mnlu.new_list, pindex); + if (this == &m_chainman.ActiveChainstate() && mnlist_updates.diff.HasChanges()) { + GetMainSignals().NotifyMasternodeListChanged(false, mnlist_updates.old_list, mnlist_updates.diff); + uiInterface.NotifyMasternodeListChanged(mnlist_updates.new_list, pindex); } ::g_stats_client->timing("ConnectBlock_ms", Ticks(time_8 - time_start), 1.0f); @@ -4846,11 +4856,14 @@ bool Chainstate::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& in // MUST process special txes before updating UTXO to ensure consistency between mempool and block processing BlockValidationState state; - std::optional mnlist_updates_opt{std::nullopt}; - if (!m_chain_helper->special_tx->ProcessSpecialTxsInBlock(*this, block, pindex, inputs, false /*fJustCheck*/, false /*fScriptChecks*/, state, mnlist_updates_opt)) { + const CAmount blockSubsidy = GetBlockSubsidy(pindex, m_chainman.GetConsensus()); + const bool is_v24_active{DeploymentActiveAfter(pindex->pprev, m_chainman, Consensus::DEPLOYMENT_V24)}; + MNListUpdates mnlist_updates; + if (!m_chain_helper->special_tx->ProcessSpecialTxsInBlock(*this, m_chain, block, pindex, is_v24_active, inputs, blockSubsidy, /*fJustCheck=*/false, /*fCheckCbTxMerkleRoots=*/false, state, mnlist_updates)) { return error("RollforwardBlock(DASH): ProcessSpecialTxsInBlock for block %s failed with %s", pindex->GetBlockHash().ToString(), state.ToString()); } + RecordBackgroundMNListHash(pindex, mnlist_updates.new_list); for (size_t i = 0; i < block.vtx.size(); i++) { const CTransactionRef& tx = block.vtx[i]; diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index 6aa244c901a5..21560ed916ab 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -39,11 +39,12 @@ "consensus/tx_verify -> evo/assetlocktx -> llmq/commitment -> validation -> txmempool -> consensus/tx_verify", "evo/assetlocktx -> llmq/commitment -> validation -> txmempool -> evo/assetlocktx", "evo/chainhelper -> evo/creditpool -> validation -> evo/chainhelper", + "evo/creditpool -> validation -> evo/specialtxman -> evo/creditpool", "evo/deterministicmns -> node/blockstorage -> validation -> evo/deterministicmns", "evo/deterministicmns -> node/blockstorage -> validation -> masternode/payments -> evo/deterministicmns", "evo/deterministicmns -> node/blockstorage -> validation -> txmempool -> evo/deterministicmns", "evo/smldiff -> llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> evo/smldiff", - "evo/specialtxman -> validation -> evo/specialtxman", + "evo/specialtxman -> llmq/blockprocessor -> validation -> evo/specialtxman", "governance/superblock -> validation -> masternode/payments -> governance/superblock", "instantsend/instantsend -> node/blockstorage -> validation -> txmempool -> instantsend/instantsend", "llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor",