Skip to content

fix(platform)!: 3.0 audit report fixes#3053

Merged
shumkov merged 4 commits intov3.1-devfrom
fix/auditReportFixes
Feb 5, 2026
Merged

fix(platform)!: 3.0 audit report fixes#3053
shumkov merged 4 commits intov3.1-devfrom
fix/auditReportFixes

Conversation

@QuantumExplorer
Copy link
Copy Markdown
Member

@QuantumExplorer QuantumExplorer commented Feb 5, 2026

Issue being fixed or feature implemented

Internal security review of address-based state transitions identified several correctness and robustness issues in the new address funds system (transfers, withdrawals,
asset lock funding, identity creation/top-up from addresses, credit transfers to addresses). This PR fixes the confirmed issues and adds defensive checks where the code
was technically correct but lacked defense-in-depth.

What was done?

Bug Fixes

Fee strategy index shifting after BTreeMap mutation — The fee deduction function used positional .nth() lookups on BTreeMaps that were being mutated during iteration.
When an entry was drained to zero and removed, subsequent indices shifted, causing fee steps to target wrong addresses. Fixed by snapshotting BTreeMap keys into Vecs
before the loop and resolving indices via the stable snapshots.

  • rs-dpp/.../deduct_fee_from_inputs_and_outputs/v0/mod.rs

Credits conservation violation in asset lock with address inputs — AddressFundingFromAssetLock remainder calculation only considered the asset lock amount, ignoring
credits contributed from address inputs. This caused incorrect remainder outputs. Fixed by threading input_contributions_total through the action struct and including it
in the available balance.

  • rs-drive/.../address_funding_from_asset_lock/ (action struct, transformer, execution)

Witness validation bypass via zip() — validate_witnesses() paired inputs with witnesses using zip(), which silently stops at the shorter iterator. With 0 witnesses on 2
inputs, validation passed with zero iterations. Fixed by adding a length equality check before the zip loop.

  • rs-dpp/.../state_transition_witness_validation.rs

Missing minimum/maximum withdrawal amount validation — Address credit withdrawals had no check that the withdrawal amount met MIN_WITHDRAWAL_AMOUNT or stayed under
max_withdrawal_amount. Added a new WithdrawalBelowMinAmountError consensus error type and validation check.

  • rs-dpp/.../address_credit_withdrawal_transition/v0/state_transition_validation.rs
  • rs-dpp/src/errors/consensus/basic/state_transition/withdrawal_below_min_amount_error.rs (new)
  • rs-dpp/src/errors/consensus/codes.rs (error code 10818)
  • wasm-dpp/.../consensus_error.rs (exhaustive match)

Floating-point imprecision in is_fibonacci_number — is_perfect_square used (n as f64).sqrt() which loses precision above 2^52, producing false positives. Replaced with
integer Newton's method. Also renamed to is_non_zero_fibonacci_number since core_fee_per_byte = 0 is not a valid fee rate despite 0 being mathematically Fibonacci.

  • rs-dpp/src/util/is_non_zero_fibonacci_number.rs (renamed from is_fibonacci_number.rs)

Defensive Hardening

Overflow guard for u64→i64 cast in Drive sum trees — set_balance_to_address cast Credits (u64) to SumValue (i64) without checking for overflow. While upstream validation
prevents balances above i64::MAX, Drive now returns ProtocolError::Overflow defensively.

  • rs-drive/.../set_balance_to_address/v0/mod.rs

Maximum P2SH signatures count during deserialization — AddressWitness::P2sh accepted arbitrarily large signature vectors. Added MAX_P2SH_SIGNATURES = 17 (16 keys from
OP_PUSHNUM_16 + 1 CHECKMULTISIG dummy) enforced in all three deserialization paths (bincode Decode, BorrowDecode, serde Deserialize).

  • rs-dpp/src/address_funds/witness.rs

Checked arithmetic in transformers

Multiple transformers for AddressCreditWithdrawal, IdentityCreateFromAddresses, and IdentityTopUpFromAddresses were updated to use checked_sub, checked_add, and try_fold
instead of unchecked arithmetic for input/output sum calculations and remainder computation.

  • rs-drive/.../address_credit_withdrawal/v0/transformer.rs
  • rs-drive/.../identity_create_from_addresses/v0/transformer.rs
  • rs-drive/.../identity_topup_from_addresses/v0/transformer.rs

How Has This Been Tested?

