Skip to content

fix(platform)!: credit repaid identity debt to the processing fee pool - #4985

Merged
QuantumExplorer merged 4 commits into
v4.2-devfrom
claude/identity-debt-processing-pool
Sep 25, 2026
Merged

QuantumExplorer merged 4 commits into
v4.2-devfrom
claude/identity-debt-processing-pool

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

When an identity cannot cover the processing part of a fee, the unpaid part stays behind as a debt (its negative credit balance), and the fee pools only receive what was paid. Credits the identity receives later while its balance is empty repay that debt first (add_to_previous_balance): the identity's balance gets only what is left.

The repaid part was then credited nowhere. The credits left their source (a top-up's new system credits, a transfer's sender, a refund's storage pool, an epoch payout) but only the remainder reached a balance, so the platform's credit total and the sum of its balances no longer agreed after a repayment.

What was done?

The repaid part now goes to the processing fee pool of the epoch it is repaid in, where the unpaid fee would have gone. Protocol version 14 only.

  • add_to_identity_balance 1 (add_to_identity_balance_operations 1): the same arithmetic as 0, and when the added credits repay a debt the operations end with a new LowLevelDriveOperation::RepaidIdentityDebt(credits). It is not a GroveDB operation and has no cost; whoever applies the operations owes those credits to the current epoch's processing fee pool.
  • Every apply routes it:
    • apply_drive_operations 1 (every state transition's operations, and the end of block batch) takes them out of the batch and, once the batch applied, adds their total to the processing fee pool of the block's epoch in a follow-up write. The write reads the state the batch left, so at the end of a block it adds to the fee distribution the same batch wrote instead of racing it (two writes of one pool item in one batch would lose one). It is not billed, so fees do not move.
    • apply_balance_change_from_fee_to_identity 1 routes the debt the payer's own refund repays and the debts other identities' refunds repay, in its own batch. It now takes the block info (the dispatcher gained block_info; generation 0 ignores it). The fee it reports as paid is unchanged.
    • add_epoch_pool_to_proposers_payout_operations 1 hands the epoch payouts to the block's apply_drive_operations as the identity credits they are, instead of converting them to a plain grove batch with convert_drive_operations_to_grove_operations, which cannot carry the marker. The conversion reads the same state either way.
    • add_to_identity_balance 1 (the applying method) writes the pool after its batch too. Without a caller transaction both writes share an owned transaction, committed only once the pool write applied, so a failed pool write leaves the balance and debt as they were.
  • apply_drive_operations 1 first merges every AddToIdentityBalance and RemoveFromIdentityBalance of one identity in the batch into one net write (or none when they cancel), in the place of the first. Each write is converted against the balance and debt committed before the batch, so unmerged, a second write to one identity replaced the first, and two credits to an indebted identity each reported repaying the same debt. A debt exists only at a zero balance, so writes that would succeed in turn net to a credit that repays it once. A batch writing each identity's balance at most once is unchanged, and skips the merge map. This covers an epoch payout in which two masternodes pay the same recipient.
  • The epoch payout (generation 1) also skips a reward share whose payToId has no balance (the share stays with its masternode) and caps each share at what is left of the masternode's payout, so neither fails the batch that ends the block.
  • Nothing can drop one silently: apply_batch_low_level_drive_operations (in its protocol version 14 arm, generation 1), the unused partial-batch apply and convert_drive_operations_to_grove_operations refuse a batch that still holds a RepaidIdentityDebt, with CorruptedCodeExecution, and pricing one is CorruptedCodeExecution as well. grovedb_operations_consume and grovedb_operations_batch_consume document that they drop it. The unbilled pool write after a batch is one helper, apply_repaid_identity_debt_to_processing_pool.
  • AddToPreviousBalanceOutcomeV0 reports repaid_debt, the part of the added credits that repaid the debt, which add_to_previous_balance 0 already computed.
  • Tables: DRIVE_IDENTITY_METHOD_VERSIONS_V2 (protocol version 14's) sets add_to_identity_balance and apply_balance_change_from_fee_to_identity to 1, and DRIVE_ABCI_METHOD_VERSIONS_V10 sets add_epoch_pool_to_proposers_payout_operations to 1, both amended in place since protocol version 14 is unreleased.
  • Docs: item 42 of the protocol version 14 notes in v14.rs, the DRIVE_IDENTITY_METHOD_VERSIONS_V2 doc comment, and the identity debt paragraph of the book's address fees chapter.

Before and after

An identity with an empty balance owes 100 credits. It is topped up with 300 credits in a block of epoch 2:

Before (protocol version 13):
  system credits  +300
  identity balance 0 -> 200, debt 100 -> 0
  epoch 2 processing fee pool  unchanged
  => the platform's credit total is 100 above the sum of its balances

After (protocol version 14):
  system credits  +300
  identity balance 0 -> 200, debt 100 -> 0
  epoch 2 processing fee pool  +100
  => the credit total equals the sum of the balances

The same holds when the credits come from a transfer, a document or token purchase, a moderators pot settlement, a storage refund (the payer's own or another identity's) or an epoch payout. When the added credits do not cover the debt (60 credits against 100), all 60 go to the pool and 40 stay owed, as before. An estimate reads no debt and repays none, as before.

How Has This Been Tested?

Drive (add_to_identity_balance/v1, apply_balance_change_from_fee_to_identity/v1):

  • should_credit_the_repaid_debt_to_the_processing_fee_pool_and_keep_the_credit_sum: the top-up above; calculate_total_credits_balance balances.
  • should_credit_all_added_credits_to_the_pool_when_they_do_not_cover_the_debt
  • should_leave_the_repaid_debt_in_no_counted_balance_at_protocol_version_13: the same top-up through the dispatcher at protocol version 13 still repays and credits no pool, and the credit sum does not balance.
  • should_credit_every_debt_one_batch_repays_to_the_processing_fee_pool_once: two indebted identities credited in one apply_drive_operations batch; the pool gets both repayments.
  • should_add_the_repaid_debt_to_a_processing_fee_pool_write_of_the_same_batch: a batch that writes the epoch's processing fees itself (as the end of a block does) and repays a debt ends with both.
  • should_repay_nothing_when_only_estimating
  • should_repay_a_debt_once_when_one_batch_credits_the_identity_twice: a debt of 100 and two credits of 60 in one batch end with balance 20, debt 0 and 100 in the pool, and the credit sum balances.
  • should_keep_every_credit_one_batch_makes_to_one_identity: two credits to one identity without a debt both land.
  • should_merge_a_credit_and_a_debit_of_one_indebted_identity_into_one_net_write: a debt of 100, a credit of 300 and a debit of 7 in one batch end with balance 193, debt 0 and 100 in the pool, and the credit sum balances.
  • should_commit_neither_write_when_the_pool_write_fails_without_a_caller_transaction: the pool write fails (an epoch with no pool tree) and the balance and debt are unchanged.
  • should_refuse_to_apply_a_batch_holding_an_unrouted_repaid_debt, should_refuse_to_convert_a_debt_repaying_credit_into_a_plain_batch
  • should_credit_the_debts_that_the_payer_and_another_refund_repay_to_the_processing_fee_pool: the payer's refund and another identity's refund both repay; the fee reported as paid is the fee result, unchanged.
  • should_credit_the_debt_another_refund_repays_when_the_payers_balance_does_not_change
  • should_leave_the_processing_fee_pool_alone_at_protocol_version_13
  • The four existing debt tests of apply_balance_change_from_fee_to_identity_operations (identity and token balance modules) now expect the trailing RepaidIdentityDebt with the repaid amount.

drive-abci:

  • should_credit_the_debt_a_transfer_repays_to_the_processing_fee_pool (identity credit transfer): a real transfer to an indebted identity through process_raw_state_transitions; the recipient keeps the rest and the epoch's processing fee pool holds exactly the repaid debt.
  • should_pay_proposers_and_shares_and_credit_a_repaid_debt_to_the_block_epochs_pool (payout 1): the payout test with one share recipient in debt; the recipient gets its share less the debt, and the pool of the block's epoch gets the debt.
  • should_repay_a_shared_recipients_debt_once_from_every_share_one_payout_pays_it (payout 1): two masternodes each pay half their reward to one indebted recipient; it receives both shares less the debt once, and the pool of the block's epoch gets the debt.
  • should_keep_a_share_without_a_recipient_and_cap_a_share_above_the_reward (payout 1): a share naming no identity stays with its masternode, and a 120% share pays its recipient the masternode's whole reward.
  • The payout generation 0 test is pinned to protocol version 13.

Results, run locally:

  • cargo test -p drive --lib -- add_to_identity_balance apply_balance_change_from_fee_to_identity balance::update identity::update: 85 passed.
  • cargo test -p drive-abci --lib: 3514 passed, 0 failed. After the review fixes, on a clean build of the workspace crates: the Drive filter above 90 passed, cargo test -p drive-abci --lib -- add_epoch_pool_to_proposers_payout_operations identity_credit_transfer fee_pool process_block_fees 102 passed (the payout tests 6 runs in a row), and after the second round (debits merged, payout skip and cap): the Drive filter 91 passed, the drive-abci filter 103 passed, cargo test -p drive-abci --lib 3518 passed, and the strategy tests below green; clippy, fmt and the verify build clean. With the debit mapping, the skip or the cap removed, the matching new test fails. With the merge disabled the three repeated-credit tests fail; without the owned transaction the atomicity test fails.
  • Strategy tests across epoch changes (the credit sum is verified every block, and each epoch change pays proposers through the new payout generation): run_chain_insert_one_new_identity_per_block_with_epoch_change, run_chain_comprehensive_mixed_operations_with_epoch_change_and_quorum_rotation, run_chain_insert_many_new_identity_per_block_many_document_insertions_updates_transfers_and_deletions_with_epoch_change, run_chain_top_up_identities, run_chain_top_up_identities_from_addresses, run_chain_transfer_between_identities, run_chain_core_height_randomly_increasing_with_quick_epoch_change, should_record_the_total_credits_history_after_epoch_core_rewards: all passed. The first overflows the default test thread stack in a local debug build on the base branch too, so it ran with RUST_MIN_STACK=16777216.
  • Mutation check: with the three table rows set back to 0, the seven Drive tests and two drive-abci tests that expect the pool credit fail, and the protocol version 13, estimate and guard tests still pass.
  • cargo clippy -p drive -p drive-abci -p platform-version --all-features --all-targets -- -D warnings, cargo check -p drive --no-default-features --features verify and cargo fmt --all -- --check are clean.

Breaking Changes

Consensus, at protocol version 14 only (unreleased): credits that repay an identity's debt are credited to the processing fee pool of the current epoch. No fee changes. Earlier protocol versions keep generation 0 of every method involved.

In-place changes to shipped generations

  • add_to_previous_balance 0 (every protocol version): its outcome also reports repaid_debt, the part of the added credits its arithmetic already kept from the balance. The balance and debt writes are unchanged; only the protocol version 14 generations read the new field.
  • add_to_identity_balance 0 (every protocol version): the body of add_to_identity_balance_operations_v0 moves into add_to_identity_balance_operations_and_repaid_debt_v0, which also returns the repaid part add_to_previous_balance reports, so generation 1 reuses it and only appends the marker. Generation 0 returns the same operations, in the same order; the existing fee-pinning tests of generation 0 pass unchanged.
  • apply_balance_change_from_fee_to_identity 0 (protocol versions 1 to 13): sets the new outcome field to 0 in its two literals, and its dispatcher takes a block_info that generation 0 ignores. No behaviour change.
  • convert_drive_operations_to_grove_operations 0 (every protocol version) and the partial-batch apply dispatcher (unused): refuse an operation list holding a RepaidIdentityDebt. Only add_to_identity_balance_operations 1 produces one, which protocol version 14 alone selects, so no earlier batch meets the check. The same guard in apply_batch_low_level_drive_operations sits in its generation 1 arm, which only protocol version 14 selects.
  • apply_drive_operations 1: selected by protocol version 14 alone (DRIVE_VERSION_V9), so edited in place as that version's generation.

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 added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed
  • If I added or changed GroveDB structure, I described it in the area's structure.rs, regenerated grovedb-structure.json, and checked the structure viewer link posted on this pull request

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

PR Hygiene · 0bd4f1c

  • Bots — coderabbitai ✓ · thepastaclaw not yet — /skip-bots proceeds without the ones not yet reported
  • Self-review — post /self-reviewed once the bots are done
  • Within your 5 open PRs
  • Build running
  • Approvals — you own every area touched; none needed

When every box is checked the PR Hygiene check passes and this can merge.

Summary by CodeRabbit

  • New Features
    • From protocol version 14, credits added to an identity with debt repay that debt first. Repaid credits are added to the current epoch’s processing-fee pool.
    • Proposer payouts now account for debt repayments and distribute eligible rewards according to proposers’ block contributions.
    • Balance updates within a batch are combined before processing.
  • Documentation
    • Updated protocol and fee documentation to explain debt repayment and fee-pool accounting.

…l (PV14)

An identity whose balance could not cover the processing part of a fee
keeps the unpaid part as a debt, and credits it receives while its
balance is empty repay that debt first. The repaid part was credited
nowhere: only the remainder reached a balance.

From protocol version 14 the repaid part goes to the processing fee
pool of the epoch it is repaid in, where the unpaid fee would have gone.
add_to_identity_balance_operations 1 marks it with a new
LowLevelDriveOperation::RepaidIdentityDebt, and every apply routes it:
apply_drive_operations 1 writes the pool after its batch (unbilled, and
adding to a fee distribution the same batch wrote), the fee path
(apply_balance_change_from_fee_to_identity 1, which now takes the block
info) writes it in its own batch, and the epoch payout (generation 1)
hands its credits to the block's apply_drive_operations instead of a
plain grove batch. A batch still holding one is refused by
apply_batch_low_level_drive_operations and by
convert_drive_operations_to_grove_operations. add_to_previous_balance 0
reports the repaid part it already computed. Fees do not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 24, 2026
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: dashpay/platform/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: ad513b76-d3ad-4c21-b503-cdf15d0d5a76

📥 Commits

Reviewing files that changed from the base of the PR and between 9eb59ec and 0bd4f1c.

📒 Files selected for processing (30)
  • book/src/fees/platform-address-fees.md
  • packages/rs-drive-abci/src/execution/platform_events/fee_pool_outwards_distribution/add_epoch_pool_to_proposers_payout_operations/mod.rs
  • packages/rs-drive-abci/src/execution/platform_events/fee_pool_outwards_distribution/add_epoch_pool_to_proposers_payout_operations/v0/mod.rs
  • packages/rs-drive-abci/src/execution/platform_events/fee_pool_outwards_distribution/add_epoch_pool_to_proposers_payout_operations/v1/mod.rs
  • packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/execute_event/v0/mod.rs
  • packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/execute_event/v1/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_transfer/mod.rs
  • packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/add_epoch_processing_credits_for_distribution_operation/mod.rs
  • packages/rs-drive/src/drive/identity/balance/update.rs
  • packages/rs-drive/src/drive/identity/update/methods/add_to_identity_balance/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/add_to_identity_balance/v0/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/add_to_identity_balance/v1/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/add_to_previous_balance/v0/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/apply_balance_change_from_fee_to_identity/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/apply_balance_change_from_fee_to_identity/v0/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/apply_balance_change_from_fee_to_identity/v1/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/debt_test_helpers.rs
  • packages/rs-drive/src/drive/identity/update/methods/mod.rs
  • packages/rs-drive/src/drive/identity/update/structs/add_to_previous_balance_outcome/mod.rs
  • packages/rs-drive/src/drive/identity/update/structs/add_to_previous_balance_outcome/v0/mod.rs
  • packages/rs-drive/src/drive/tokens/balance/update.rs
  • packages/rs-drive/src/fees/op.rs
  • packages/rs-drive/src/util/batch/drive_op_batch/drive_methods/apply_drive_operations/v1/mod.rs
  • packages/rs-drive/src/util/batch/drive_op_batch/drive_methods/convert_drive_operations_to_grove_operations/v0/mod.rs
  • packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/mod.rs
  • packages/rs-drive/src/util/operations/apply_partial_batch_low_level_drive_operations/mod.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/v2.rs
  • packages/rs-platform-version/src/version/drive_versions/v9.rs
  • packages/rs-platform-version/src/version/v14.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Protocol version 14 changes identity credit and fee balance handling so credits that repay identity debt are recorded in the applicable epoch’s processing-fee pool. The change also adds batch routing and version-1 proposer payout logic, with tests for repayment and payout cases.

Changes

Identity debt repayment

Layer / File(s) Summary
Record repayment during identity credits
packages/rs-drive/src/drive/identity/update/methods/add_to_identity_balance/*, packages/rs-drive/src/drive/identity/update/methods/add_to_previous_balance/v0/mod.rs, packages/rs-drive/src/drive/identity/update/structs/add_to_previous_balance_outcome/*, packages/rs-drive/src/drive/identity/update/methods/debt_test_helpers.rs, packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/v2.rs
Identity credit outcomes report credits used to repay debt. Version 1 records and routes those credits to the processing-fee pool. Tests cover debt repayment, pool accounting, and protocol version 13 behavior.
Route repayments from fee balance changes
packages/rs-drive/src/drive/identity/update/methods/apply_balance_change_from_fee_to_identity/*, packages/rs-drive/src/drive/credit_pools/epochs/credit_distribution_pools/add_epoch_processing_credits_for_distribution_operation/mod.rs, packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/execute_event/*, packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_transfer/mod.rs, packages/rs-drive/src/drive/identity/balance/update.rs, book/src/fees/platform-address-fees.md
Version 1 fee balance changes record debt repaid by payer and refund credits, then route it to the block epoch’s processing-fee pool. Event calls pass block information. Tests check repayment amounts and pool accounting.
Route repayment markers in Drive batches
packages/rs-drive/src/fees/op.rs, packages/rs-drive/src/util/batch/drive_op_batch/drive_methods/apply_drive_operations/v1/mod.rs, packages/rs-drive/src/util/batch/drive_op_batch/drive_methods/convert_drive_operations_to_grove_operations/v0/mod.rs, packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/mod.rs, packages/rs-drive/src/util/operations/apply_partial_batch_low_level_drive_operations/mod.rs, packages/rs-drive/src/drive/tokens/balance/update.rs, packages/rs-platform-version/src/version/drive_versions/v9.rs
Drive batches merge repeated identity balance writes and route extracted RepaidIdentityDebt amounts to the epoch pool. Conversion and low-level apply paths reject batches that contain unrouted repayment markers.
Add epoch-pool proposer payouts
packages/rs-drive-abci/src/execution/platform_events/fee_pool_outwards_distribution/add_epoch_pool_to_proposers_payout_operations/*, packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs, packages/rs-platform-version/src/version/v14.rs
Version 1 creates proposer and eligible reward-share payouts from epoch fees and core rewards. Shares are skipped for identities without balances and capped at the proposer’s remaining payout.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Event as State transition event
  participant Drive as Identity balance processing
  participant Batch as Drive operation batch
  participant Pool as Epoch processing-fee pool
  Event->>Drive: Apply credit or fee balance change
  Drive->>Batch: Add balance operations and repayment markers
  Batch->>Pool: Route repaid debt credits for the block epoch
Loading

Suggested reviewers: thepastaclaw

Merge Risk: 🔵 Low · up to 0bd4f

From protocol version 14, credits that repay an identity's debt are added to the epoch's processing fee pool. Proposer payouts now skip recipients without balances and cap reward shares at the proposer's remaining payout. No outstanding defects were found, and the earlier atomicity and double-counting concerns appear resolved. Because this changes consensus-level credit accounting, owners should keep the new repayment and payout tests in place when making related changes.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 0bd4f

The change affects how credits move through the platform’s shared fee pools. The reviewed paths contain controls for keeping balance and pool updates together, but the breadth of the accounting change and incomplete recovery coverage warrant design review. No PR-introduced security issue was established.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — An accounting error in this path could affect shared epoch processing-fee funds, not only the credited identity: repayment markers are aggregated across a batch and applied to an epoch pool.

Trust Boundaries and Controls

  • observed — Reviewed proposer-payout evidence retains proposer-owned reward-share lookup. Version 1 also checks recipient balance existence and caps a share to the remaining proposer payout, rather than widening the configured recipient’s payout authority.

Resilience and Maintainability Implications

  • observed — Block finalization holds a transaction through proposal finalization and commits it afterward; on an ordinary failed commit, the handler requires a restart to restore caches before a block retry.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 95.65% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 69 functions across 29 files. (1 skipped: 1…
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 Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: routing repaid identity debt to the processing fee pool.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

📖 Book Preview built successfully.

Download the preview from the workflow artifacts.
To view locally: download the artifact, unzip, and open index.html.

Updated at 2026-09-25T14:39:05.136Z

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Sep 24, 2026
@thepastaclaw

thepastaclaw commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

🕓 Queued for automated review — 5th in line, estimated start in ~2.3 h (commit 0bd4f1c)
Estimated review time once started: ~55 min (two-phase automated review; median of recent runs).

  • Request priority review — click to move this review to the front of the queue.

@github-actions github-actions Bot added the bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. label Sep 24, 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.

Final validation — Phase 2 only (queue backlog)

The PV14 debt-routing logic correctly preserves the intended accounting for distinct indebted identities, but it does not handle repeated credits to the same identity within one batch. The standalone balance API also commits the identity mutation before its separate processing-pool write when no caller transaction is supplied, allowing a failed pool update to leave the credit accounting inconsistent.

🔴 2 blocking

Review provenance

Source: reviewer 1: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 2: gpt-6-astra (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6-astra (agent: phase2-reviewer, role: platform-versioning); reviewer 4: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 5: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)

  • Triage: critical by gpt-6-astra (effort low) — This is a large, intricate change to consensus-critical funds movement and fee accounting across Drive and ABCI operation application, including identity debt repayment, processing fee pools, and protocol-version behavior.
  • Phase 1 reviewers: not run (skipped for throughput: 18 PRs queued, above the 10 limit)
  • Fresh verifier: gpt-6-astra — final-verifier; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort xhigh); 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 `packages/rs-drive/src/util/batch/drive_op_batch/drive_methods/apply_drive_operations/v1/mod.rs`:
- [BLOCKING] packages/rs-drive/src/util/batch/drive_op_batch/drive_methods/apply_drive_operations/v1/mod.rs:113-114: Aggregate repeated credits to one indebted identity before routing repayment
  Each `add_to_identity_balance_operations_v1` call is converted against the same pre-batch state, and this code only sums the resulting `RepaidIdentityDebt` markers. If two operations credit the same indebted identity, both observe the original debt and emit updates based on it. With the production default `batching_consistency_verification = false`, the duplicate debt writes leave the final identity state at the result of one credit, while both markers are routed to the processing pool. For example, a debt of 100 receiving two credits of 60 ends with balance 0, debt 40, and pool credit 120 instead of balance 20, debt 0, and pool credit 100. This can occur in epoch payouts when multiple reward-share payments target the same recipient. Coalesce credits per identity before calculating repayment, or otherwise apply pending balance/debt updates when constructing the batch, and add a shared-recipient payout regression.

In `packages/rs-drive/src/drive/identity/update/methods/add_to_identity_balance/v1/mod.rs`:
- [BLOCKING] packages/rs-drive/src/drive/identity/update/methods/add_to_identity_balance/v1/mod.rs:47-69: Commit the identity update and processing-pool credit atomically
  When `transaction` is `None`, the first `apply_batch_low_level_drive_operations` call commits the identity balance and debt changes before the processing-pool operation is constructed and applied. The pool operation can fail while reading or updating the epoch pool—for example, an uninitialized or malformed pool returns an error at lines 57–62—after the identity mutation has already persisted. A process interruption between the two calls has the same effect. The direct public API can therefore clear debt and reduce the identity's incoming balance without recording the corresponding pool credit, recreating the conservation defect this PR addresses. When applying without a caller transaction, create an owned transaction spanning the reads and both writes, commit only after the pool update succeeds, and preserve caller-supplied transactions.

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Your move: thepastaclaw requested changes on this head; dismiss the review or push a fix; thepastaclaw left review threads unresolved; resolve them.
Full checklist in the description.

@github-actions github-actions Bot added waiting-self-review Waiting for the author to post /self-reviewed and removed waiting-bots Waiting for the review bots to report on this head labels Sep 24, 2026
…ce, and credit the pool atomically (PV14)

apply_drive_operations 1 converted every AddToIdentityBalance against
the balance and debt committed before the batch, so two credits to one
indebted identity each reported repaying the same debt while the later
balance write replaced the earlier one: the pool got the repayment
twice and the identity kept one credit. It now merges every credit the
batch makes to one identity into one, in the place of the first, before
converting; a batch crediting each identity once is unchanged. This
also covers an epoch payout in which two masternodes pay one recipient.

add_to_identity_balance 1 committed the balance write before the pool
write when the caller passed no transaction. Both now share an owned
transaction, committed only after the pool write applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added waiting-bots Waiting for the review bots to report on this head and removed waiting-self-review Waiting for the author to post /self-reviewed bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. labels Sep 25, 2026
@QuantumExplorer QuantumExplorer changed the title fix(platform)!: credit repaid identity debt to the processing fee pool (PV14) fix(platform)!: credit repaid identity debt to the processing fee pool Sep 25, 2026
@github-actions github-actions Bot added the bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. label Sep 25, 2026
…nd harden the epoch payout (PV14)

apply_drive_operations 1 now nets every AddToIdentityBalance and
RemoveFromIdentityBalance of one identity in a batch into one write (or
none), in the place of the first, skipping the map when a batch writes
at most one balance. A credit and a debit of one identity no longer
lose a write, and an indebted identity repays its debt once.

The epoch payout (generation 1) skips a reward share whose payToId has
no balance, leaving it with its masternode, and caps each share at what
is left of the masternode's payout, so neither fails the batch that
ends the block.

The unbilled repaid debt pool write is one helper,
apply_repaid_identity_debt_to_processing_pool. The guard refusing an
unrouted RepaidIdentityDebt moves into the protocol version 14 arm of
apply_batch_low_level_drive_operations and joins the partial batch
apply; the consume helpers document that they drop it. The debt test
helpers are shared, the payout and transfer tests assert balance and
debt apart, and the protocol version 14 notes describe the merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot removed the bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. label Sep 25, 2026
…erations 0 and 1

Generation 0's operations body moves into
add_to_identity_balance_operations_and_repaid_debt_v0, which also
returns the part of the credits that repaid the identity's debt;
generation 0 drops it and generation 1 appends the RepaidIdentityDebt
marker, instead of carrying a copy of the whole body. Generation 0
builds the same operations, in the same order, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@QuantumExplorer QuantumExplorer left a comment

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.

Reviewed

@QuantumExplorer
QuantumExplorer merged commit 9af3d3b into v4.2-dev Sep 25, 2026
43 of 44 checks passed
@QuantumExplorer
QuantumExplorer deleted the claude/identity-debt-processing-pool branch September 25, 2026 15:25
QuantumExplorer added a commit that referenced this pull request Sep 25, 2026
#4985 landed first with its own add_epoch_pool_to_proposers_payout_operations
v1. This PR folds into it in place: the per-identity sum (add_owed_credits),
usize proposer index and checked last-payout add join #4985's block-batch
hand-off, skip of a payToId without a balance and share cap. #4985's v1
tests are kept; the copied should_pay_every_proposer_and_its_reward_share
is dropped as covered by them. The v14.rs item moves to 43. The dispatcher
tests now apply the payout in epoch 1, and the debt test asserts that the
credit sum balances.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-bots Waiting for the review bots to report on this head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants