From f7749db1e903b97fa575065cd217334ad5516390 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 5 May 2025 12:51:13 +0700 Subject: [PATCH 1/9] refactor: use helper GetDataFromUnlockTxes to get credit pool amount --- src/evo/creditpool.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index ce90cce4de6a..4a9ba86847f7 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -48,17 +48,23 @@ static bool GetDataFromUnlockTx(const CTransaction& tx, CAmount& toUnlock, uint6 namespace { struct UnlockDataPerBlock { + CAmount credit_pool{0}; CAmount unlocked{0}; std::unordered_set indexes; }; } // anonymous namespace // it throws exception if anything went wrong -static UnlockDataPerBlock GetDataFromUnlockTxes(const std::vector& vtx) +static UnlockDataPerBlock GetDataFromUnlockTxes(const CBlock& block) { UnlockDataPerBlock blockData; - for (CTransactionRef tx : vtx) { + const auto opt_cbTx = GetTxPayload(block.vtx[0]->vExtraPayload); + if (!opt_cbTx) { + throw std::runtime_error(strprintf("%s: failed-getcreditpool-cbtx-payload", __func__)); + } + blockData.credit_pool = opt_cbTx->creditPoolBalance; + for (CTransactionRef tx : block.vtx) { if (!tx->IsSpecialTxVersion() || tx->nType != TRANSACTION_ASSET_UNLOCK) continue; CAmount unlocked{0}; @@ -151,19 +157,12 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_nullGetBlockHash(), block_index->nHeight, emptyPool); return emptyPool; } - CAmount locked = [&, func=__func__]() { - const auto opt_cbTx = GetTxPayload(block->vtx[0]->vExtraPayload); - if (!opt_cbTx) { - throw std::runtime_error(strprintf("%s: failed-getcreditpool-cbtx-payload", func)); - } - return opt_cbTx->creditPoolBalance; - }(); // We use here sliding window with Params().CreditPoolPeriodBlocks to determine // current limits for asset unlock transactions. // Indexes should not be duplicated since genesis block, but the Unlock Amount // of withdrawal transaction is limited only by this window - UnlockDataPerBlock blockData = GetDataFromUnlockTxes(block->vtx); + const UnlockDataPerBlock blockData = GetDataFromUnlockTxes(*block); CRangesSet indexes{std::move(prev.indexes)}; if (std::any_of(blockData.indexes.begin(), blockData.indexes.end(), [&](const uint64_t index) { return !indexes.Add(index); })) { throw std::runtime_error(strprintf("%s: failed-getcreditpool-index-duplicated", __func__)); @@ -177,28 +176,28 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_null distant_block = GetBlockForCreditPool(distant_block_index, consensusParams); distant_block) { - distantUnlocked = GetDataFromUnlockTxes(distant_block->vtx).unlocked; + distantUnlocked = GetDataFromUnlockTxes(*distant_block).unlocked; } } - CAmount currentLimit = locked; + CAmount currentLimit = blockData.credit_pool; const CAmount latelyUnlocked = prev.latelyUnlocked + blockData.unlocked - distantUnlocked; if (DeploymentActiveAt(*block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_WITHDRAWALS)) { currentLimit = std::min(currentLimit, LimitAmountV22); } else { // Unlock limits in pre-v22 are max(100, min(.10 * assetlockpool, 1000)) inside window if (currentLimit + latelyUnlocked > LimitAmountLow) { - currentLimit = std::max(LimitAmountLow, locked / 10) - latelyUnlocked; + currentLimit = std::max(LimitAmountLow, blockData.credit_pool / 10) - latelyUnlocked; if (currentLimit < 0) currentLimit = 0; } currentLimit = std::min(currentLimit, LimitAmountHigh - latelyUnlocked); } - if (currentLimit != 0 || latelyUnlocked > 0 || locked > 0) { + if (currentLimit != 0 || latelyUnlocked > 0 || blockData.credit_pool > 0) { LogPrint(BCLog::CREDITPOOL, /* Continued */ "CCreditPoolManager: asset unlock limits on height: %d locked: %d.%08d limit: %d.%08d " "unlocked-in-window: %d.%08d\n", - block_index->nHeight, locked / COIN, locked % COIN, currentLimit / COIN, currentLimit % COIN, + block_index->nHeight, blockData.credit_pool / COIN, blockData.credit_pool % COIN, currentLimit / COIN, currentLimit % COIN, latelyUnlocked / COIN, latelyUnlocked % COIN); } @@ -207,7 +206,7 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_nullGetBlockHash(), block_index->nHeight, pool); return pool; From afd7f84c67e943a7d99075803e9ba2e57856b68c Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 5 May 2025 12:59:13 +0700 Subject: [PATCH 2/9] perf: use GetAncestor() to jump blocks back for CreditPool --- src/evo/creditpool.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index 4a9ba86847f7..f389330448fd 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -168,11 +167,7 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_nullpprev; - if (distant_block_index == nullptr) break; - } + const CBlockIndex* distant_block_index{block_index->GetAncestor(block_index->nHeight - Params().CreditPoolPeriodBlocks())}; CAmount distantUnlocked{0}; if (distant_block_index) { if (std::optional distant_block = GetBlockForCreditPool(distant_block_index, consensusParams); distant_block) { From e3d3783fe6bc22f89ede21ad836004c88cfdda18 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 5 May 2025 13:11:51 +0700 Subject: [PATCH 3/9] refactor: combine GetDataFromUnlockTxes and GetBlockForCreditPool to GetCreditDataFromBlock --- src/evo/creditpool.cpp | 56 ++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index f389330448fd..a95fb9fffceb 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -46,7 +46,7 @@ static bool GetDataFromUnlockTx(const CTransaction& tx, CAmount& toUnlock, uint6 } namespace { - struct UnlockDataPerBlock { + struct CreditPoolDataPerBlock { CAmount credit_pool{0}; CAmount unlocked{0}; std::unordered_set indexes; @@ -54,9 +54,26 @@ namespace { } // anonymous namespace // it throws exception if anything went wrong -static UnlockDataPerBlock GetDataFromUnlockTxes(const CBlock& block) +static std::optional GetCreditDataFromBlock(const gsl::not_null block_index, + const Consensus::Params& consensusParams) + { - UnlockDataPerBlock blockData; + // There's no CbTx before DIP0003 activation + if (!DeploymentActiveAt(*block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) { + return std::nullopt; + } + + CBlock block; + if (!ReadBlockFromDisk(block, block_index, consensusParams)) { + throw std::runtime_error("failed-getcbforblock-read"); + } + + if (block.vtx.empty() || block.vtx[0]->vExtraPayload.empty() || !block.vtx[0]->IsSpecialTxVersion()) { + LogPrintf("%s: ERROR: empty CbTx for CreditPool at height=%d\n", __func__, block_index->nHeight); + return std::nullopt; + } + + CreditPoolDataPerBlock blockData; const auto opt_cbTx = GetTxPayload(block.vtx[0]->vExtraPayload); if (!opt_cbTx) { @@ -70,7 +87,7 @@ static UnlockDataPerBlock GetDataFromUnlockTxes(const CBlock& block) TxValidationState tx_state; uint64_t index{0}; if (!GetDataFromUnlockTx(*tx, unlocked, index, tx_state)) { - throw std::runtime_error(strprintf("%s: CCreditPoolManager::GetDataFromUnlockTxes failed: %s", __func__, tx_state.ToString())); + throw std::runtime_error(strprintf("%s: GetDataFromUnlockTxfailed: %s", __func__, tx_state.ToString())); } blockData.unlocked += unlocked; blockData.indexes.insert(index); @@ -117,32 +134,11 @@ void CCreditPoolManager::AddToCache(const uint256& block_hash, int height, const } } -static std::optional GetBlockForCreditPool(const gsl::not_null block_index, - const Consensus::Params& consensusParams) -{ - // There's no CbTx before DIP0003 activation - if (!DeploymentActiveAt(*block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) { - return std::nullopt; - } - - CBlock block; - if (!ReadBlockFromDisk(block, block_index, consensusParams)) { - throw std::runtime_error("failed-getcbforblock-read"); - } - - if (block.vtx.empty() || block.vtx[0]->vExtraPayload.empty() || !block.vtx[0]->IsSpecialTxVersion()) { - LogPrintf("%s: ERROR: empty CbTx for CreditPool at height=%d\n", __func__, block_index->nHeight); - return std::nullopt; - } - - return block; -} - CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_null block_index, CCreditPool prev, const Consensus::Params& consensusParams) { - std::optional block = GetBlockForCreditPool(block_index, consensusParams); - if (!block) { + std::optional opt_block_data = GetCreditDataFromBlock(block_index, consensusParams); + if (!opt_block_data) { // If reading of previous block is not successfully, but // prev contains credit pool related data, something strange happened if (prev.locked != 0) { @@ -156,12 +152,12 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_nullGetBlockHash(), block_index->nHeight, emptyPool); return emptyPool; } + const CreditPoolDataPerBlock& blockData{*opt_block_data}; // We use here sliding window with Params().CreditPoolPeriodBlocks to determine // current limits for asset unlock transactions. // Indexes should not be duplicated since genesis block, but the Unlock Amount // of withdrawal transaction is limited only by this window - const UnlockDataPerBlock blockData = GetDataFromUnlockTxes(*block); CRangesSet indexes{std::move(prev.indexes)}; if (std::any_of(blockData.indexes.begin(), blockData.indexes.end(), [&](const uint64_t index) { return !indexes.Add(index); })) { throw std::runtime_error(strprintf("%s: failed-getcreditpool-index-duplicated", __func__)); @@ -170,8 +166,8 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_nullGetAncestor(block_index->nHeight - Params().CreditPoolPeriodBlocks())}; CAmount distantUnlocked{0}; if (distant_block_index) { - if (std::optional distant_block = GetBlockForCreditPool(distant_block_index, consensusParams); distant_block) { - distantUnlocked = GetDataFromUnlockTxes(*distant_block).unlocked; + if (std::optional distant_block{GetCreditDataFromBlock(distant_block_index, consensusParams)}; distant_block) { + distantUnlocked = distant_block->unlocked; } } From 17062708c578ff0223d4a4ff86bfb5e5b35d2847 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 5 May 2025 13:19:00 +0700 Subject: [PATCH 4/9] perf: cache block data for credit pool for calculation asset unlock limits --- src/evo/creditpool.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index a95fb9fffceb..07666d2c75ad 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -56,13 +56,23 @@ namespace { // it throws exception if anything went wrong static std::optional GetCreditDataFromBlock(const gsl::not_null block_index, const Consensus::Params& consensusParams) - { // There's no CbTx before DIP0003 activation if (!DeploymentActiveAt(*block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) { return std::nullopt; } + CreditPoolDataPerBlock blockData; + + static Mutex cache_mutex; + static unordered_lru_cache block_data_cache GUARDED_BY(cache_mutex) {576 * 2}; + { + LOCK(cache_mutex); + if (block_data_cache.get(block_index->GetBlockHash(), blockData)) { + return blockData; + } + } + CBlock block; if (!ReadBlockFromDisk(block, block_index, consensusParams)) { throw std::runtime_error("failed-getcbforblock-read"); @@ -73,13 +83,10 @@ static std::optional GetCreditDataFromBlock(const gsl::n return std::nullopt; } - CreditPoolDataPerBlock blockData; - const auto opt_cbTx = GetTxPayload(block.vtx[0]->vExtraPayload); - if (!opt_cbTx) { - throw std::runtime_error(strprintf("%s: failed-getcreditpool-cbtx-payload", __func__)); + if (const auto opt_cbTx = GetTxPayload(block.vtx[0]->vExtraPayload); opt_cbTx) { + blockData.credit_pool = opt_cbTx->creditPoolBalance; } - blockData.credit_pool = opt_cbTx->creditPoolBalance; for (CTransactionRef tx : block.vtx) { if (!tx->IsSpecialTxVersion() || tx->nType != TRANSACTION_ASSET_UNLOCK) continue; @@ -92,6 +99,9 @@ static std::optional GetCreditDataFromBlock(const gsl::n blockData.unlocked += unlocked; blockData.indexes.insert(index); } + + LOCK(cache_mutex); + block_data_cache.insert(block_index->GetBlockHash(), blockData); return blockData; } From 34e06ecfaaec7f5a31b61a9fce795a7cb53eef9f Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 5 May 2025 17:20:59 +0700 Subject: [PATCH 5/9] fmt: apply clang-format suggestions --- src/evo/creditpool.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index 07666d2c75ad..20206e399312 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -46,16 +46,16 @@ static bool GetDataFromUnlockTx(const CTransaction& tx, CAmount& toUnlock, uint6 } namespace { - struct CreditPoolDataPerBlock { - CAmount credit_pool{0}; - CAmount unlocked{0}; - std::unordered_set indexes; - }; +struct CreditPoolDataPerBlock { + CAmount credit_pool{0}; + CAmount unlocked{0}; + std::unordered_set indexes; +}; } // anonymous namespace // it throws exception if anything went wrong static std::optional GetCreditDataFromBlock(const gsl::not_null block_index, - const Consensus::Params& consensusParams) + const Consensus::Params& consensusParams) { // There's no CbTx before DIP0003 activation if (!DeploymentActiveAt(*block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) { @@ -65,7 +65,8 @@ static std::optional GetCreditDataFromBlock(const gsl::n CreditPoolDataPerBlock blockData; static Mutex cache_mutex; - static unordered_lru_cache block_data_cache GUARDED_BY(cache_mutex) {576 * 2}; + static unordered_lru_cache block_data_cache GUARDED_BY( + cache_mutex){576 * 2}; { LOCK(cache_mutex); if (block_data_cache.get(block_index->GetBlockHash(), blockData)) { @@ -173,10 +174,12 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_nullGetAncestor(block_index->nHeight - Params().CreditPoolPeriodBlocks())}; + const CBlockIndex* distant_block_index{ + block_index->GetAncestor(block_index->nHeight - Params().CreditPoolPeriodBlocks())}; CAmount distantUnlocked{0}; if (distant_block_index) { - if (std::optional distant_block{GetCreditDataFromBlock(distant_block_index, consensusParams)}; distant_block) { + if (std::optional distant_block{GetCreditDataFromBlock(distant_block_index, consensusParams)}; + distant_block) { distantUnlocked = distant_block->unlocked; } } @@ -198,8 +201,8 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_nullnHeight, blockData.credit_pool / COIN, blockData.credit_pool % COIN, currentLimit / COIN, currentLimit % COIN, - latelyUnlocked / COIN, latelyUnlocked % COIN); + block_index->nHeight, blockData.credit_pool / COIN, blockData.credit_pool % COIN, currentLimit / COIN, + currentLimit % COIN, latelyUnlocked / COIN, latelyUnlocked % COIN); } if (currentLimit < 0) { From 52ae3aa51745887ac97c8aa2556ec466a7866885 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 28 May 2025 02:36:01 +0700 Subject: [PATCH 6/9] refactor: use CreditPoolPeriodBlocks for block_data_cache Co-authored-by: UdjinM6 --- src/evo/creditpool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index 20206e399312..e3204ae0e2a8 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -66,7 +66,7 @@ static std::optional GetCreditDataFromBlock(const gsl::n static Mutex cache_mutex; static unordered_lru_cache block_data_cache GUARDED_BY( - cache_mutex){576 * 2}; + cache_mutex){static_cast(Params().CreditPoolPeriodBlocks()) * 2}; { LOCK(cache_mutex); if (block_data_cache.get(block_index->GetBlockHash(), blockData)) { From 9c5e451b75f6fc52e8efa861bc628d18446969ef Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 28 May 2025 02:36:30 +0700 Subject: [PATCH 7/9] fix: typo in error message for GetDataFromUnlockTx Co-authored-by: UdjinM6 --- src/evo/creditpool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index e3204ae0e2a8..a06621699db6 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -95,7 +95,7 @@ static std::optional GetCreditDataFromBlock(const gsl::n TxValidationState tx_state; uint64_t index{0}; if (!GetDataFromUnlockTx(*tx, unlocked, index, tx_state)) { - throw std::runtime_error(strprintf("%s: GetDataFromUnlockTxfailed: %s", __func__, tx_state.ToString())); + throw std::runtime_error(strprintf("%s: GetDataFromUnlockTx failed: %s", __func__, tx_state.ToString())); } blockData.unlocked += unlocked; blockData.indexes.insert(index); From 095b2d8a4e18b75b5d5075fd8d9ce71167345053 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 3 Jun 2025 01:51:06 +0700 Subject: [PATCH 8/9] refactor: use if statement feature Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> --- src/evo/creditpool.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index a06621699db6..4fa2ff69a590 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -67,11 +67,8 @@ static std::optional GetCreditDataFromBlock(const gsl::n static Mutex cache_mutex; static unordered_lru_cache block_data_cache GUARDED_BY( cache_mutex){static_cast(Params().CreditPoolPeriodBlocks()) * 2}; - { - LOCK(cache_mutex); - if (block_data_cache.get(block_index->GetBlockHash(), blockData)) { - return blockData; - } + if (LOCK(cache_mutex); block_data_cache.get(block_index->GetBlockHash(), blockData)) { + return blockData; } CBlock block; From 72cfb93ae022f5c15ca30d19407b5e9756de7913 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 3 Jun 2025 01:54:52 +0700 Subject: [PATCH 9/9] feat: bail out if GetTxPayload failed Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> --- src/evo/creditpool.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index 4fa2ff69a590..a94d05108f7f 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -84,6 +84,9 @@ static std::optional GetCreditDataFromBlock(const gsl::n if (const auto opt_cbTx = GetTxPayload(block.vtx[0]->vExtraPayload); opt_cbTx) { blockData.credit_pool = opt_cbTx->creditPoolBalance; + } else { + LogPrintf("%s: WARNING: No valid CbTx at height=%d\n", __func__, block_index->nHeight); + return std::nullopt; } for (CTransactionRef tx : block.vtx) { if (!tx->IsSpecialTxVersion() || tx->nType != TRANSACTION_ASSET_UNLOCK) continue;