From 6277a71edea716c56c1a494136df7258616d100c Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 17 Apr 2026 23:25:23 +0700 Subject: [PATCH 1/2] refactor: inline CheckAssetLockUnlockTx function to creditpool --- src/evo/assetlocktx.cpp | 15 --------------- src/evo/assetlocktx.h | 1 - src/evo/creditpool.cpp | 8 ++------ 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/evo/assetlocktx.cpp b/src/evo/assetlocktx.cpp index dace5ae1113d..cba0bdc55534 100644 --- a/src/evo/assetlocktx.cpp +++ b/src/evo/assetlocktx.cpp @@ -23,21 +23,6 @@ using node::BlockManager; -/** - * Common code for Asset Lock and Asset Unlock - */ -bool CheckAssetLockUnlockTx(const BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, gsl::not_null pindexPrev, const std::optional& indexes, TxValidationState& state) -{ - switch (tx.nType) { - case TRANSACTION_ASSET_LOCK: - return CheckAssetLockTx(tx, state); - case TRANSACTION_ASSET_UNLOCK: - return CheckAssetUnlockTx(blockman, qman, tx, pindexPrev, indexes, state); - default: - return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-not-asset-locks-at-all"); - } -} - /** * Asset Lock Transaction */ diff --git a/src/evo/assetlocktx.h b/src/evo/assetlocktx.h index 0f3c688cd83c..6a00f605f2c0 100644 --- a/src/evo/assetlocktx.h +++ b/src/evo/assetlocktx.h @@ -156,7 +156,6 @@ class CAssetUnlockPayload bool CheckAssetLockTx(const CTransaction& tx, TxValidationState& state); bool CheckAssetUnlockTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, gsl::not_null pindexPrev, const std::optional& indexes, TxValidationState& state); -bool CheckAssetLockUnlockTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, gsl::not_null pindexPrev, const std::optional& indexes, TxValidationState& state); bool GetAssetUnlockFee(const CTransaction& tx, CAmount& txfee, TxValidationState& state); #endif // BITCOIN_EVO_ASSETLOCKTX_H diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index 287560d936ac..dc49629d1e9b 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -300,18 +300,14 @@ bool CCreditPoolDiff::Unlock(const CTransaction& tx, TxValidationState& state) bool CCreditPoolDiff::ProcessLockUnlockTransaction(const BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, TxValidationState& state) { if (!tx.IsSpecialTxVersion()) return true; - if (tx.nType != TRANSACTION_ASSET_LOCK && tx.nType != TRANSACTION_ASSET_UNLOCK) return true; - - if (!CheckAssetLockUnlockTx(blockman, qman, tx, pindexPrev, pool.indexes, state)) { - // pass the state returned by the function above - return false; - } try { switch (tx.nType) { case TRANSACTION_ASSET_LOCK: + if (!CheckAssetLockTx(tx, state)) return false; // pass the state set by CheckAssetLockTx return Lock(tx, state); case TRANSACTION_ASSET_UNLOCK: + if (!CheckAssetUnlockTx(blockman, qman, tx, pindexPrev, pool.indexes, state)) return false; // pass the state set by CheckAssetUnlockTx return Unlock(tx, state); default: return true; From 4476d18bce0c385820cf72a8efbc2944054808d3 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 18 Apr 2026 02:25:30 +0700 Subject: [PATCH 2/2] refactor: remove re-validation of asset lock transactions for existing blocks in the chain --- src/evo/creditpool.cpp | 12 ++++-------- src/evo/creditpool.h | 12 ++++-------- src/evo/specialtxman.cpp | 2 +- src/node/miner.cpp | 17 +++++++++++++++-- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index dc49629d1e9b..6faa197d9e59 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -22,7 +22,6 @@ #include #include -using node::BlockManager; using node::ReadBlockFromDisk; // Forward declaration to prevent a new circular dependencies through masternode/payments.h @@ -248,8 +247,7 @@ CCreditPoolManager::~CCreditPoolManager() = default; CCreditPoolDiff::CCreditPoolDiff(CCreditPool starter, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams, const CAmount blockSubsidy) : - pool(std::move(starter)), - pindexPrev(pindexPrev) + pool(std::move(starter)) { assert(pindexPrev); @@ -297,17 +295,15 @@ bool CCreditPoolDiff::Unlock(const CTransaction& tx, TxValidationState& state) return true; } -bool CCreditPoolDiff::ProcessLockUnlockTransaction(const BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, TxValidationState& state) +bool CCreditPoolDiff::ProcessLockUnlockTransaction(const CTransaction& tx, TxValidationState& state) { if (!tx.IsSpecialTxVersion()) return true; try { switch (tx.nType) { case TRANSACTION_ASSET_LOCK: - if (!CheckAssetLockTx(tx, state)) return false; // pass the state set by CheckAssetLockTx return Lock(tx, state); case TRANSACTION_ASSET_UNLOCK: - if (!CheckAssetUnlockTx(blockman, qman, tx, pindexPrev, pool.indexes, state)) return false; // pass the state set by CheckAssetUnlockTx return Unlock(tx, state); default: return true; @@ -318,7 +314,7 @@ bool CCreditPoolDiff::ProcessLockUnlockTransaction(const BlockManager& blockman, } } -std::optional GetCreditPoolDiffForBlock(CCreditPoolManager& cpoolman, const BlockManager& blockman, const llmq::CQuorumManager& qman, +std::optional GetCreditPoolDiffForBlock(CCreditPoolManager& cpoolman, const CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams, const CAmount blockSubsidy, BlockValidationState& state) { @@ -329,7 +325,7 @@ std::optional GetCreditPoolDiffForBlock(CCreditPoolManager& cpo for (size_t i = 1; i < block.vtx.size(); ++i) { const auto& tx = *block.vtx[i]; TxValidationState tx_state; - if (!creditPoolDiff.ProcessLockUnlockTransaction(blockman, qman, tx, tx_state)) { + if (!creditPoolDiff.ProcessLockUnlockTransaction(tx, tx_state)) { assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS); state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), strprintf("Process Lock/Unlock Transaction failed at Credit Pool (tx hash %s) %s", tx.GetHash().ToString(), tx_state.GetDebugMessage())); diff --git a/src/evo/creditpool.h b/src/evo/creditpool.h index e3eb066ecd75..fec44ab680cb 100644 --- a/src/evo/creditpool.h +++ b/src/evo/creditpool.h @@ -33,9 +33,6 @@ struct Params; namespace llmq { class CQuorumManager; } // namespace llmq -namespace node { -class BlockManager; -} // namespace node struct CCreditPool { CAmount locked{0}; @@ -70,16 +67,15 @@ struct CCreditPool { * limits should stay same and depends only on the previous block. */ class CCreditPoolDiff { -private: +public: const CCreditPool pool; +private: std::unordered_set newIndexes; CAmount sessionLocked{0}; CAmount sessionUnlocked{0}; CAmount platformReward{0}; - const CBlockIndex *pindexPrev{nullptr}; - public: explicit CCreditPoolDiff(CCreditPool starter, const CBlockIndex *pindexPrev, const Consensus::Params& consensusParams, @@ -90,7 +86,7 @@ class CCreditPoolDiff { * to change amount of credit pool * @return true if transaction can be included in this block */ - bool ProcessLockUnlockTransaction(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, TxValidationState& state); + bool ProcessLockUnlockTransaction(const CTransaction& tx, TxValidationState& state); /** * this function returns total amount of credits for the next block @@ -147,7 +143,7 @@ class CCreditPoolManager EXCLUSIVE_LOCKS_REQUIRED(!cache_mutex); }; -std::optional GetCreditPoolDiffForBlock(CCreditPoolManager& cpoolman, const node::BlockManager& blockman, const llmq::CQuorumManager& qman, +std::optional GetCreditPoolDiffForBlock(CCreditPoolManager& cpoolman, const CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams, const CAmount blockSubsidy, BlockValidationState& state); diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 515c1dd06dec..65b1e6c4e2c6 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -743,7 +743,7 @@ bool CSpecialTxProcessor::CheckCreditPoolDiffForBlock(const CBlock& block, const try { const CAmount blockSubsidy = GetBlockSubsidy(pindex, m_consensus_params); - const auto creditPoolDiff = GetCreditPoolDiffForBlock(m_cpoolman, m_chainman.m_blockman, m_qman, block, + const auto creditPoolDiff = GetCreditPoolDiffForBlock(m_cpoolman, block, pindex->pprev, m_consensus_params, blockSubsidy, state); if (!creditPoolDiff.has_value()) return false; diff --git a/src/node/miner.cpp b/src/node/miner.cpp index d93c320eb195..dd85e29b18c4 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -305,7 +305,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc LogPrintf("CreateNewBlock() h[%d] CbTx failed to find best CL. Inserting null CL\n", nHeight); } BlockValidationState state; - const auto creditPoolDiff = GetCreditPoolDiffForBlock(*m_chain_helper.credit_pool_manager, m_blockman, m_qman, *pblock, pindexPrev, chainparams.GetConsensus(), blockSubsidy, state); + const auto creditPoolDiff = GetCreditPoolDiffForBlock(*m_chain_helper.credit_pool_manager, *pblock, pindexPrev, chainparams.GetConsensus(), blockSubsidy, state); if (creditPoolDiff == std::nullopt) { throw std::runtime_error(strprintf("%s: GetCreditPoolDiffForBlock failed: %s", __func__, state.ToString())); } @@ -544,7 +544,20 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele // `state` is local here because used only to log info about this specific tx TxValidationState state; - if (!creditPoolDiff->ProcessLockUnlockTransaction(m_blockman, m_qman, iter->GetTx(), state)) { + if (iter->GetTx().IsSpecialTxVersion() && iter->GetTx().nType == TRANSACTION_ASSET_UNLOCK) { + // ASSET_UNLOCK transactions may expire after being added to mempool + // They should not be included to the block + if (!CheckAssetUnlockTx(m_blockman, m_qman, iter->GetTx(), pindexPrev, creditPoolDiff->pool.indexes, state)) { + if (fUsingModified) { + mapModifiedTx.get().erase(modit); + failedTx.insert(iter); + } + LogPrintf("%s: asset unlock tx %s is skipped due %s\n", + __func__, iter->GetTx().GetHash().ToString(), state.ToString()); + continue; + } + } + if (!creditPoolDiff->ProcessLockUnlockTransaction(iter->GetTx(), state)) { if (fUsingModified) { mapModifiedTx.get().erase(modit); failedTx.insert(iter);