Comprehensive test coverage across all 6 address-based state transitions:

  • Fee deduction stability: Tests verify correct address targeting after entry removal across address_funds_transfer, address_credit_withdrawal,
    address_funding_from_asset_lock, identity_create_from_addresses, and identity_top_up_from_addresses
  • Witness validation: Test confirms zero-witness mismatch is caught at witness validation stage (not deferred to structure validation)
  • Withdrawal limits: Tests for both structure validation and full processing pipeline rejection of dust withdrawals
  • Overflow guards: Tests verify Drive rejects balances above i64::MAX and accepts the boundary value
  • P2SH signatures limit: Boundary tests verify 17 signatures accepted, 18+ rejected across multiple decode paths
  • Integer perfect square: Tests confirm no false positives for large values near 67108865^2 where f64 was previously imprecise
  • Credits conservation: Tests verify asset lock transitions with address inputs correctly account for all credit sources

Breaking Changes

  • is_fibonacci_number renamed to is_non_zero_fibonacci_number (rejects 0, module path changed)
  • New consensus error WithdrawalBelowMinAmountError (code 10818) — withdrawals below MIN_WITHDRAWAL_AMOUNT or above max_withdrawal_amount are now rejected
  • AddressFundingFromAssetLockTransitionActionV0 has a new input_contributions_total field
  • P2SH witnesses with more than 17 signatures are now rejected during deserialization

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

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • Bug Fixes

    • Enforced P2SH signature cap (17) and tightened witness vs. input validation.
    • Added withdrawal bounds check and WithdrawalBelowMinAmount error.
    • Strengthened overflow and arithmetic guards across sums, deductions, and transforms.
    • Stabilized fee deduction to avoid index-shift issues during entry removals.
  • New Features

    • Remainder calculation now accounts for total input contributions.
    • Core-fee validation now rejects zero (non-zero Fibonacci check).
  • Tests

    • Large expansion of security-focused tests covering withdrawals, funding, transfers, witnesses.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

Adds WithdrawalBelowMinAmountError and bounds checks for withdrawals, replaces Fibonacci utility with a non-zero variant, snapshots input/output addresses for fee deduction, caps P2SH signatures, applies overflow-checked arithmetic in transformers, adds input contribution tracking for asset-lock funding, and inserts extensive security-audit tests (some duplicated).

Changes

