diff --git a/src/chainlock/signing.cpp b/src/chainlock/signing.cpp index 9a63ee387ae2..ff31dc01637f 100644 --- a/src/chainlock/signing.cpp +++ b/src/chainlock/signing.cpp @@ -126,7 +126,7 @@ void ChainLockSigner::TrySignChainTip() // considered safe when it is islocked or at least known since 10 minutes (from mempool or block). These checks are // performed for the tip (which we try to sign) and the previous 5 blocks. If a ChainLocked block is found on the // way down, we consider all TXs to be safe. - if (m_isman.IsInstantSendEnabled() && m_isman.RejectConflictingBlocks()) { + if (m_isman.IsInstantSendEnabled()) { const auto* pindexWalk = pindex; while (pindexWalk != nullptr) { if (pindex->nHeight - pindexWalk->nHeight > TX_CONFIRM_THRESHOLD) { diff --git a/src/evo/chainhelper.cpp b/src/evo/chainhelper.cpp index 63c6fdafd2a7..a82c4a2bad8d 100644 --- a/src/evo/chainhelper.cpp +++ b/src/evo/chainhelper.cpp @@ -17,13 +17,13 @@ CChainstateHelper::CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmnman, CGovernanceManager& govman, llmq::CInstantSendManager& isman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, - const Consensus::Params& consensus_params, const CMasternodeSync& mn_sync, - const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman) : + const Consensus::Params& consensus_params, const chainlock::Chainlocks& chainlocks, + const llmq::CQuorumManager& qman) : isman{isman}, credit_pool_manager{std::make_unique(evodb, chainman)}, m_chainlocks{chainlocks}, ehf_manager{std::make_unique(evodb, chainman, qman)}, - mn_payments{std::make_unique(dmnman, govman, chainman, consensus_params, mn_sync)}, + mn_payments{std::make_unique(dmnman, govman, chainman, consensus_params)}, special_tx{std::make_unique(*credit_pool_manager, dmnman, *ehf_manager, qblockman, qsnapman, chainman, consensus_params, chainlocks, qman)} {} @@ -68,8 +68,6 @@ bool CChainstateHelper::RemoveConflictingISLockByTx(const CTransaction& tx) return true; } -bool CChainstateHelper::ShouldInstantSendRejectConflicts() const { return isman.RejectConflictingBlocks(); } - std::unordered_map CChainstateHelper::GetSignalsStage(const CBlockIndex* const pindexPrev) { return ehf_manager->GetSignalsStage(pindexPrev); diff --git a/src/evo/chainhelper.h b/src/evo/chainhelper.h index ec129d30df2b..aa27db154774 100644 --- a/src/evo/chainhelper.h +++ b/src/evo/chainhelper.h @@ -15,7 +15,6 @@ class CDeterministicMNManager; class CEvoDB; class CGovernanceManager; class ChainstateManager; -class CMasternodeSync; class CMNHFManager; class CMNPaymentsProcessor; class CSpecialTxProcessor; @@ -53,8 +52,8 @@ class CChainstateHelper explicit CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmnman, CGovernanceManager& govman, llmq::CInstantSendManager& isman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, - const Consensus::Params& consensus_params, const CMasternodeSync& mn_sync, - const chainlock::Chainlocks& chainlocks, const llmq::CQuorumManager& qman); + const Consensus::Params& consensus_params, const chainlock::Chainlocks& chainlocks, + const llmq::CQuorumManager& qman); ~CChainstateHelper(); /** Passthrough functions to chainlock::Chainlocks */ @@ -69,7 +68,6 @@ class CChainstateHelper std::optional> ConflictingISLockIfAny(const CTransaction& tx) const; bool IsInstantSendWaitingForTx(const uint256& hash) const; bool RemoveConflictingISLockByTx(const CTransaction& tx); - bool ShouldInstantSendRejectConflicts() const; std::unordered_map GetSignalsStage(const CBlockIndex* const pindexPrev); }; diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 0123bba56b2b..761c6e9e130b 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -97,6 +97,8 @@ bool CGovernanceManager::LoadCache(bool load_cache) return is_valid; } +bool CGovernanceManager::IsValidAndSynced() const { return is_valid && m_mn_sync.IsSynced(); } + void CGovernanceManager::RelayObject(const CGovernanceObject& obj) { AssertLockNotHeld(cs_relay); diff --git a/src/governance/governance.h b/src/governance/governance.h index cb43b3bbd452..f8d13599ed36 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -272,6 +272,7 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent // Basic initialization and querying bool IsValid() const override { return is_valid; } + bool IsValidAndSynced() const; bool LoadCache(bool load_cache) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional); diff --git a/src/init.cpp b/src/init.cpp index 113bd4c9f025..7bdcbf74527c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1992,7 +1992,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) chainman, *node.govman, *node.mn_metaman, - *node.mn_sync, *node.sporkman, *node.chainlocks, node.chain_helper, diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 29afd31f9dda..693558f5b752 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -15,11 +14,9 @@ using node::fImporting; using node::fReindex; namespace llmq { -CInstantSendManager::CInstantSendManager(CSporkManager& sporkman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params) : +CInstantSendManager::CInstantSendManager(CSporkManager& sporkman, const util::DbWrapperParams& db_params) : db{db_params}, - spork_manager{sporkman}, - m_mn_sync{mn_sync} + spork_manager{sporkman} { } @@ -469,14 +466,6 @@ bool CInstantSendManager::IsInstantSendEnabled() const return !fReindex && !fImporting && spork_manager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED); } -bool CInstantSendManager::RejectConflictingBlocks() const -{ - if (!m_mn_sync.IsBlockchainSynced()) { - return false; - } - return true; -} - Uint256HashMap CInstantSendManager::RemoveConfirmedInstantSendLocks(const CBlockIndex* pindex) { int nUntilHeight = pindex->nHeight; diff --git a/src/instantsend/instantsend.h b/src/instantsend/instantsend.h index a9667ab4384c..527c7da8060d 100644 --- a/src/instantsend/instantsend.h +++ b/src/instantsend/instantsend.h @@ -20,7 +20,6 @@ class CBlockIndex; class CDataStream; -class CMasternodeSync; class CSporkManager; namespace Consensus { struct LLMQParams; @@ -54,7 +53,6 @@ class CInstantSendManager private: instantsend::CInstantSendDb db; CSporkManager& spork_manager; - const CMasternodeSync& m_mn_sync; mutable Mutex cs_pendingLocks; // Incoming and not verified yet @@ -90,8 +88,7 @@ class CInstantSendManager CInstantSendManager() = delete; CInstantSendManager(const CInstantSendManager&) = delete; CInstantSendManager& operator=(const CInstantSendManager&) = delete; - explicit CInstantSendManager(CSporkManager& sporkman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params); + explicit CInstantSendManager(CSporkManager& sporkman, const util::DbWrapperParams& db_params); ~CInstantSendManager(); void AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined) @@ -154,12 +151,6 @@ class CInstantSendManager int GetTipHeight() const EXCLUSIVE_LOCKS_REQUIRED(!cs_height_cache); bool IsInstantSendEnabled() const; - /** - * If true, MN should sign all transactions, if false, MN should not sign - * transactions in mempool, but should sign txes included in a block. This - * allows ChainLocks to continue even while this spork is disabled. - */ - bool RejectConflictingBlocks() const; Uint256HashMap RemoveConfirmedInstantSendLocks(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_nonLocked, !cs_pendingRetry); }; diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index 3e24e34d01fa..3d28a364fa5b 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -13,9 +13,8 @@ #include LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, - ChainstateManager& chainman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params, int8_t bls_threads, int16_t worker_count, - int64_t max_recsigs_age) : + ChainstateManager& chainman, const util::DbWrapperParams& db_params, int8_t bls_threads, + int16_t worker_count, int64_t max_recsigs_age) : bls_worker{std::make_shared()}, qsnapman{std::make_unique(evo_db)}, quorum_block_processor{std::make_unique(chainman.ActiveChainstate(), dmnman, evo_db, @@ -23,7 +22,7 @@ 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)}, - isman{std::make_unique(sporkman, mn_sync, db_params)} + isman{std::make_unique(sporkman, db_params)} { // Have to start it early to let VerifyDB check ChainLock signatures in coinbase bls_worker->Start(worker_count); diff --git a/src/llmq/context.h b/src/llmq/context.h index 4f3e5e984e3f..8ffc38dfa529 100644 --- a/src/llmq/context.h +++ b/src/llmq/context.h @@ -13,7 +13,6 @@ class CBLSWorker; class ChainstateManager; class CDeterministicMNManager; class CEvoDB; -class CMasternodeSync; class CSporkManager; class PeerManager; @@ -34,9 +33,8 @@ struct LLMQContext { LLMQContext(const LLMQContext&) = delete; LLMQContext& operator=(const LLMQContext&) = delete; explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, - ChainstateManager& chainman, const CMasternodeSync& mn_sync, - const util::DbWrapperParams& db_params, int8_t bls_threads, int16_t worker_count, - int64_t max_recsigs_age); + ChainstateManager& chainman, const util::DbWrapperParams& db_params, int8_t bls_threads, + int16_t worker_count, int64_t max_recsigs_age); ~LLMQContext(); /** Guaranteed if LLMQContext is initialized then all members are valid too diff --git a/src/masternode/payments.cpp b/src/masternode/payments.cpp index 01a379b1d1b8..bb1169121f50 100644 --- a/src/masternode/payments.cpp +++ b/src/masternode/payments.cpp @@ -160,7 +160,7 @@ CAmount PlatformShare(const CAmount reward) int nOffset = nBlockHeight % m_consensus_params.nBudgetPaymentsCycleBlocks; if (nOffset < m_consensus_params.nBudgetPaymentsWindowBlocks) { // NOTE: old budget system is disabled since 12.1 - if(m_mn_sync.IsSynced()) { + if (m_govman.IsValidAndSynced()) { // no old budget blocks should be accepted here on mainnet, // testnet/devnet/regtest should produce regular blocks only LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- WARNING! Client synced but old budget system is disabled, checking block value against block reward\n", __func__); @@ -232,7 +232,7 @@ bool CMNPaymentsProcessor::IsBlockValueValid(const CBlock& block, const int nBlo return false; } - if (!m_mn_sync.IsSynced() || !m_govman.IsValid()) { + if (!m_govman.IsValidAndSynced()) { LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- WARNING! Not enough data, checked superblock max bounds only\n", __func__); // not enough data for full checks but at least we know that the superblock limits were honored. // We rely on the network to have followed the correct chain in this case @@ -280,7 +280,7 @@ bool CMNPaymentsProcessor::IsBlockPayeeValid(const CTransaction& txNew, const CB return false; } - if (!m_mn_sync.IsSynced() || !m_govman.IsValid()) { + if (!m_govman.IsValidAndSynced()) { // governance data is either incomplete or non-existent LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- WARNING! Not enough data, skipping superblock payee checks\n", __func__); return true; // not an error diff --git a/src/masternode/payments.h b/src/masternode/payments.h index 358d58d00d44..836e4ad83ad4 100644 --- a/src/masternode/payments.h +++ b/src/masternode/payments.h @@ -15,7 +15,6 @@ class CBlockIndex; class CDeterministicMNManager; class CGovernanceManager; class ChainstateManager; -class CMasternodeSync; class CTransaction; class CTxOut; @@ -36,7 +35,6 @@ class CMNPaymentsProcessor CGovernanceManager& m_govman; const ChainstateManager& m_chainman; const Consensus::Params& m_consensus_params; - const CMasternodeSync& m_mn_sync; private: [[nodiscard]] bool GetBlockTxOuts(const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, @@ -48,9 +46,14 @@ class CMNPaymentsProcessor [[nodiscard]] bool IsOldBudgetBlockValueValid(const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet); public: - explicit CMNPaymentsProcessor(CDeterministicMNManager& dmnman, CGovernanceManager& govman, const ChainstateManager& chainman, - const Consensus::Params& consensus_params, const CMasternodeSync& mn_sync) : - m_dmnman{dmnman}, m_govman{govman}, m_chainman{chainman}, m_consensus_params{consensus_params}, m_mn_sync{mn_sync} {} + explicit CMNPaymentsProcessor(CDeterministicMNManager& dmnman, CGovernanceManager& govman, + const ChainstateManager& chainman, const Consensus::Params& consensus_params) : + m_dmnman{dmnman}, + m_govman{govman}, + m_chainman{chainman}, + m_consensus_params{consensus_params} + { + } bool IsBlockValueValid(const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet, const bool check_superblock); bool IsBlockPayeeValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, const bool check_superblock); diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index 2b69c71cb47d..e367a24e3e27 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -9,6 +9,11 @@ #include #include +#include + +void NullNodeSyncNotifier::SyncReset() { assert(false); } +void NullNodeSyncNotifier::SyncFinished() { assert(false); } + CMasternodeSync::CMasternodeSync(std::unique_ptr&& sync_notifier) : nTimeAssetSyncStarted{GetTime()}, nTimeLastBumped{GetTime()}, diff --git a/src/masternode/sync.h b/src/masternode/sync.h index 247d8d0ff889..98401982727e 100644 --- a/src/masternode/sync.h +++ b/src/masternode/sync.h @@ -32,6 +32,20 @@ class NodeSyncNotifier virtual ~NodeSyncNotifier() = default; }; +/** Stub implementation for use in chainstate-only (non-network) contexts. + * CMasternodeSync constructed with this notifier permanently returns + * IsBlockchainSynced()=false and IsSynced()=false, which correctly disables + * network-dependent validation paths. + * + * Asserts on any call — if sync state is being advanced, a real notifier + * (NodeSyncNotifierImpl) must be used instead. */ +class NullNodeSyncNotifier final : public NodeSyncNotifier +{ +public: + void SyncReset() override; + void SyncFinished() override; +}; + // // CMasternodeSync : Sync masternode assets in stages // diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 289720d0949f..df83d41c6e5c 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -37,7 +37,6 @@ std::optional LoadChainstate(bool fReset, ChainstateManager& chainman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CMasternodeSync& mn_sync, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, @@ -83,7 +82,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, chainlocks, chain_helper, + DashChainstateSetup(chainman, govman, mn_metaman, sporkman, chainlocks, chain_helper, dmnman, *evodb, llmq_ctx, mempool, data_dir, dash_dbs_in_memory, /*llmq_dbs_wipe=*/fReset || fReindexChainState, bls_threads, worker_count, max_recsigs_age, consensus_params); @@ -209,7 +208,6 @@ std::optional LoadChainstate(bool fReset, void DashChainstateSetup(ChainstateManager& chainman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CMasternodeSync& mn_sync, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, @@ -230,13 +228,13 @@ void DashChainstateSetup(ChainstateManager& chainman, dmnman = std::make_unique(evodb, mn_metaman); llmq_ctx.reset(); - llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainman, mn_sync, + llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainman, util::DbWrapperParams{.path = data_dir, .memory = llmq_dbs_in_memory, .wipe = llmq_dbs_wipe}, bls_threads, worker_count, max_recsigs_age); mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); chain_helper.reset(); chain_helper = std::make_unique(evodb, *dmnman, govman, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), - *(llmq_ctx->qsnapman), chainman, consensus_params, mn_sync, chainlocks, + *(llmq_ctx->qsnapman), chainman, consensus_params, chainlocks, *(llmq_ctx->qman)); } diff --git a/src/node/chainstate.h b/src/node/chainstate.h index 09adc7cdf214..ac6b3f9235da 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -17,7 +17,6 @@ class CEvoDB; class CGovernanceManager; class ChainstateManager; class CMasternodeMetaMan; -class CMasternodeSync; class CSporkManager; class CTxMemPool; struct LLMQContext; @@ -80,7 +79,6 @@ std::optional LoadChainstate(bool fReset, ChainstateManager& chainman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CMasternodeSync& mn_sync, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, @@ -111,7 +109,6 @@ std::optional LoadChainstate(bool fReset, void DashChainstateSetup(ChainstateManager& chainman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CMasternodeSync& mn_sync, CSporkManager& sporkman, chainlock::Chainlocks& chainlocks, std::unique_ptr& chain_helper, diff --git a/src/node/miner.cpp b/src/node/miner.cpp index d93c320eb195..8d370907a788 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -378,7 +378,7 @@ bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& packa } const auto& txid = it->GetTx().GetHash(); - if (!m_isman.RejectConflictingBlocks() || !m_isman.IsInstantSendEnabled() || m_isman.IsLocked(txid)) { + if (!m_isman.IsInstantSendEnabled() || m_isman.IsLocked(txid)) { continue; } diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 2ac2bc6d6698..1562c679c088 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,8 @@ struct MinerTestingSetup : public TestingSetup { } BlockAssembler AssemblerForTest(const CChainParams& params); }; + +static constexpr int WAIT_FOR_ISLOCK_TIMEOUT{601}; } // namespace miner_tests BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup) @@ -132,6 +135,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co uint256 hashHighFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(50000).Time(Now()).SpendsCoinbase(false).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hashParentTx, hashMediumFeeTx, hashHighFeeTx}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); std::unique_ptr pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 4U); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx); @@ -142,6 +147,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vin[0].prevout.hash = hashHighFeeTx; tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee uint256 hashFreeTx = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hashFreeTx}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(0).FromTx(tx)); size_t freeTxSize = GetVirtualTransactionSize(CTransaction(tx)); @@ -167,6 +174,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee hashLowFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hashLowFeeTx}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx); @@ -181,6 +190,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[1].nValue = 100000000; // 1BTC output uint256 hashFreeTx2 = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({tx.GetHash()}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); // This tx can't be mined by itself tx.vin[0].prevout.hash = hashFreeTx2; @@ -189,6 +200,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse; uint256 hashLowFeeTx2 = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({tx.GetHash()}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); // Verify that this tx isn't selected. @@ -202,6 +215,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vin[0].prevout.n = 1; tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({tx.GetHash()}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 9U); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2); @@ -235,8 +250,11 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C { tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); bool spendsCoinbase = i == 0; // only first tx spends coinbase // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails + m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } @@ -252,6 +270,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C hash = tx.GetHash(); bool spendsCoinbase = i == 0; // only first tx spends coinbase // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); tx.vin[0].prevout.hash = hash; } @@ -281,6 +301,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C // orphan in mempool, template creation fails hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); m_node.mempool->clear(); @@ -307,6 +329,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; tx.vout[0].nValue = 0; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); // give it a fee so it'll get mined m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(false).FromTx(tx)); // Should throw bad-cb-multiple @@ -322,6 +346,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(Now()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(Now()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); m_node.mempool->clear(); @@ -361,11 +387,15 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C CScript script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script)); hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << std::vector(script.begin(), script.end()); tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, pblocktemplate->block.nTime - WAIT_FOR_ISLOCK_TIMEOUT); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(false).FromTx(tx)); // Should throw block-validation-failed BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); @@ -382,6 +412,7 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C // non-final txs in mempool SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1); + int64_t mocked_time_for_is = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() - WAIT_FOR_ISLOCK_TIMEOUT; const int flags{LOCKTIME_VERIFY_SEQUENCE}; // height map std::vector prevheights; @@ -400,6 +431,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C tx.vout[0].scriptPubKey = CScript() << OP_1; tx.nLockTime = 0; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, mocked_time_for_is); m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(Now()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail @@ -414,6 +447,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1-m_node.chainman->ActiveChain()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block prevheights[0] = baseheight + 2; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, mocked_time_for_is); m_node.mempool->addUnchecked(entry.Time(Now()).FromTx(tx)); BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail @@ -437,6 +472,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C prevheights[0] = baseheight + 3; tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, mocked_time_for_is); m_node.mempool->addUnchecked(entry.Time(Now()).FromTx(tx)); BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass @@ -448,6 +485,8 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C prevheights.resize(1); prevheights[0] = baseheight + 4; hash = tx.GetHash(); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hash}, mocked_time_for_is); m_node.mempool->addUnchecked(entry.Time(Now()).FromTx(tx)); BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass @@ -541,6 +580,8 @@ void MinerTestingSetup::TestPrioritisedMining(const CChainParams& chainparams, c uint256 hashFreeGrandchild = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx)); + // Age transaction for InstantSend + m_node.clhandler->UpdateTxFirstSeenMap({hashMediumFeeTx, hashPrioritsedChild, hashParentTx, hashFreeParent, hashFreeChild, hashFreeGrandchild, hashFreePrioritisedTx}, std::chrono::duration_cast(Now().time_since_epoch()).count() - WAIT_FOR_ISLOCK_TIMEOUT); auto pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashFreeParent); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 8a96cb0c9ba4..13d11c42888a 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -144,7 +144,7 @@ void DashChainstateSetup(ChainstateManager& chainman, bool llmq_dbs_wipe, const Consensus::Params& consensus_params) { - DashChainstateSetup(chainman, *Assert(node.govman.get()), *Assert(node.mn_metaman.get()), *Assert(node.mn_sync.get()), + DashChainstateSetup(chainman, *Assert(node.govman.get()), *Assert(node.mn_metaman.get()), *Assert(node.sporkman.get()), *Assert(node.chainlocks), node.chain_helper, node.dmnman, *node.evodb, node.llmq_ctx, Assert(node.mempool.get()), node.args->GetDataDirNet(), llmq_dbs_in_memory, llmq_dbs_wipe, llmq::DEFAULT_BLSCHECK_THREADS, llmq::DEFAULT_WORKER_COUNT, llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE, @@ -320,7 +320,6 @@ void ChainTestingSetup::LoadVerifyActivateChainstate() chainman, *Assert(m_node.govman.get()), *Assert(m_node.mn_metaman.get()), - *Assert(m_node.mn_sync.get()), *Assert(m_node.sporkman.get()), *Assert(m_node.chainlocks.get()), m_node.chain_helper, diff --git a/src/validation.cpp b/src/validation.cpp index 2a16f339b631..d8ca123ace86 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2612,7 +2612,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, // DASH : CHECK TRANSACTIONS FOR INSTANTSEND - if (m_chain_helper->ShouldInstantSendRejectConflicts()) { + if (!IsInitialBlockDownload()) { // Require other nodes to comply, send them some data in case they are missing it. const bool has_chainlock = m_chain_helper->HasChainLock(pindex->nHeight, pindex->GetBlockHash()); for (const auto& tx : block.vtx) {