Skip to content

Commit 29e2d43

Browse files
Merge #6656: perf: use tiny cache for best chainlock in CbTx instead running heavy ReadBlockFromDisk
18044c9 perf: use tiny cache for best chainlock in CbTx instead running heavy ReadBlockFromDisk (Konstantin Akimov) 66c9592 refactor: use more strict scope v20 instead dip0003 (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented Validation of chainlocks requires to read previous block from disk for every new block. ## What was done? Added CL from CbTx of tip of the chain. ## How Has This Been Tested? develop: <img width="754" alt="image" src="https://github.com/user-attachments/assets/7e1a5ab4-9634-4fc7-910e-9124d2d742d0" /> ``` 2025-05-04T18:36:29Z [bench] - CheckCbTxBestChainlock: 3.34ms [18.68s] 2025-05-04T18:36:29Z [bench] - Connect total: 54.40ms [94.48s (16.14ms/blk)] ``` PR: <img width="754" alt="image" src="https://github.com/user-attachments/assets/462df0f1-358d-46dd-8a08-f3a0324bb282" /> ``` 2025-05-04T18:53:18Z [bench] - CheckCbTxBestChainlock: 3.15ms [16.41s] 2025-05-04T18:53:18Z [bench] - Connect total: 52.89ms [90.75s (15.51ms/blk)] ``` `perf` looks a bit confusing (like cpu spent for CheckCbTxBestChainlock is close to zero which contradicts to logs time (16seconds vs 18seconds). It happened due to missing data for bls / gmp - somehow they are not attached to the caller, but shown as a root nodes without proper stacktrace). Detailed break-down is: ``` 2025-05-04T19:50:37Z [bench] - CheckCbTxBestChainlock::payload: 0.22ms [1.34s] 2025-05-04T19:50:37Z [bench] - CheckCbTxBestChainlock::get-cb-cl: 0.52ms [2.50s] 2025-05-04T19:50:37Z [bench] - CheckCbTxBestChainlock::validate: 2.63ms [14.94s] <--- missing in perf 2025-05-04T19:50:37Z [bench] - CheckCbTxBestChainlock: 3.38ms [18.87s] ``` ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone ACKs for top commit: UdjinM6: utACK 18044c9 PastaPastaPasta: utACK 18044c9 Tree-SHA512: 21508505e4b3cec9cba7f3835e2b73a81461d7b626f97efb15d442767fa0862f6ba92ed1101e00b04dbbddab1b7c56da796b15c89d80c95ce9b0246bbbbe02f2
2 parents 8732326 + 18044c9 commit 29e2d43

File tree

4 files changed

+27
-11
lines changed

4 files changed

+27
-11
lines changed

src/chainparams.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,7 @@ class CRegTestParams : public CChainParams {
921921
UpdateLLMQTestParametersFromArgs(args, Consensus::LLMQType::LLMQ_TEST_INSTANTSEND);
922922
UpdateLLMQTestParametersFromArgs(args, Consensus::LLMQType::LLMQ_TEST_PLATFORM);
923923
UpdateLLMQInstantSendDIP0024FromArgs(args);
924+
assert(consensus.V20Height >= consensus.DIP0003Height);
924925
}
925926

926927
/**

src/evo/cbtx.cpp

+24-10
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,26 @@ bool CheckCbTxBestChainlock(const CBlock& block, const CBlockIndex* pindex,
331331
return true;
332332
}
333333

334+
static Mutex cached_mutex;
335+
static const CBlockIndex* cached_pindex GUARDED_BY(cached_mutex){nullptr};
336+
static std::optional<std::pair<CBLSSignature, uint32_t>> cached_chainlock GUARDED_BY(cached_mutex){std::nullopt};
337+
334338
auto best_clsig = chainlock_handler.GetBestChainLock();
335339
if (best_clsig.getHeight() == pindex->nHeight - 1 && cbTx.bestCLHeightDiff == 0 && cbTx.bestCLSignature == best_clsig.getSig()) {
336340
// matches our best clsig which still hold values for the previous block
341+
LOCK(cached_mutex);
342+
cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
343+
cached_pindex = pindex;
337344
return true;
338345
}
339346

340-
const auto prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindex->pprev);
347+
std::optional<std::pair<CBLSSignature, uint32_t>> prevBlockCoinbaseChainlock{std::nullopt};
348+
if (LOCK(cached_mutex); cached_pindex == pindex->pprev) {
349+
prevBlockCoinbaseChainlock = cached_chainlock;
350+
}
351+
if (!prevBlockCoinbaseChainlock.has_value()) {
352+
prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindex->pprev);
353+
}
341354
// If std::optional prevBlockCoinbaseChainlock is empty, then up to the previous block, coinbase Chainlock is null.
342355
if (prevBlockCoinbaseChainlock.has_value()) {
343356
// Previous block Coinbase has a non-null Chainlock: current block's Chainlock must be non-null and at least as new as the previous one
@@ -355,12 +368,18 @@ bool CheckCbTxBestChainlock(const CBlock& block, const CBlockIndex* pindex,
355368
int curBlockCoinbaseCLHeight = pindex->nHeight - static_cast<int>(cbTx.bestCLHeightDiff) - 1;
356369
if (best_clsig.getHeight() == curBlockCoinbaseCLHeight && best_clsig.getSig() == cbTx.bestCLSignature) {
357370
// matches our best (but outdated) clsig, no need to verify it again
371+
LOCK(cached_mutex);
372+
cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
373+
cached_pindex = pindex;
358374
return true;
359375
}
360376
uint256 curBlockCoinbaseCLBlockHash = pindex->GetAncestor(curBlockCoinbaseCLHeight)->GetBlockHash();
361377
if (chainlock_handler.VerifyChainLock(llmq::CChainLockSig(curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, cbTx.bestCLSignature)) != llmq::VerifyRecSigStatus::Valid) {
362378
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid-clsig");
363379
}
380+
LOCK(cached_mutex);
381+
cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
382+
cached_pindex = pindex;
364383
} else if (cbTx.bestCLHeightDiff != 0) {
365384
// Null bestCLSignature is allowed only with bestCLHeightDiff = 0
366385
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-cldiff");
@@ -437,8 +456,8 @@ std::optional<std::pair<CBLSSignature, uint32_t>> GetNonNullCoinbaseChainlock(co
437456
return std::nullopt;
438457
}
439458

440-
// There's no CbTx before DIP0003 activation
441-
if (!DeploymentActiveAt(*pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) {
459+
// There's no CL in CbTx before v20 activation
460+
if (!DeploymentActiveAt(*pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20)) {
442461
return std::nullopt;
443462
}
444463

@@ -454,14 +473,9 @@ std::optional<std::pair<CBLSSignature, uint32_t>> GetNonNullCoinbaseChainlock(co
454473
return std::nullopt;
455474
}
456475

457-
const CCbTx& cbtx = opt_cbtx.value();
458-
if (cbtx.nVersion < CCbTx::Version::CLSIG_AND_BALANCE) {
459-
return std::nullopt;
460-
}
461-
462-
if (!cbtx.bestCLSignature.IsValid()) {
476+
if (!opt_cbtx->bestCLSignature.IsValid()) {
463477
return std::nullopt;
464478
}
465479

466-
return std::make_pair(cbtx.bestCLSignature, cbtx.bestCLHeightDiff);
480+
return std::make_pair(opt_cbtx->bestCLSignature, opt_cbtx->bestCLHeightDiff);
467481
}

test/functional/feature_assumevalid.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class AssumeValidTest(BitcoinTestFramework):
6262
def set_test_params(self):
6363
self.setup_clean_chain = True
6464
self.num_nodes = 3
65-
self.extra_args = ["-dip3params=9000:9000", "-checkblockindex=0"]
65+
self.extra_args = ["-dip3params=9000:9000", '-testactivationheight=v20@9000', "-checkblockindex=0"]
6666
self.rpc_timeout = 120
6767

6868
def setup_network(self):

test/functional/feature_csv_activation.py

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def set_test_params(self):
100100
'-peertimeout=999999', # bump because mocktime might cause a disconnect otherwise
101101
102102
'-dip3params=2000:2000',
103+
'-testactivationheight=v20@2000',
103104
f'-testactivationheight=csv@{CSV_ACTIVATION_HEIGHT}',
104105
'-par=1', # Use only one script thread to get the exact reject reason for testing
105106
]]

0 commit comments

Comments
 (0)