Cohort / File(s) Summary
Withdrawal error types & wiring
packages/rs-dpp/src/errors/consensus/basic/state_transition/withdrawal_below_min_amount_error.rs, packages/rs-dpp/src/errors/consensus/basic/state_transition/mod.rs, packages/rs-dpp/src/errors/consensus/basic/basic_error.rs, packages/rs-dpp/src/errors/consensus/codes.rs, packages/wasm-dpp/src/errors/consensus/consensus_error.rs
Adds WithdrawalBelowMinAmountError, exposes it in state_transition module, integrates into BasicError, assigns error code, and maps it into WASM consensus error conversion.
Withdrawal validation logic
packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_credit_withdrawal_transition/v0/state_transition_validation.rs
Enforces input>output withdrawal balance check, validates withdrawal against MIN and platform max, adds input_sum overflow guards, and uses non-zero Fibonacci check for core fee.
Fibonacci utility rename & new implementation
packages/rs-dpp/src/util/is_fibonacci_number.rs, packages/rs-dpp/src/util/is_non_zero_fibonacci_number.rs, packages/rs-dpp/src/util/mod.rs
Removes old is_fibonacci_number, adds is_non_zero_fibonacci_number (integer sqrt helper), and updates util exports to the new module name.
Core fee validation update
packages/rs-drive-abci/src/execution/validation/.../identity_credit_withdrawal/structure/v1/mod.rs
Replaces is_fibonacci_number usage with is_non_zero_fibonacci_number to disallow zero as a valid core fee.
Fee deduction snapshotting
packages/rs-dpp/src/address_funds/fee_strategy/deduct_fee_from_inputs_and_outputs/v0/mod.rs
Creates snapshots of input/output addresses and switches to key-based lookups to avoid index-shift issues when removing/updating entries during fee deduction.
P2SH signature limit & witness validation
packages/rs-dpp/src/address_funds/witness.rs, packages/rs-dpp/src/state_transition/traits/state_transition_witness_validation.rs
Adds MAX_P2SH_SIGNATURES = 17 and enforces limit during decode/deserialize; adds pre-check for inputs vs witnesses count and per-witness signature verification error reporting.
Overflow-safe transformers & actions
packages/rs-drive/src/state_transition_action/.../address_credit_withdrawal/v0/transformer.rs, packages/rs-drive/src/state_transition_action/identity/identity_create_from_addresses/v0/transformer.rs, packages/rs-drive/src/state_transition_action/identity/identity_topup_from_addresses/v0/transformer.rs
Replaces naive sums/subtractions with checked accumulation (try_fold) and checked_sub, returning OverflowError on overflow/underflow.
Address funding: include input contributions
packages/rs-drive/src/state_transition_action/address_funds/address_funding_from_asset_lock/v0/mod.rs, .../v0/transformer.rs, .../mod.rs, .../action_convert_to_operations/.../address_funding_from_asset_lock_transition.rs
Adds input_contributions_total field to V0 action, computes total input contributions, exposes accessor, and updates remainder logic to use asset_lock + inputs total.
Drive balance overflow guard
packages/rs-drive/src/drive/address_funds/set_balance_to_address/v0/mod.rs
Adds pre-check returning ProtocolError::Overflow if balance exceeds i64::MAX before casting to internal SumValue.
Widespread security-audit tests
packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/.../tests.rs (many files)
Adds extensive security/audit test suites covering fee deduction stability, index-shift, witness/signature edge cases, overflow scenarios, dust/minimum checks, and other adversarial cases; several test blocks are duplicated across files.
Minor test module addition
packages/rs-drive-abci/src/execution/validation/.../identity_create_from_addresses/mod.rs
Adds a cfg(test) tests module declaration (test-only code inclusion).

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through code with careful paws,
I counted fees and guarded flaws.
No zero Fib to trip our chase,
Signatures capped in tight embrace.
Overflow checked — safe is our place. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'fix(platform)!: 3.0 audit report fixes' is directly related to the PR objectives, which document this as addressing security review findings, but it is overly broad and does not clearly indicate the primary changes (fee deduction snapshots, witness validation, withdrawal bounds, overflow guards, etc.). Consider a more specific title like 'fix(platform)!: address witness/withdrawal/overflow validation and fee deduction snapshots' to better communicate the core issues being fixed.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/auditReportFixes

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 and usage tips.

@github-actions github-actions bot added this to the v3.1.0 milestone Feb 5, 2026
@QuantumExplorer QuantumExplorer marked this pull request as ready for review February 5, 2026 00:54
Copy link
Copy Markdown
Contributor

@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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_credit_withdrawal/tests.rs (1)

3924-3951: ⚠️ Potential issue | 🟡 Minor

Derive the large balance from max_withdrawal to avoid future test brittleness.
If the limit ever increases beyond 600 Dash, the test may fail for the wrong reason.

🔧 Suggested tweak
-            // Test withdrawal at the maximum allowed amount (500 Dash)
+            // Test withdrawal at the maximum allowed amount
             let platform_version = PlatformVersion::latest();
@@
-            // Balance large enough to cover max withdrawal + fees
-            let large_balance = dash_to_credits!(600.0);
+            let max_withdrawal = platform_version.system_limits.max_withdrawal_amount;
+            // Balance large enough to cover max withdrawal + fees
+            let large_balance = max_withdrawal + dash_to_credits!(100.0);
             setup_address_with_balance(&mut platform, input_address, 0, large_balance);
@@
-            // Withdraw the max allowed amount (500 Dash = 50_000_000_000_000 credits)
-            let max_withdrawal = platform_version.system_limits.max_withdrawal_amount;
+            // Withdraw the max allowed amount
             inputs.insert(input_address, (1 as AddressNonce, max_withdrawal));
