Skip to content

fix: check credit pool amount accumulation - #7816

Merged
PastaPastaPasta merged 1 commit into
dashpay:developfrom
PastaPastaPasta:codex/security-credit-pool-arithmetic
Oct 8, 2026
Merged

PastaPastaPasta merged 1 commit into
dashpay:developfrom
PastaPastaPasta:codex/security-credit-pool-arithmetic

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Credit-pool deposit accounting uses unchecked signed addition. ConnectBlock runs ProcessSpecialTxsInBlock (and with it the credit-pool diff) before CheckTxInputs, so asset-lock amounts are accumulated before their funding is checked. A block that stacks enough unfunded MAX_MONEY asset locks overflows sessionLocked, which is signed-overflow undefined behaviour and terminates trapping builds. Checking representability before mutation turns this into a normal validation failure and keeps correct accounting for representable deposit/withdrawal flows.

What was done?

Use the existing CheckedAdd helper for session deposit and withdrawal amounts and the withdrawal output sum. Assign session amounts only after the candidate sum passes. Calculate the final balance with checked arithmetic, subtracting withdrawals before adding deposits and the Platform reward. An unrepresentable final balance throws through the existing validation/mining error paths.

The CAmount getter API, withdrawal limits and atomic package rollback remain intact. No aggregate or final MAX_MONEY rule was added, and the final net balance is checked after processing rather than after each deposit.

How Has This Been Tested?

  • Local arm64 macOS build using prebuilt depends: make -C src dashd test/test_dash passed.
  • ./src/test/test_dash --run_test=evo_assetlocks_tests passed. The new credit_pool_lock_arithmetic case accumulates individually in-range deposits to the CAmount boundary through the public ProcessLockUnlockTransaction API and checks that the next deposit is rejected and leaves the diff unchanged. The existing suite still covers ordinary asset locks and package rollback.

The fixture exercises the public bookkeeping interface; it is not a funded-block proof, and full-node exploitability was not reproduced.

Breaking Changes

Successful representable accounting retains its mathematical result. Unrepresentable sums now report an error. No monetary cap or activation rule was added.

Checklist:

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9e47206c-843e-4982-bb66-0d44483c1f8a
📥 Commits

Reviewing files that changed from the base of the PR and between e07b557 and 3ef0555.

📒 Files selected for processing (3)
  • src/evo/creditpool.cpp
  • src/evo/creditpool.h
  • src/test/evo_assetlocks_tests.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.


Walkthrough

Credit-pool lock and unlock processing now uses checked addition and rejects overflowing totals. GetTotalLocked uses checked arithmetic and throws std::overflow_error when the result cannot be represented as CAmount. A test verifies that lock amounts can reach the CAmount maximum and that an additional unit is rejected without changing the total.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~15 minutes

Merge Risk: ⚪ Minimal · up to 3ef05

The change adds overflow checks to credit-pool accounting and rejects unrepresentable totals instead of silently overflowing. No actionable merge-blocking risk was found.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description clearly explains the unchecked credit-pool amount accumulation issue, the checked-arithmetic changes, and the related tests.
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding checks for credit-pool amount accumulation.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

Comment @coderabbitai help to get the list of available commands.

@PastaPastaPasta PastaPastaPasta changed the title fix(evo): check credit pool amount accumulation fix: check credit pool amount accumulation Oct 7, 2026
@thepastaclaw

thepastaclaw commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit 3ef0555) · triage: low

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Final validation — Phase 1 + Phase 2

Verified the complete change at c9f0345 and its surrounding validation, mining callers, arithmetic helper, and tests; no actionable in-scope defects were found. Checked additions preserve accounting state on rejection, withdrawal-first final accounting preserves representable net balances under validated amount invariants, and existing validation/RPC exception handlers cover the new overflow error. This was static verification only: the supplied CI snapshot confirms successful completed builds and lint checks, but four test jobs and the ASan/multiprocess builds remain pending, so the claim that full CI is green is not supported by that snapshot.

Review provenance

Source: reviewer 1: glm-5.3-flash (agent: phase1-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: low by gpt-6.1-sol (effort low) — The diff is a small, contained arithmetic-safety fix in credit-pool accounting using CheckedAdd and existing error paths, not a large or intricate change despite touching consensus-sensitive amounts.
  • Phase 1 reviewers: glm-5.3-flash — general (completed, effort high); agent phase1-reviewer
  • Phase 1 model: glm-5.3-flash — zai quota: 5h 97% left, weekly 96% left; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 15% left, 5h 100% left)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — dash-core-commit-history (completed, effort medium); agent phase2-reviewer

