Skip to content

Conversation

@knst
Copy link
Collaborator

@knst knst commented Jun 11, 2025

Issue being fixed or feature implemented

Coinbase Transaction (CCbTx) is parsed several times:

  • itself validation (during CheckCbTx)
  • for merkle roots (during CheckCbTxMerkleRoots)
  • for CL validation (during CheckCbTxBestChainlock)
  • for Credit Pool (during CheckCreditPoolDiffForBlock)

For pre-v20 blocks this parsing has been relatively cheap, but once V20 has been activated, each CbTx has CL signature and its deserialization become expensive due to heavy call of CBLSWrapper<bls::G2Element, 96ul, CBLSSignature>::Unserialize<CDataStream>(CDataStream&, bool).

What was done?

CCbTx is parsed only once and reused for all related checks.

This PR makes validation of blocks after v20 activation for ~6% faster.
Validation of pre-v20 block is expected to be improved insignificantly (<1% of total time).

How Has This Been Tested?

Invalidated + reconsidered ~8.5k blocks (~2 weeks) after v20 activation.

Develop:
image

[bench]         - Loop: 0.22ms [10.01s]
[bench]           - GetTxPayload: 0.21ms [2.16s]
[bench]         - CheckCbTxMerkleRoots: 3.23ms [28.46s]
[bench]         - CheckCbTxBestChainlock: 3.16ms [24.65s]
[bench]       - ProcessSpecialTxsInBlock: 8.15ms [101.19s (11.66ms/blk)]
[bench]       - CheckCreditPoolDiffForBlock: 0.22ms [2.98s (0.34ms/blk)]
[bench]     - Dash specific: 0.42ms [4.98s (0.57ms/blk)]
[bench]   - Connect total: 8.87ms [113.20s (13.04ms/blk)]
[bench] - Connect block: 9.26ms [116.51s (13.42ms/blk)]

PR:
image

[bench]       - GetTxPayload: 0.24ms [2.34s]
[bench]       - Loop: 0.02ms [7.89s]
[bench]       - CheckCreditPoolDiffForBlock: 0.01ms [0.76s]
[bench]       - CheckCbTxMerkleRoots: 3.17ms [26.50s]
[bench]       - CheckCbTxBestChainlock: 3.08ms [22.77s]
[bench]       - ProcessSpecialTxsInBlock: 8.02ms [98.70s (11.37ms/blk)]
[bench]     - Dash specific: 0.27ms [1.97s (0.23ms/blk)]
[bench]   - Connect total: 8.58ms [107.65s (12.40ms/blk)]
[bench] - Connect block: 8.92ms [110.97s (12.78ms/blk)]

Some insignificant lines (not relevant to PR) are removed from both screenshots and bench logs, only affected functions are shown.

Breaking Changes

N/A

Checklist:

  • 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
  • I have assigned this pull request to a milestone

@knst knst added this to the 23 milestone Jun 11, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 11, 2025

Walkthrough

The changes refactor several special transaction validation functions to accept a CCbTx (coinbase transaction payload) object directly, rather than extracting and validating it internally from a generic transaction or block. This update removes redundant payload extraction and type checks from functions such as CheckCbTx, CheckCbTxMerkleRoots, and CheckCbTxBestChainlock, shifting responsibility for providing a validated CCbTx to the caller. The ProcessSpecialTx and UndoSpecialTx methods are removed, and credit pool validation is centralized, with updated function signatures to operate on CCbTx. Timing instrumentation for payload extraction and credit pool checks is also adjusted throughout the codebase.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/evo/specialtxman.cpp (3)

133-136: Clarify the comment about transaction processing.

The comment "starts from the 2nd transaction" is misleading. The code only skips the first transaction if it's a coinbase transaction.

 for (size_t i = 0; i < block.vtx.size(); ++i) {
-    // we validated CCbTx above, starts from the 2nd transaction
+    // Skip coinbase transaction as we already validated its CCbTx above
     if (i == 0 && block.vtx[i]->nType == TRANSACTION_COINBASE) continue;

140-140: Fix typo in comment.

Minor grammatical correction needed.

-// At this moment CheckSpecialTx() may fail by 2 possible ways:
+// At this point CheckSpecialTx() may fail by 2 possible ways:

153-159: Simplify redundant condition check.

The condition fCheckCbTxMerkleRoots && block.vtx[0]->nType == TRANSACTION_COINBASE is redundant since opt_cbTx only has a value when both conditions are already true.

-if (fCheckCbTxMerkleRoots && block.vtx[0]->nType == TRANSACTION_COINBASE) {
+if (opt_cbTx.has_value()) {
     if (!CheckCreditPoolDiffForBlock(block, pindex, *opt_cbTx, state)) {
         return error("CSpecialTxProcessor: CheckCreditPoolDiffForBlock for block %s failed with %s",
                      pindex->GetBlockHash().ToString(), state.ToString());
     }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09aa42e and 0c7cfd9.

📒 Files selected for processing (5)
  • src/evo/cbtx.cpp (5 hunks)
  • src/evo/cbtx.h (1 hunks)
  • src/evo/specialtxman.cpp (5 hunks)
  • src/evo/specialtxman.h (2 hunks)
  • src/validation.cpp (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/validation.cpp (2)
src/evo/specialtxman.h (3)
  • block (68-69)
  • block (71-71)
  • block (75-76)
src/validation.h (20)
  • block (682-682)
  • block (683-683)
  • block (714-714)
  • block (755-755)
  • block (881-885)
  • block (1016-1016)
  • pindex (708-708)
  • pindex (753-753)
  • pindex (757-757)
  • state (185-187)
  • state (185-185)
  • state (646-649)
  • state (673-675)
  • state (686-686)
  • state (693-693)
  • state (698-698)
  • state (703-703)
  • state (750-750)
  • state (751-751)
  • state (760-760)
src/evo/specialtxman.cpp (4)
src/evo/specialtxman.h (4)
  • tx (66-66)
  • block (68-69)
  • block (71-71)
  • block (75-76)
src/evo/cbtx.h (3)
  • CheckCbTx (67-67)
  • CheckCbTxMerkleRoots (69-71)
  • CheckCbTxBestChainlock (77-78)
src/evo/cbtx.cpp (6)
  • CheckCbTx (22-45)
  • CheckCbTx (22-22)
  • CheckCbTxMerkleRoots (48-88)
  • CheckCbTxMerkleRoots (48-50)
  • CheckCbTxBestChainlock (290-352)
  • CheckCbTxBestChainlock (290-291)
src/validation.cpp (8)
  • pindex (2063-2063)
  • pindex (2063-2063)
  • pindex (2067-2073)
  • pindex (2067-2067)
  • pindex (2991-2998)
  • pindex (2991-2991)
  • GetBlockSubsidy (1429-1434)
  • GetBlockSubsidy (1429-1429)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build container / Build container
  • GitHub Check: Build slim container / Build container
🔇 Additional comments (11)
src/validation.cpp (2)

2506-2508: Timing update looks correct

The new nTime5_3 checkpoint is taken immediately after IsBlockValueValid() and the cumulative nTimeValueValid is updated and logged. Variable names and math are consistent with surrounding timing code.


2515-2517: Consistent follow-up timer

Same pattern applies here for nTimePayeeValid; implementation matches adjacent benchmark blocks.

src/evo/specialtxman.h (2)

16-16: LGTM!

Good addition of the forward declaration for CCbTx to support the updated method signature.


74-76: LGTM! Signature change aligns with the optimization goal.

The change from CAmount blockSubsidy to const CCbTx& cbTx parameter is appropriate as it allows the function to access the creditPoolBalance directly from the already-parsed coinbase transaction payload, avoiding redundant parsing.

src/evo/cbtx.h (1)

67-78: LGTM! API changes support single-parse optimization.

The signature changes for CheckCbTx, CheckCbTxMerkleRoots, and CheckCbTxBestChainlock to accept const CCbTx& directly are well-designed. This eliminates redundant payload extraction in each function and aligns perfectly with the PR's performance optimization goal.

src/evo/cbtx.cpp (1)

22-45: LGTM! Clean implementation of the single-parse optimization.

The removal of CCbTx extraction logic from CheckCbTx, CheckCbTxMerkleRoots, and CheckCbTxBestChainlock functions successfully eliminates redundant parsing operations. The functions now focus solely on validation logic, improving both performance and code clarity.

Also applies to: 48-88, 290-352

src/evo/specialtxman.cpp (5)

50-59: LGTM! Proper validation of coinbase transactions.

The addition of the TRANSACTION_COINBASE case correctly validates that the transaction is indeed a coinbase and properly extracts the CCbTx payload before validation.


101-125: LGTM! Core optimization implemented correctly.

This is the key performance improvement - extracting the CCbTx payload once at the beginning of block processing when merkle root checks are enabled. The implementation includes proper validation and error handling.


195-213: LGTM! Efficient reuse of parsed CCbTx.

The code correctly reuses the pre-parsed CCbTx for both merkle root and chainlock validation, achieving the optimization goal. Proper optional value checking ensures safety.


282-283: Verify the deployment check change from DIP0003 to DIP0008.

The deployment check has been changed from DIP0003 to DIP0008. While this appears intentional, please confirm this aligns with the expected activation requirements for credit pool validation.


277-291: LGTM! Clean implementation of signature change.

The function now correctly:

  1. Accepts the pre-parsed CCbTx object
  2. Retrieves the block subsidy internally
  3. Uses cbTx.creditPoolBalance for validation

This change successfully eliminates redundant CCbTx parsing while maintaining the same validation logic.

@knst knst requested review from PastaPastaPasta and UdjinM6 June 12, 2025 07:20
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 0c7cfd9

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 0c7cfd9

@PastaPastaPasta PastaPastaPasta merged commit 1ab7e9e into dashpay:develop Jun 17, 2025
58 of 59 checks passed
knst pushed a commit to knst/dash that referenced this pull request Sep 15, 2025
0c7cfd9 refactor: make CheckCreditPoolDiffForBlock private and tidy up it (Konstantin Akimov)
a7fe377 refactor: bench time is properly aligned, variables properly renamed (Konstantin Akimov)
ebb9dc0 perf: parse CCbTx once during block validation for Credit Poool (Konstantin Akimov)
59c2407 refactor: remove ProcessSpecialTx and UndoSpecialTx which do nothing (Konstantin Akimov)
60bf96d perf: exclude parsing CCbTx if flag fCheckCbTxMerkleRoots is not set (Konstantin Akimov)
b911929 perf: parse CCbTx once during block validation (check CbTx) (Konstantin Akimov)
d4f4173 perf: parse CCbTx once during block validation (merkle roots and CL) (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  Coinbase Transaction (CCbTx) is parsed several times:
   - itself validation (during CheckCbTx)
   - for merkle roots (during CheckCbTxMerkleRoots)
   - for CL validation (during CheckCbTxBestChainlock)
   - for Credit Pool (during CheckCreditPoolDiffForBlock)

  For pre-v20 blocks this parsing has been relatively cheap, but once V20 has been activated, each CbTx has CL signature and its deserialization become expensive due to heavy call of `CBLSWrapper<bls::G2Element, 96ul, CBLSSignature>::Unserialize<CDataStream>(CDataStream&, bool)`.

  ## What was done?
  CCbTx is parsed only once and reused for all related checks.

  This PR makes validation of blocks after v20 activation for ~6% faster.
  Validation of pre-v20 block is expected to be improved insignificantly (<1% of total time).

  ## How Has This Been Tested?
  Invalidated + reconsidered ~8.5k blocks (~2 weeks) after v20 activation.

  Develop:
  <img width="704" alt="image" src="https://github.com/user-attachments/assets/08b63507-4cbf-4e2c-917e-1287ff17210d" />
  ```
  [bench]         - Loop: 0.22ms [10.01s]
  [bench]           - GetTxPayload: 0.21ms [2.16s]
  [bench]         - CheckCbTxMerkleRoots: 3.23ms [28.46s]
  [bench]         - CheckCbTxBestChainlock: 3.16ms [24.65s]
  [bench]       - ProcessSpecialTxsInBlock: 8.15ms [101.19s (11.66ms/blk)]
  [bench]       - CheckCreditPoolDiffForBlock: 0.22ms [2.98s (0.34ms/blk)]
  [bench]     - Dash specific: 0.42ms [4.98s (0.57ms/blk)]
  [bench]   - Connect total: 8.87ms [113.20s (13.04ms/blk)]
  [bench] - Connect block: 9.26ms [116.51s (13.42ms/blk)]
  ```

  PR:
  <img width="704" alt="image" src="https://github.com/user-attachments/assets/697d9b73-83f4-419e-9ecc-fa9c24e1eafc" />
  ```
  [bench]       - GetTxPayload: 0.24ms [2.34s]
  [bench]       - Loop: 0.02ms [7.89s]
  [bench]       - CheckCreditPoolDiffForBlock: 0.01ms [0.76s]
  [bench]       - CheckCbTxMerkleRoots: 3.17ms [26.50s]
  [bench]       - CheckCbTxBestChainlock: 3.08ms [22.77s]
  [bench]       - ProcessSpecialTxsInBlock: 8.02ms [98.70s (11.37ms/blk)]
  [bench]     - Dash specific: 0.27ms [1.97s (0.23ms/blk)]
  [bench]   - Connect total: 8.58ms [107.65s (12.40ms/blk)]
  [bench] - Connect block: 8.92ms [110.97s (12.78ms/blk)]
  ```

  Some insignificant lines (not relevant to PR) are removed from both screenshots and bench logs, only affected functions are shown.

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] 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 0c7cfd9
  PastaPastaPasta:
    utACK 0c7cfd9

Tree-SHA512: b684f8b3073870ac40cb4de19066e9424307e2c39c753e0bc3c584d491c8dbc1634a35b63fe906acfe03bf9e1b78f0642cee4c0c3715533570ea6693fa4767ea
knst pushed a commit to knst/dash that referenced this pull request Sep 17, 2025
0c7cfd9 refactor: make CheckCreditPoolDiffForBlock private and tidy up it (Konstantin Akimov)
a7fe377 refactor: bench time is properly aligned, variables properly renamed (Konstantin Akimov)
ebb9dc0 perf: parse CCbTx once during block validation for Credit Poool (Konstantin Akimov)
59c2407 refactor: remove ProcessSpecialTx and UndoSpecialTx which do nothing (Konstantin Akimov)
60bf96d perf: exclude parsing CCbTx if flag fCheckCbTxMerkleRoots is not set (Konstantin Akimov)
b911929 perf: parse CCbTx once during block validation (check CbTx) (Konstantin Akimov)
d4f4173 perf: parse CCbTx once during block validation (merkle roots and CL) (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  Coinbase Transaction (CCbTx) is parsed several times:
   - itself validation (during CheckCbTx)
   - for merkle roots (during CheckCbTxMerkleRoots)
   - for CL validation (during CheckCbTxBestChainlock)
   - for Credit Pool (during CheckCreditPoolDiffForBlock)

  For pre-v20 blocks this parsing has been relatively cheap, but once V20 has been activated, each CbTx has CL signature and its deserialization become expensive due to heavy call of `CBLSWrapper<bls::G2Element, 96ul, CBLSSignature>::Unserialize<CDataStream>(CDataStream&, bool)`.

  ## What was done?
  CCbTx is parsed only once and reused for all related checks.

  This PR makes validation of blocks after v20 activation for ~6% faster.
  Validation of pre-v20 block is expected to be improved insignificantly (<1% of total time).

  ## How Has This Been Tested?
  Invalidated + reconsidered ~8.5k blocks (~2 weeks) after v20 activation.

  Develop:
  <img width="704" alt="image" src="https://github.com/user-attachments/assets/08b63507-4cbf-4e2c-917e-1287ff17210d" />
  ```
  [bench]         - Loop: 0.22ms [10.01s]
  [bench]           - GetTxPayload: 0.21ms [2.16s]
  [bench]         - CheckCbTxMerkleRoots: 3.23ms [28.46s]
  [bench]         - CheckCbTxBestChainlock: 3.16ms [24.65s]
  [bench]       - ProcessSpecialTxsInBlock: 8.15ms [101.19s (11.66ms/blk)]
  [bench]       - CheckCreditPoolDiffForBlock: 0.22ms [2.98s (0.34ms/blk)]
  [bench]     - Dash specific: 0.42ms [4.98s (0.57ms/blk)]
  [bench]   - Connect total: 8.87ms [113.20s (13.04ms/blk)]
  [bench] - Connect block: 9.26ms [116.51s (13.42ms/blk)]
  ```

  PR:
  <img width="704" alt="image" src="https://github.com/user-attachments/assets/697d9b73-83f4-419e-9ecc-fa9c24e1eafc" />
  ```
  [bench]       - GetTxPayload: 0.24ms [2.34s]
  [bench]       - Loop: 0.02ms [7.89s]
  [bench]       - CheckCreditPoolDiffForBlock: 0.01ms [0.76s]
  [bench]       - CheckCbTxMerkleRoots: 3.17ms [26.50s]
  [bench]       - CheckCbTxBestChainlock: 3.08ms [22.77s]
  [bench]       - ProcessSpecialTxsInBlock: 8.02ms [98.70s (11.37ms/blk)]
  [bench]     - Dash specific: 0.27ms [1.97s (0.23ms/blk)]
  [bench]   - Connect total: 8.58ms [107.65s (12.40ms/blk)]
  [bench] - Connect block: 8.92ms [110.97s (12.78ms/blk)]
  ```

  Some insignificant lines (not relevant to PR) are removed from both screenshots and bench logs, only affected functions are shown.

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] 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 0c7cfd9
  PastaPastaPasta:
    utACK 0c7cfd9

Tree-SHA512: b684f8b3073870ac40cb4de19066e9424307e2c39c753e0bc3c584d491c8dbc1634a35b63fe906acfe03bf9e1b78f0642cee4c0c3715533570ea6693fa4767ea
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants