Skip to content

feat: record DAPI address ban reason and expose via platform-wallet FFI#3890

Merged
QuantumExplorer merged 4 commits into
v3.1-devfrom
claude/dapi-ban-reason
Jun 14, 2026
Merged

feat: record DAPI address ban reason and expose via platform-wallet FFI#3890
QuantumExplorer merged 4 commits into
v3.1-devfrom
claude/dapi-ban-reason

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jun 14, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

When the DAPI client bans an address, it tracked only ban_count and banned_until — the underlying error (the reason) was logged at tracing::warn! and then discarded. This surfaced during iOS QA: a shielded operation failed with Dapi client error: no available addresses to use, and there was no way for the app to show why the nodes were banned. This change records the ban reason and exposes the ban list (with reasons) up through platform-wallet and its FFI so clients like the iOS app can read it.

What was done?

Threaded a ban reason through the stack (additive, non-breaking):

  • rs-dapi-client (address_list.rs): added AddressStatus.ban_reason: Option<String>; new ban_with_reason() stores it (ban() preserved as a None-reason shim); unban() clears it. Added public AddressBanInfo { uri, banned, ban_count, banned_until, reason } and AddressList::ban_info() -> Vec<AddressBanInfo> snapshot getter (exported from the crate root). The runtime ban site in update_address_ban_status (dapi_client.rs) now passes error.to_string().
  • dash-sdk: Sdk::address_ban_info() delegating to address_list().ban_info().
  • platform-wallet: PlatformWalletManager::address_ban_info_blocking() -> Vec<AddressBanInfoSnapshot> from its Arc<Sdk> (banned_until projected to epoch-ms so the FFI stays chrono-free).
  • platform-wallet-ffi: platform_wallet_manager_address_ban_info(...) + _free, struct AddressBanInfoFFI { address, banned, ban_count, banned_until_ms, reason }, following the existing platform_wallet_tracked_asset_locks_list/_free array convention.
  • swift-sdk: PlatformWalletManager.addressBanInfo() -> [AddressBanInfo] — a thin bridge (no logic), per the swift-sdk architecture rules.

How Has This Been Tested?

  • cargo fmt --all --check clean.
  • cargo check --all-targets + cargo clippy --all-targets clean on rs-dapi-client, dash-sdk, platform-wallet, platform-wallet-ffi.
  • cargo test -p rs-dapi-client → 94 pass, including 8 new tests (ban stores reason / ban-without-reason is None / unban clears it at both AddressStatus and AddressList levels / ban_info reflects banned/unbanned/empty).
  • FFI header regenerated (host + aarch64-apple-ios-sim); unified xcframework assembled; the Swift wrapper matches the regenerated header.

Note: the full iOS app build currently fails at EmitSwiftModule due to a pre-existing, unrelated duplicate ContestedResourceVoteChoiceFFI emission in rs-sdk-ffi.h (from #3883) — being fixed in a separate change. It does not touch any code in this PR; the Swift wrapper here is a mechanical bridge over the regenerated header.

Breaking Changes

None. ban() keeps its signature; the new AddressStatus field is private; all additions are new public items.

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

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Addresses can now be banned with an optional human-readable reason.
    • Added address ban-list inspection APIs that return a snapshot including effective ban status, ban count, optional expiration, and the recorded reason.
    • Exposed the ban-list snapshot through SDK/FFI and Swift for diagnostics.
  • Bug Fixes

    • When retry-based bans occur, the stored ban reason now includes the underlying error text.
  • Tests

    • Extended coverage for reason-aware banning, unbanning, and snapshot behavior.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@QuantumExplorer, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 second. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01fc9fa1-2673-4353-a046-6717a5025654

📥 Commits

Reviewing files that changed from the base of the PR and between d40564c and b8ea71b.

📒 Files selected for processing (5)
  • packages/rs-dapi-client/src/address_ban_info.rs
  • packages/rs-dapi-client/src/address_list.rs
  • packages/rs-dapi-client/src/lib.rs
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/BannedAddressesView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/OptionsView.swift
📝 Walkthrough

Walkthrough

Adds optional ban-reason tracking to DAPI address banning. AddressStatus gains a ban_reason field and a ban_with_reason method; AddressList gains ban_info() returning owned snapshots. The ban call-site in dapi_client now passes the stringified error as the reason. A new AddressBanInfo type propagates through the Rust SDK, a platform wallet snapshot accessor, a C FFI layer with paired alloc/free functions, and a Swift binding.

Changes

DAPI Address Ban Reason Feature

Layer / File(s) Summary
Core ban-reason data model and AddressBanInfo snapshot
packages/rs-dapi-client/src/address_list.rs
AddressStatus gains ban_reason field and ban_with_reason method; AddressBanInfo public snapshot struct added; AddressList::ban_with_reason and ban_info() implemented; tests cover reason storage, clearing on unban, and ban_info() behavior.
Ban call-site update and SDK re-exports
packages/rs-dapi-client/src/dapi_client.rs, packages/rs-dapi-client/src/lib.rs, packages/rs-sdk/src/sdk.rs
update_address_ban_status switches to ban_with_reason passing the stringified error; AddressBanInfo is re-exported from both rs-dapi-client and rs-sdk; Sdk::address_ban_info() delegates to AddressList::ban_info().
Platform wallet AddressBanInfoSnapshot and blocking accessor
packages/rs-platform-wallet/src/manager/accessors.rs
AddressBanInfoSnapshot struct introduced; address_ban_info_blocking() synchronously fetches SDK results and projects banned_until to millisecond i64 timestamp.
C FFI struct, accessor, and deallocator
packages/rs-platform-wallet-ffi/src/core_wallet_types.rs, packages/rs-platform-wallet-ffi/src/manager_diagnostics.rs
AddressBanInfoFFI C-ABI struct defined with heap-owned string fields; platform_wallet_manager_address_ban_info allocates and returns array; platform_wallet_manager_address_ban_info_free frees per-entry strings and parent array.
Swift AddressBanInfo struct and addressBanInfo() method
packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerDiagnostics.swift
PlatformWalletManager.AddressBanInfo struct added; addressBanInfo() calls FFI accessor, converts C strings and banned_until_ms to Swift optionals, frees FFI memory, and returns mapped array.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Suggested labels

ready for final review

Suggested reviewers

  • shumkov

Poem

🐇 A node gets banned, but now we know why,
The error's preserved, no reason to lie.
From Rust to the FFI, then Swift's gentle hand,
The ban info flows as the engineers planned.
Hop hop, little bunny, the diagnostics are clear—
Every banned address has its reason right here!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main feature: recording DAPI address ban reasons and exposing them via platform-wallet FFI, which directly aligns with the primary changes across the entire changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/dapi-ban-reason

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/rs-dapi-client/src/dapi_client.rs (1)

190-190: ⚡ Quick win

Add a direct regression check for reason propagation in this call path.

Line 190 changes behavior materially, but current tests only verify that banning happened. Add one assertion in the retryable-error test that address_list.ban_info()[0].reason is populated after update_address_ban_status.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/rs-dapi-client/src/dapi_client.rs` at line 190, The ban_with_reason
call on line 190 now passes error details as the reason, but the existing
retryable-error test only verifies that banning occurred without checking if the
reason was actually propagated. Add an assertion in the retryable-error test
that directly verifies the reason field is populated by accessing
address_list.ban_info()[0].reason after the update_address_ban_status call
completes, ensuring the error message was correctly stored in the ban
information.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/rs-platform-wallet-ffi/src/manager_diagnostics.rs`:
- Around line 820-824: The ban_count field assignment uses an unsafe raw cast
(as u32) on s.ban_count which can silently truncate values on 64-bit targets,
causing incorrect diagnostics to be reported through FFI. Replace the raw cast
in the ban_count: s.ban_count as u32 line with a checked conversion method (such
as try_into() with appropriate error handling, or use .min(u32::MAX) for
explicit saturation) to prevent truncation and ensure accurate diagnostic
information is passed to the FFI caller.

In
`@packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerDiagnostics.swift`:
- Line 515: The current code in PlatformWalletManagerDiagnostics.swift converts
NULL addresses to empty strings using optional mapping with nil coalescing,
which creates non-actionable diagnostic entries. Replace this mapping approach
with compactMap to filter out entries where entry.address is NULL entirely,
ensuring only entries with valid addresses are retained and included in the
diagnostic output. This will prevent empty string addresses from appearing in
the ban info entries.

---

Nitpick comments:
In `@packages/rs-dapi-client/src/dapi_client.rs`:
- Line 190: The ban_with_reason call on line 190 now passes error details as the
reason, but the existing retryable-error test only verifies that banning
occurred without checking if the reason was actually propagated. Add an
assertion in the retryable-error test that directly verifies the reason field is
populated by accessing address_list.ban_info()[0].reason after the
update_address_ban_status call completes, ensuring the error message was
correctly stored in the ban information.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3da25ac1-f764-4101-a91c-9aa69ff9ae6a

📥 Commits

Reviewing files that changed from the base of the PR and between defc1d5 and 36bdc55.

📒 Files selected for processing (8)
  • packages/rs-dapi-client/src/address_list.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-dapi-client/src/lib.rs
  • packages/rs-platform-wallet-ffi/src/core_wallet_types.rs
  • packages/rs-platform-wallet-ffi/src/manager_diagnostics.rs
  • packages/rs-platform-wallet/src/manager/accessors.rs
  • packages/rs-sdk/src/sdk.rs
  • packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerDiagnostics.swift

Comment thread packages/rs-platform-wallet-ffi/src/manager_diagnostics.rs
The DAPI client's ban list previously tracked only ban_count and
banned_until; the error that caused a ban was logged then discarded.
Capture it so it can be surfaced to clients (e.g. the iOS app's
diagnostics) when a node becomes unavailable.

- rs-dapi-client: add AddressStatus.ban_reason; ban_with_reason() stores
  it (ban() kept as a None-reason shim); unban() clears it. New public
  AddressBanInfo { uri, banned, ban_count, banned_until, reason } and
  AddressList::ban_info() snapshot getter. The runtime ban site in
  update_address_ban_status now records error.to_string().
- dash-sdk: Sdk::address_ban_info() over address_list().ban_info().
- platform-wallet: PlatformWalletManager::address_ban_info_blocking()
  returning an owned snapshot (banned_until projected to epoch-ms).
- platform-wallet-ffi: platform_wallet_manager_address_ban_info() +
  _free, struct AddressBanInfoFFI { address, banned, ban_count,
  banned_until_ms, reason }, following the tracked-asset-locks array
  convention.
- swift-sdk: PlatformWalletManager.addressBanInfo() -> [AddressBanInfo]
  thin bridge.

Additive and non-breaking (ban() signature preserved; new field is
private). rs-dapi-client gains 8 unit tests covering reason set/clear
and ban_info reflection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer force-pushed the claude/dapi-ban-reason branch from 36bdc55 to c4c372e Compare June 14, 2026 07:48
@QuantumExplorer

Copy link
Copy Markdown
Member Author

Rebased onto latest v3.1-dev, which now includes #3892 — that resolves the pre-existing ContestedResourceVoteChoiceFFI header collision noted above. ✅

Verified end-to-end on this rebased branch: ./build_ios.sh --target sim builds clean through ** BUILD SUCCEEDED ** for the full SwiftExampleApp (not just the xcframework), so the regenerated header is collision-free and the new platform_wallet_manager_address_ban_info FFI + PlatformWalletManager.addressBanInfo() Swift wrapper compile and link. Rust side: cargo fmt --check clean, check/clippy --all-targets clean on all four crates, cargo test -p rs-dapi-client 94 pass (8 new).

QuantumExplorer and others added 3 commits June 14, 2026 10:06
- platform-wallet-ffi: saturate ban_count usize->u32 conversion
  (u32::try_from(..).unwrap_or(u32::MAX)) instead of a raw cast that
  could silently truncate on 64-bit.
- swift-sdk: addressBanInfo() uses compactMap and skips entries with a
  NULL address rather than surfacing a non-actionable blank row.
- rs-dapi-client: the retryable-error ban test now also asserts the ban
  reason is propagated through update_address_ban_status.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the public AddressBanInfo snapshot out of address_list.rs into
address_ban_info.rs (address_list.rs now holds only Address /
AddressStatus / AddressList). The crate-root re-export path
(rs_dapi_client::AddressBanInfo) is unchanged, so rs-sdk / platform-wallet
consumers are unaffected. Drive-by: fixed a pre-existing broken intra-doc
link ([get_live_address] -> [`Self::get_live_address`]) in the same file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surface the DAPI address ban list (with reasons) in Settings → Data,
as the last diagnostic explorer before "Manage Local Data". The view is
pure presentation over the existing PlatformWalletManager.addressBanInfo()
wrapper: lists each address with a Banned/Live badge, ban count,
banned-until, and the recorded reason; refresh via toolbar + pull-to-
refresh; ContentUnavailableView empty state noting an empty list can mean
no bans or an unseeded address pool.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer merged commit b3efe4b into v3.1-dev Jun 14, 2026
3 checks passed
@QuantumExplorer
QuantumExplorer deleted the claude/dapi-ban-reason branch June 14, 2026 08:45
@github-actions

Copy link
Copy Markdown
Contributor

✅ DashSDKFFI.xcframework built for this PR.

SwiftPM (host the zip at a stable URL, then use):

.binaryTarget(
  name: "DashSDKFFI",
  url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
  checksum: "593b5946c8f07d28fae1da04f58ea5001a222aea9694aab68397293a4af05aca"
)

Xcode manual integration:

  • Download 'DashSDKFFI.xcframework' artifact from the run link above.
  • Drag it into your app target (Frameworks, Libraries & Embedded Content) and set Embed & Sign.
  • If using the Swift wrapper package, point its binaryTarget to the xcframework location or add the package and place the xcframework at the expected path.

@codecov

codecov Bot commented Jun 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.20%. Comparing base (65042eb) to head (b8ea71b).
⚠️ Report is 20 commits behind head on v3.1-dev.

Additional details and impacted files
@@              Coverage Diff              @@
##           v3.1-dev    #3890       +/-   ##
=============================================
- Coverage     87.22%   71.20%   -16.03%     
=============================================
  Files          2641       20     -2621     
  Lines        328569     2837   -325732     
=============================================
- Hits         286597     2020   -284577     
+ Misses        41972      817    -41155     
Components Coverage Δ
dpp ∅ <ø> (∅)
drive ∅ <ø> (∅)
drive-abci ∅ <ø> (∅)
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value ∅ <ø> (∅)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

1 participant