🤖 Fix all issues with AI agents
In
`@packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funding_from_asset_lock/tests.rs`:
- Around line 9334-9347: The test creates two outputs summing to > u64::MAX but
omits a remainder output so basic structure validation fails before overflow is
checked; update the test in tests.rs that constructs outputs and calls
create_raw_transition_with_dummy_witnesses to include an explicit remainder
output (e.g., add a third entry in the outputs BTreeMap with Some(value)
representing the remainder) so that the transition passes structure checks and
the arithmetic overflow path in AddressFundsFeeStrategy::from(...) (and related
validation) is exercised; ensure the remainder output is placed alongside the
existing output_addr_1 and output_addr_2 and adjust the
AddressFundsFeeStrategy/fee steps only if needed to still trigger the overflow
failure.
- Around line 9095-9114: The test currently constructs outputs without a
remainder output so structure validation fails before exercising the
remainder-removal path; update the call to
create_signed_address_funding_from_asset_lock_transition to include an explicit
remainder output (insert a None entry in the outputs BTreeMap at the remainder
index) and change the fee strategy step to target that remainder index (use
AddressFundsFeeStrategyStep::ReduceOutput with the remainder's index) so the
test actually exercises the remainder-removal scenario. Ensure you modify the
local outputs map used in the
create_signed_address_funding_from_asset_lock_transition call and keep the fee
strategy vector consistent with the remainder index.

In
`@packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funds_transfer/tests.rs`:
- Around line 8298-8306: The new test module currently declared as "mod
security" (the SECURITY AUDIT TESTS block) duplicates an existing module named
"security" and causes a compile error; rename this new module to a unique
identifier (e.g., "mod security_audit" or "mod security_tests") or merge its
contents into the earlier "security" module, and update any internal references
accordingly so the module name no longer conflicts.

In
`@packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create_from_addresses/tests.rs`:
- Around line 11323-11328: The SECURITY AUDIT TESTS module comment currently
says the tests are expected to FAIL until vulnerabilities are fixed, but this PR
claims to fix them; update the module to reflect current intent by either
removing or amending that note and/or mark any intentionally failing tests with
#[ignore] so CI expectations are explicit; locate the failing tests under the
identity_create_from_addresses test module in tests.rs and add #[ignore] to the
specific test functions (or change the comment to indicate which tests remain
intentionally failing) to clarify behavior.
- Around line 11481-11537: The test currently constructs inputs with u64::MAX
values but never funds those addresses so processing can fail with
AddressDoesNotExist instead of an overflow; update the test that builds the
transition (uses create_platform_address,
create_raw_transition_with_dummy_witnesses, AddressFundsFeeStrategy and invokes
platform.process_raw_state_transitions) to seed the input addresses with
sufficient balances in the platform state (via TestPlatformBuilder or
platform.drive.grove transaction setup) so the state transition reaches the
arithmetic path, then change the assertion to check for the specific
overflow/checked-arithmetic rejection (the transformer overflow behavior
referenced in transformer.rs) rather than a generic failure. Ensure the funding
step happens before calling process_raw_state_transitions and that the assertion
inspects processing_result for the overflow-specific error variant.
- Around line 11576-11600: The test uses tiny_balance = min_output + 1 which is
below the identity funding threshold and causes rejection before fee coverage
logic; change the test to compute the platform's min_identity_funding_amount
(platform_version.dpp.state_transitions.identity.min_identity_funding_amount)
and set tiny_balance to at least that value (e.g. min_identity_funding_amount +
1) but still insufficient to cover processing fees so the transition reaches the
fee handling branch; update the call to
create_signed_identity_create_from_addresses_transition and the
inputs/AddressFundsFeeStrategy to use that adjusted tiny_balance so the test
exercises the fee_fully_covered path instead of failing min‑funding early.
🧹 Nitpick comments (6)
packages/rs-dpp/src/address_funds/witness.rs (1)

408-512: Comprehensive bincode test coverage; consider adding serde boundary test.

The tests thoroughly cover the bincode deserialization paths with good boundary testing (exact limit succeeds, limit+1 fails). The intentional asymmetry where encoding succeeds but decoding rejects over-limit signatures is correctly verified.

However, the serde Deserialize path (lines 211-217) is not explicitly tested for limit enforcement. While the existing test_p2sh_serde test covers basic round-trip with a small signature count, adding a boundary test for the serde path would ensure parity with the bincode tests.

🧪 Optional: Add serde boundary test
#[cfg(feature = "state-transition-serde-conversion")]
#[test]
fn test_p2sh_serde_rejects_excessive_signatures() {
    // Manually construct JSON with MAX_P2SH_SIGNATURES + 1 signatures
    let signatures: Vec<Vec<u8>> = (0..MAX_P2SH_SIGNATURES + 1)
        .map(|_| vec![0x30, 0x44, 0x02, 0x20])
        .collect();
    
    let json = serde_json::json!({
        "type": "p2sh",
        "signatures": signatures,
        "redeemScript": [0x52, 0xae]
    });
    
    let result: Result<AddressWitness, _> = serde_json::from_value(json);
    assert!(
        result.is_err(),
        "P2SH witness with {} signatures should be rejected during serde deserialization",
        MAX_P2SH_SIGNATURES + 1,
    );
}
packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_top_up_from_addresses/tests.rs (1)

3430-3492: Test may not verify the intended overflow behavior.

The test test_transformer_subtraction_uses_checked_arithmetic creates addresses using create_platform_address but never calls setup_address_with_balance to register them on the platform. The transition will likely be rejected with AddressDoesNotExistError during state validation rather than an overflow error during structure validation.

While the broad assertion !matches!(...SuccessfulExecution...) will pass, it doesn't verify that the overflow path is actually being tested. Consider either:

  1. Setting up the addresses with balances matching the inputs to allow the transition to reach overflow validation
  2. Adding a more specific assertion that checks for OverflowError
♻️ Suggested improvement to test the actual overflow path

If the intent is to verify overflow detection in structure validation, the addresses need to exist:

             // Two inputs that sum to > u64::MAX
+            let mut signer = TestAddressSigner::new();
+            let addr1 = signer.add_p2pkh([1u8; 32]);
+            let addr2 = signer.add_p2pkh([2u8; 32]);
+            // Store balances at max storable value (i64::MAX fits in sum tree)
+            setup_address_with_balance(&mut platform, addr1, 0, i64::MAX as u64);
+            setup_address_with_balance(&mut platform, addr2, 0, i64::MAX as u64);
+
             let mut inputs = BTreeMap::new();
-            inputs.insert(create_platform_address(1), (0 as AddressNonce, u64::MAX));
-            inputs.insert(create_platform_address(2), (0 as AddressNonce, u64::MAX));
+            inputs.insert(addr1, (1 as AddressNonce, u64::MAX));
+            inputs.insert(addr2, (1 as AddressNonce, u64::MAX));

Then update the assertion to be more specific:

-            assert!(
-                !matches!(
-                    processing_result.execution_results().as_slice(),
-                    [StateTransitionExecutionResult::SuccessfulExecution { .. }]
-                ),
+            // Expect either OverflowError (structure) or AddressNotEnoughFundsError (state)
+            // depending on validation order
+            assert_matches!(
+                processing_result.execution_results().as_slice(),
+                [StateTransitionExecutionResult::UnpaidConsensusError(_)]
packages/rs-dpp/src/util/is_non_zero_fibonacci_number.rs (1)

42-51: Consider updating the test documentation comment.

The comment describes the previous floating-point implementation ("casts u64 to f64 and checks sqrt().fract() == 0.0") that was vulnerable to precision issues. Since the implementation now uses integer Newton's method, this documentation could confuse future readers into thinking the current code has this vulnerability.

Consider updating to clarify this documents the fixed behavior and why the test should pass.

packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funding_from_asset_lock/tests.rs (1)

9276-9292: Use the actual fee result instead of max_reasonable_fee.

The fixed 0.01‑Dash threshold can go stale if fee schedules change. Use the execution fee_result.total_base_fee() to assert exact conservation. Apply the same pattern in both conservation tests.

♻️ Example (M5 test) using fee_result
-            assert_matches!(
-                processing_result.execution_results().as_slice(),
-                [StateTransitionExecutionResult::SuccessfulExecution { .. }],
-                "Pure asset lock funding should succeed"
-            );
+            let fee_result = assert_matches!(
+                processing_result.execution_results().as_slice(),
+                [StateTransitionExecutionResult::SuccessfulExecution { fee_result, .. }] => fee_result.clone(),
+                "Pure asset lock funding should succeed"
+            );
@@
-            let max_reasonable_fee = dash_to_credits!(0.01);
-
-            assert!(
-                total_in_addresses >= asset_lock_credits - max_reasonable_fee,
-                "AUDIT M5: Credits not conserved. Asset lock provided {} credits, \
-                but output addresses only received {} credits total. \
-                Expected at least {} (asset_lock - max_fee {}). \
-                There is no explicit conservation-of-credits assertion in the processing code.",
-                asset_lock_credits,
-                total_in_addresses,
-                asset_lock_credits - max_reasonable_fee,
-                max_reasonable_fee,
-            );
-
-            assert!(
-                total_in_addresses <= asset_lock_credits,
-                "AUDIT M5: Output total {} exceeds asset lock credits {}. \
-                Credits were created from nothing!",
-                total_in_addresses,
-                asset_lock_credits,
-            );
+            let total_fee = fee_result.total_base_fee();
+            assert_eq!(
+                total_in_addresses + total_fee,
+                asset_lock_credits,
+                "Credits should conserve: outputs + fees must equal asset lock credits"
+            );

Also applies to: 9611-9624

packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create_from_addresses/tests.rs (2)

8842-8848: Consider asserting the overflow guard here.

The test sets a near‑max balance but never executes the transition, so regressions in overflow handling wouldn’t be caught. Suggest running the transition and asserting the expected overflow/consensus error (or mark as TODO/ignored).


11377-11385: Make the fee‑drain condition explicit.

This assumes fees exceed 1000 credits so the first input is fully drained; if fees drop below that, the test won’t exercise the index‑shift bug. Consider computing the fee (or asserting the first entry is removed) before checking second_final.

Copy link
Copy Markdown
Contributor

@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: 1

🤖 Fix all issues with AI agents
In
`@packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create_from_addresses/tests.rs`:
- Around line 11371-11454: The test must assert that the first input was
actually drained/removed so the index-shift scenario is exercised: after
committing the transaction (after
platform.drive.grove.commit_transaction(...).expect("should commit")), fetch the
balance/nonce for the address identified by variable first using
fetch_balance_and_nonce and assert that the balance is zero or the address is
absent, ensuring first_input was fully consumed before proceeding to check the
second address; update the test around variables first, first_balance,
first_input, fetch_balance_and_nonce, and the existing assertions so the check
runs immediately before computing second_remaining_before_fee and the subsequent
assertion about the second_final balance.
🧹 Nitpick comments (2)
packages/rs-dpp/src/util/is_non_zero_fibonacci_number.rs (1)

42-51: Test comments describe old floating-point implementation in present tense.

The comments at lines 44-46 state that is_perfect_square "casts u64 to f64 and checks sqrt().fract() == 0.0", but the actual implementation now uses integer Newton's method. This could confuse future maintainers.

Consider updating the comments to past tense to clarify these describe the previous implementation that was fixed:

📝 Suggested documentation update
     /// AUDIT M6: Floating-point imprecision in is_perfect_square for large values.
     ///
-    /// `is_perfect_square` casts u64 to f64 and checks `sqrt().fract() == 0.0`.
-    /// f64 has only 52 bits of mantissa, so for values above 2^52, precision is lost.
-    /// This means `is_perfect_square` can return incorrect results for large numbers.
+    /// The previous `is_perfect_square` implementation cast u64 to f64 and checked
+    /// `sqrt().fract() == 0.0`. f64 has only 52 bits of mantissa, so for values
+    /// above 2^52, precision was lost, causing incorrect results for large numbers.
+    /// This was fixed by replacing the floating-point approach with integer Newton's method.
     ///
-    /// Currently not exploitable for `core_fee_per_byte` (u32), but the function
-    /// accepts u64 and is technically unsound for large inputs.
-    ///
-    /// Location: rs-dpp/src/util/is_non_zero_fibonacci_number.rs:1-3
+    /// This test verifies the integer implementation doesn't have false positives.
packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_credit_withdrawal/tests.rs (1)

3923-3988: Derive large_balance from max_withdrawal_amount to keep the test future-proof.

large_balance is hard-coded to 600 DASH while the withdrawal amount is sourced from platform_version.system_limits.max_withdrawal_amount. If the max changes, this test can fail due to insufficient balance even though the intent is to validate the max withdrawal boundary. Consider tying the balance to the max plus a small fee buffer.

Suggested update
-            // Balance large enough to cover max withdrawal + fees
-            let large_balance = dash_to_credits!(600.0);
+            let max_withdrawal = platform_version.system_limits.max_withdrawal_amount;
+            // Balance large enough to cover max withdrawal + fees
+            let large_balance = max_withdrawal.saturating_add(dash_to_credits!(10.0));
             setup_address_with_balance(&mut platform, input_address, 0, large_balance);
@@
-            // Withdraw the max allowed amount (500 Dash = 50_000_000_000_000 credits)
-            let max_withdrawal = platform_version.system_limits.max_withdrawal_amount;
+            // Withdraw the max allowed amount
             inputs.insert(input_address, (1 as AddressNonce, max_withdrawal));

@shumkov shumkov merged commit f449f11 into v3.1-dev Feb 5, 2026
83 of 85 checks passed
@shumkov shumkov deleted the fix/auditReportFixes branch February 5, 2026 05:25
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.

2 participants