@thepastaclaw thepastaclaw added the pastaclaw:approved thepastaclaw's latest review approved this PR label Oct 7, 2026
@PastaPastaPasta
PastaPastaPasta force-pushed the codex/security-credit-pool-arithmetic branch from c9f0345 to 9fe3921 Compare October 7, 2026 16:29
@thepastaclaw thepastaclaw removed the pastaclaw:approved thepastaclaw's latest review approved this PR label Oct 7, 2026

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-review — Final validation — Phase 2 only (queue backlog)

Verified the complete diff at 9fe3921. No blocking correctness issue was found: one boundary-test suggestion is valid, while the claimed loss of withdrawal-index rollback coverage is contradicted by the source. This was a static review; no builds or tests were run, and the supplied CI snapshot shows successful formatting checks with build jobs still queued.

🟡 1 suggestion(s)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: low by gpt-6.1-sol (effort low) — The diff is a small, contained arithmetic-safety fix in CCreditPoolDiff and withdrawal summation with focused regression coverage, not a large or intricate change despite touching consensus accounting.
  • Phase 1 reviewers: not run (skipped for throughput: 22 PRs queued, above the 10 limit)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — dash-core-commit-history (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — dash-core-commit-history (completed, effort medium); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/test/evo_assetlocks_tests.cpp`:
- [SUGGESTION] src/test/evo_assetlocks_tests.cpp:901-920: Cover final-balance overflow and withdrawal-first boundary arithmetic
  This test starts with an empty pool and reaches the sessionLocked limit, so the rejected deposit exercises Lock's overflow check but never the new overflow exception in GetTotalLocked. It also does not distinguish withdrawal-first arithmetic from deposit-first arithmetic. Add public-interface scenarios with a starting balance at the CAmount maximum and zero Platform reward: a small deposit should process successfully but make GetTotalLocked throw; an equal deposit and withdrawal within currentLimit should instead return the maximum balance after both transactions, in either transaction order. These directly cover the newly introduced final-balance behavior without requiring a funded-block fixture.

Comment on lines +901 to +920
BOOST_FIXTURE_TEST_CASE(credit_pool_lock_arithmetic, TestChain100Setup)
{
LOCK(cs_main);
const auto* tip = m_node.chainman->ActiveChain().Tip();
CCreditPoolDiff diff{CCreditPool{}, tip, m_node.chainman->GetConsensus(), 0};
const CAmount maximum = std::numeric_limits<CAmount>::max();
const auto lock_max_money = CreateCreditPoolLockTx(MAX_MONEY);
for (CAmount i = 0; i < maximum / MAX_MONEY; ++i) {
TxValidationState state;
BOOST_REQUIRE(diff.ProcessLockUnlockTransaction(*lock_max_money, state));
if (i == 1) BOOST_CHECK_EQUAL(diff.GetTotalLocked(), 2 * MAX_MONEY);
}
TxValidationState remainder_state;
BOOST_REQUIRE(diff.ProcessLockUnlockTransaction(*CreateCreditPoolLockTx(maximum % MAX_MONEY), remainder_state));
BOOST_CHECK_EQUAL(diff.GetTotalLocked(), maximum);

TxValidationState overflow_state;
BOOST_CHECK(!diff.ProcessLockUnlockTransaction(*CreateCreditPoolLockTx(1), overflow_state));
BOOST_CHECK_EQUAL(overflow_state.GetRejectReason(), "failed-creditpool-lock-overflow");
BOOST_CHECK_EQUAL(diff.GetTotalLocked(), maximum);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🟡 Suggestion: Cover final-balance overflow and withdrawal-first boundary arithmetic

This test starts with an empty pool and reaches the sessionLocked limit, so the rejected deposit exercises Lock's overflow check but never the new overflow exception in GetTotalLocked. It also does not distinguish withdrawal-first arithmetic from deposit-first arithmetic. Add public-interface scenarios with a starting balance at the CAmount maximum and zero Platform reward: a small deposit should process successfully but make GetTotalLocked throw; an equal deposit and withdrawal within currentLimit should instead return the maximum balance after both transactions, in either transaction order. These directly cover the newly introduced final-balance behavior without requiring a funded-block fixture.

source: gpt-6.1-sol (phase2-reviewer: general)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Declining: a pool already at the CAmount maximum is unreachable (pool.locked is bounded by real supply), so testing GetTotalLocked()'s throw from that state would pin the evaluation order of an internal sum rather than observable behavior. The reachable overflow, stacked unfunded deposits, is covered by credit_pool_lock_arithmetic.


🤖 Posted autonomously by Claude on behalf of pasta.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Still applies (re-reviewed at 3ef05555): You're right that the proposed maximum starting pool is unreachable, and that fixture request was incorrect. A modest nonzero pool still separates the final-balance overflow path from the session overflow covered here; checking the getter's exception and successful withdrawal cancellation would test observable public-interface behavior rather than mandate an internal evaluation order.

Comment thread src/test/evo_assetlocks_tests.cpp Outdated
BOOST_FIXTURE_TEST_CASE(credit_pool_package_atomicity, TestChain100Setup)
{
LOCK(cs_main);
const auto lock_two = CreateCreditPoolLockTx(2 * COIN);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

seems unrelated; why does it changed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

You're right, it's unrelated: package rollback isn't touched by this PR. Reverted those credit_pool_package_atomicity edits in 3ef0555; the test is back to develop's version.


🤖 Posted autonomously by Claude on behalf of pasta.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@PastaPastaPasta
PastaPastaPasta force-pushed the codex/security-credit-pool-arithmetic branch 2 times, most recently from 9fe3921 to 3ef0555 Compare October 7, 2026 17:24

@knst knst left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

utACK 3ef0555

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-review — Final validation — Phase 1 + Phase 2

Verified the complete PR diff at the exact head and found no blocking correctness issues. One non-blocking coverage suggestion remains: the current regression checks session-deposit overflow but not final-balance overflow or representable cancellation at the boundary. This was a static review; no builds or tests were run, and the supplied CI snapshot still shows checks queued, pending, or in progress.

🟡 1 suggestion(s)

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Review provenance

Source: reviewer 1: glm-5.3-flash (agent: phase1-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: low by gpt-6.1-sol (effort low) — The diff is a small, contained arithmetic-safety fix in creditpool.cpp using an existing checked-add helper, with a focused regression test, rather than a large or intricate change despite touching consensus accounting.
  • Phase 1 reviewers: glm-5.3-flash — general (completed, effort high); agent phase1-reviewer
  • Phase 1 model: glm-5.3-flash — zai quota: 5h 99% left, weekly 88% left; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 15% left, 5h 100% left)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — dash-core-commit-history (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — dash-core-commit-history (completed, effort medium); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/test/evo_assetlocks_tests.cpp`:
- [SUGGESTION] src/test/evo_assetlocks_tests.cpp:901-920: Cover final-balance overflow and withdrawal-first boundary arithmetic
  (existing thread: https://github.com/dashpay/dash/pull/7816#discussion_r4209694158)
  The original request for a starting pool at the CAmount maximum was incorrect: funded accounting cannot produce that pool. The coverage gap nevertheless exists with a modest nonzero starting pool. Lock() checks only sessionLocked, so accepted bookkeeping can leave that subtotal representable while pool.locked + sessionLocked is not; the empty starting pool in this test never exercises GetTotalLocked()'s new overflow_error branch. CheckCreditPoolDiffForBlock calls the getter before input funding is checked, making this a distinct defensive arithmetic path within the PR's scope. Extend the public-interface boundary coverage with a modest nonzero pool and zero reward: check that an unrepresentable final balance throws, and that a withdrawal within currentLimit allows a representable final net balance despite an unrepresentable deposit-first intermediate sum, in either transaction order. These scenarios exercise the getter's observable exception and result, without asserting that the deposits form a valid funded block.

@PastaPastaPasta PastaPastaPasta added this to the 24 milestone Oct 8, 2026
@PastaPastaPasta
PastaPastaPasta merged commit 21ea7ba into dashpay:develop Oct 8, 2026
50 of 55 checks passed
@PastaPastaPasta
PastaPastaPasta deleted the codex/security-credit-pool-arithmetic branch October 8, 2026 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants