Skip to content

Conversation

@shumkov
Copy link
Collaborator

@shumkov shumkov commented Jan 7, 2026

Issue being fixed or feature implemented

When SDK doesn't have enough live addresses to retry a faulty request it returns "no available addresses" error which is actaully hiding the real issue.

What was done?

  • Keep last error around and return it in case if "no available addresses" is returned

How Has This Been Tested?

With existing and additional unit tests

Breaking Changes

In case if live addresses are less than expected retries you get and error of the last request failure instead of "no available addresses"

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

  • New Features

    • Per-request capture of the last transport error and a new error variant exposing it when address retries are exhausted.
  • Bug Fixes

    • More accurate classification of retryable vs non-retryable failures and clearer reporting for address-exhaustion cases.
    • Preserves original error context on final failure to aid troubleshooting.
  • Chores

    • Cross-platform async sleep helpers for WASM/non‑WASM and several error types made cloneable; small SDK signature refinements.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions github-actions bot added this to the v3.0.0 milestone Jan 7, 2026
@github-actions
Copy link

github-actions bot commented Jan 7, 2026

✅ gRPC Query Coverage Report

================================================================================
gRPC Query Coverage Report - NEW QUERIES ONLY
================================================================================

Total queries in proto: 53
Previously known queries: 47
New queries found: 6

================================================================================

New Query Implementation Status:
--------------------------------------------------------------------------------
✓ getAddressInfo                                /home/runner/work/platform/platform/packages/rs-sdk/src/platform/query.rs
✓ getAddressesBranchState                       /home/runner/work/platform/platform/packages/rs-sdk/src/platform/address_sync/mod.rs
✓ getAddressesInfos                             /home/runner/work/platform/platform/packages/rs-sdk/src/platform/fetch_many.rs
✓ getAddressesTrunkState                        /home/runner/work/platform/platform/packages/rs-sdk/src/platform/address_sync/mod.rs
✓ getRecentAddressBalanceChanges                /home/runner/work/platform/platform/packages/rs-sdk/src/platform/query.rs
✓ getRecentCompactedAddressBalanceChanges       /home/runner/work/platform/platform/packages/rs-sdk/src/platform/query.rs

================================================================================
Summary:
--------------------------------------------------------------------------------
New queries implemented: 6 (100.0%)
New queries missing: 0 (0.0%)

Total known queries: 53
  - Implemented: 50
  - Not implemented: 2
  - Excluded: 1

Not implemented queries:
  - getConsensusParams
  - getTokenPreProgrammedDistributions

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Adds per-execution last-error tracking and a new non-retriable NoAvailableAddressesToRetry error variant; extends CanRetry with is_no_available_addresses(), derives Clone for several errors, introduces platform sleep helpers, updates retry control flow and several public function signatures and dependencies.

Changes

Cohort / File(s) Summary of changes (attention areas)
Dapi client core
packages/rs-dapi-client/src/dapi_client.rs
Introduces explicit retry loop with per-execution last_error (Arc<Mutex<Option<TransportError>>>), records last transport error on failures, wraps final address-exhaustion into NoAvailableAddressesToRetry(Box<TransportError>) when present, and adds tracing around request/transport execution.
Retry execution glue
packages/rs-dapi-client/src/executor.rs, packages/rs-dapi-client/src/lib.rs
Adds is_no_available_addresses() to CanRetry (default false); ExecutionError delegates is_no_available_addresses() to inner error. Trait extension may affect implementors.
Transport & wasm sleep
packages/rs-dapi-client/src/transport.rs, packages/rs-dapi-client/src/transport/wasm_channel.rs
Adds sleep(Duration) helpers: tokio-based for non-wasm and into_send_sleep for wasm (returns BoxFuture<'static, ()>). Implements Clone for TransportError and a Mockable impl for Box<TransportError> under mocks — cloning reconstructs tonic::Status (source lost).
Retry sync logic (SDK)
packages/rs-sdk/src/sync.rs
Reworks retry to return ExecutionResult<R, Error> (concrete Error), makes future_factory_fn mutable and futures yield Error, adds R: Send, records last_meaningful_error, and wraps NoAvailableAddresses into NoAvailableAddressesToRetry when applicable. Public signature changed.
SDK error types
packages/rs-sdk/src/error.rs, packages/wasm-sdk/src/error.rs
Adds NoAvailableAddressesToRetry(Box<Error>) variant and is_no_available_addresses() mapping; WasmSdkError conversion extended to map NoAvailableAddressesToRetry.
Error derivations / mocks
packages/rs-dapi-client/src/address_list.rs, packages/rs-dapi-client/src/mock.rs
Added Clone derive to AddressListError and MockError.
Platform broadcast signatures
packages/rs-sdk/src/platform/transition/broadcast.rs
Adds Send bound to generic T on wait_for_response and broadcast_and_wait (trait + impl) — tightens consumer requirements.
Dependencies / Cargo
packages/rs-dapi-client/Cargo.toml, packages/rs-sdk/Cargo.toml
Adds tokio = { version = "1.40", features = ["time"] } under non-wasm target in rs-dapi-client; bumps backon in rs-sdk from 1.2 → 1.3.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as DapiClient
    participant Addr as AddressList
    participant TransportFactory as TransportFactory
    participant Transport as TransportClient
    participant Timer as Sleep

    Note over Client,Addr: execute starts — loop-based retry with shared last_error

    Client->>Addr: pick_next_address()
    Client->>TransportFactory: create_transport_client(address)
    TransportFactory->>Transport: new client
    Client->>Transport: execute(request)
    alt transport OK
        Transport-->>Client: success(response)
        Client->>Addr: unban/update_success(address)
        Client-->>Caller: return response (with retries)
    else transport Err
        Transport-->>Client: TransportError
        Client->>Client: record last_error (Arc<Mutex>)
        Client->>Addr: ban/update_failure(address)
        Client->>Timer: sleep(retry_delay)
        Timer-->>Client: wake
        Client->>Client: increment retry count
        alt addresses exhausted AND last_error present
            Client-->>Caller: NoAvailableAddressesToRetry(last_error)
        else addresses exhausted WITHOUT last_error
            Client-->>Caller: NoAvailableAddresses
        else non-retriable or retries exhausted
            Client-->>Caller: propagate error
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I nibble logs and count the hops,
stash the last slip in cozy locks,
ban, unban, sleep, then try—
when addresses vanish, I still sigh.
Little rabbit watches, wide-eyed and spry. 🐇✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: preserving and returning the last meaningful error when no available addresses remain for retries, which is the core purpose of this breaking change.
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

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87e95df and 011253e.

📒 Files selected for processing (1)
  • packages/rs-sdk/src/sync.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Rust code must pass cargo clippy --workspace linter checks
Rust code must be formatted using cargo fmt --all

**/*.rs: Use 4-space indent for Rust files
Follow rustfmt defaults and keep code clippy-clean for Rust modules
Use snake_case for Rust module names
Use PascalCase for Rust type names
Use SCREAMING_SNAKE_CASE for Rust constants

Files:

  • packages/rs-sdk/src/sync.rs
🧠 Learnings (16)
📚 Learning: 2024-10-25T10:35:05.071Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2266
File: packages/rs-sdk/src/sync.rs:171-221
Timestamp: 2024-10-25T10:35:05.071Z
Learning: In `packages/rs-sdk/src/sync.rs`, within the `retry` function, the closure passed to `.when()` in `backon::Retryable::retry` is not executed concurrently, so using a mutable variable like `retries` inside it does not lead to data races.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-22T10:53:12.111Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/dapi_client.rs:137-139
Timestamp: 2024-10-22T10:53:12.111Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when passing data into asynchronous code, ensure that data structures are `Send + Sync`. Using `Arc<AtomicUsize>` is necessary for the retry counter.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-10-09T15:59:33.162Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2716
File: packages/rs-dapi/src/clients/tenderdash_client.rs:0-0
Timestamp: 2025-10-09T15:59:33.162Z
Learning: In the rs-dapi codebase, the `Workers::spawn` method (in `packages/rs-dapi/src/sync.rs`) accepts futures that return `Result<O, E>`, not `()`. Its signature is `fn spawn<F, O, E>(&self, fut: F) where F: Future<Output = Result<O, E>> + Send + 'static`. The method handles error logging internally, so spawned tasks should return Results directly without additional error handling wrappers.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-18T13:52:36.233Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-dapi-client/src/dapi_client.rs:267-267
Timestamp: 2024-10-18T13:52:36.233Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when handling retries, if `can_retry()` returns `None`, we should treat it as non-retryable and not retry. This is the preferred approach for safety, and future implementations of `CanRetry` may return `None` less frequently.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T14:44:01.184Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/src/executor.rs:38-38
Timestamp: 2024-10-29T14:44:01.184Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, for the structs `ExecutionError<E>` and `ExecutionResponse<R>`, only the serde derives (`Serialize`, `Deserialize`) should be conditional based on the "mocks" feature; the other derives (`Debug`, `Clone`, `Eq`, `PartialEq`) should always be present.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T14:40:54.727Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-sdk/src/core/transaction.rs:0-0
Timestamp: 2024-10-29T14:40:54.727Z
Learning: In `packages/rs-sdk/src/platform/document_query.rs` and `packages/rs-sdk/src/core/transaction.rs`, certain places don't implement `IntoInner`, so direct error mappings cannot be simplified using `IntoInner`. A TODO comment has been added to address this in a future PR.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-11-20T14:14:54.721Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2337
File: packages/rs-dapi-client/src/executor.rs:161-212
Timestamp: 2024-11-20T14:14:54.721Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `WrapWithExecutionResult` trait supports on-the-fly type conversions using the `From` trait, which is useful when using the `?` operator to return errors.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-10-09T15:59:41.943Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2716
File: packages/rs-dapi/src/clients/tenderdash_client.rs:0-0
Timestamp: 2025-10-09T15:59:41.943Z
Learning: In packages/rs-dapi, the Workers::spawn method accepts futures that return Result<O, E> where E: Into<DapiError>. It handles error logging internally, so spawned tasks should return Result types rather than handling errors inside the task. The Workers implementation automatically logs worker failures.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-01-23T09:23:32.740Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2405
File: packages/rs-sdk/src/sync.rs:88-95
Timestamp: 2025-01-23T09:23:32.740Z
Learning: The `block_on` function in `packages/rs-sdk/src/sync.rs` requires true blocking behavior, which is why a non-blocking WebAssembly implementation using `wasm_bindgen_futures::spawn_local` is not suitable.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:24.653Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/sync.rs:68-71
Timestamp: 2024-10-10T10:30:24.653Z
Learning: In synchronous code within a Tokio runtime, we cannot await spawned task handles (`JoinHandle`), so it's acceptable to check if the task is finished and abort it if necessary.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:19.883Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/mock/sdk.rs:90-95
Timestamp: 2024-10-10T10:30:19.883Z
Learning: In `packages/rs-sdk/src/mock/sdk.rs`, the `load_expectations` method in `MockDashPlatformSdk` remains asynchronous (`async`) for backward compatibility, even though it now delegates to the synchronous `load_expectations_sync` method.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:25.283Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/mock/sdk.rs:129-130
Timestamp: 2024-10-10T10:30:25.283Z
Learning: In the codebase `packages/rs-sdk/src/mock/sdk.rs`, `tokio::sync::Mutex` is used instead of `std::sync::Mutex` because `std::sync::MutexGuard` is not `Send` and cannot be used in async contexts.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-01-23T09:23:32.740Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2405
File: packages/rs-sdk/src/sync.rs:88-95
Timestamp: 2025-01-23T09:23:32.740Z
Learning: The `block_on` function in `packages/rs-sdk/src/sync.rs` is currently only used in tests, and its WebAssembly implementation is deferred until there's a user request for it.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-28T10:38:49.538Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/executor.rs:49-56
Timestamp: 2024-10-28T10:38:49.538Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `ExecutionResponse` struct always has a non-optional `address` field of type `Address` because an address is always present when there's a successful response.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T14:16:40.972Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/src/address_list.rs:63-73
Timestamp: 2024-10-29T14:16:40.972Z
Learning: In the Rust SDK, it's acceptable to use `expect()` that panics on errors in the `Mockable` trait implementations during testing, and the `Mockable` trait will be refactored in a future PR.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-18T15:43:32.447Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-sdk/src/sdk.rs:654-658
Timestamp: 2024-10-18T15:43:32.447Z
Learning: In `packages/rs-sdk/src/sdk.rs`, within the `verify_metadata_height` function, when using `compare_exchange` on `last_seen_height`, it's acceptable to use `Ordering::Relaxed` for the failure ordering, as any inconsistency would only trigger an extra loop iteration without affecting correctness.

Applied to files:

  • packages/rs-sdk/src/sync.rs
🧬 Code graph analysis (1)
packages/rs-sdk/src/sync.rs (3)
packages/rs-dapi-client/src/transport.rs (2)
  • sleep (29-31)
  • sleep (35-37)
packages/rs-dapi-client/src/dapi_client.rs (4)
  • update_address_ban_status (140-199)
  • update_address_ban_status (290-290)
  • update_address_ban_status (344-344)
  • address_list (133-135)
packages/rs-dapi-client/src/request_settings.rs (1)
  • default (39-46)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: Rust packages (rs-dapi-client) / Tests
  • GitHub Check: Rust packages (dash-sdk) / Check each feature
  • GitHub Check: Rust packages (dash-sdk) / Tests
  • GitHub Check: Rust packages (rs-dapi-client) / Check each feature
  • GitHub Check: Rust packages (dash-sdk) / Linting
  • GitHub Check: Rust packages (dash-sdk) / Unused dependencies
  • GitHub Check: Rust packages (wasm-sdk) / Tests
  • GitHub Check: Rust packages (rs-dapi-client) / Linting
  • GitHub Check: Rust packages (wasm-sdk) / Unused dependencies
  • GitHub Check: Rust packages (wasm-sdk) / Linting
  • GitHub Check: Rust packages (rs-dapi-client) / Unused dependencies
  • GitHub Check: Rust packages (rs-sdk-ffi) / Tests
  • GitHub Check: Build Docker images (Drive, drive, drive-abci, SDK_TEST_DATA=true
    ) / Build Drive image
  • GitHub Check: Build Docker images (RS-DAPI, rs-dapi, rs-dapi) / Build RS-DAPI image
  • GitHub Check: Build Docker images (Dashmate helper, dashmate-helper, dashmate-helper) / Build Dashmate helper image
  • GitHub Check: Build JS packages / Build JS
  • GitHub Check: Rust crates security audit
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios-sim)
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios)
🔇 Additional comments (5)
packages/rs-sdk/src/sync.rs (5)

7-14: LGTM: Imports align with new retry implementation

The new imports support the refactored retry logic, including the platform sleep helper, address ban management, and error handling.


165-174: LGTM: Function signature correctly updated for new retry flow

The signature changes support the loop-based retry approach:

  • mut future_factory_fn allows repeated invocation
  • Concrete Error type provides clearer semantics than generic error types
  • R: Send bound is appropriate for async execution

175-181: LGTM: Proper setup for tracking retry state

The initialization correctly sets up:

  • max_retries from settings
  • total_retries counter for global retry accounting
  • current_settings for per-iteration adjustment
  • last_meaningful_error for preserving context when addresses are exhausted

183-259: LGTM: Retry loop correctly implements last-meaningful-error preservation

The refactored retry logic is well-structured:

  1. Address ban management (line 187): Properly integrates with the address list banning mechanism
  2. NoAvailableAddresses handling (lines 193-209): Correctly wraps the last meaningful error when addresses are exhausted, or returns as-is if no prior error exists
  3. Retry accounting (lines 212-234): Accurately tracks total requests across all attempts (including lower-layer retries) and respects the configured maximum
  4. Settings adjustment (line 245): Dynamically reduces the retry budget for lower layers using saturating_sub to prevent underflow

The logic ensures that when all addresses become unavailable, callers receive the root cause (e.g., StaleNode) rather than a generic "no addresses" message.


408-503: LGTM: Comprehensive test coverage for new error-wrapping behavior

The new tests thoroughly validate the last-meaningful-error preservation:

  1. test_retry_returns_last_meaningful_error_on_no_addresses (lines 417-472): Confirms that when addresses are exhausted after a meaningful failure, the original error (e.g., StaleNode) is wrapped in NoAvailableAddressesToRetry rather than being lost
  2. test_retry_returns_no_addresses_if_no_previous_error (lines 477-503): Ensures that if the first request immediately encounters NoAvailableAddresses, it's returned as-is without spurious wrapping

Both tests correctly exercise the conditional wrapping logic introduced in the main loop.


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.

Copy link
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

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86524da and 1da8ea2.

📒 Files selected for processing (5)
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-dapi-client/src/executor.rs
  • packages/rs-dapi-client/src/lib.rs
  • packages/rs-sdk/src/error.rs
  • packages/rs-sdk/src/platform/transition/broadcast.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Rust code must pass cargo clippy --workspace linter checks
Rust code must be formatted using cargo fmt --all

**/*.rs: Use 4-space indent for Rust files
Follow rustfmt defaults and keep code clippy-clean for Rust modules
Use snake_case for Rust module names
Use PascalCase for Rust type names
Use SCREAMING_SNAKE_CASE for Rust constants

Files:

  • packages/rs-dapi-client/src/lib.rs
  • packages/rs-dapi-client/src/executor.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/platform/transition/broadcast.rs
  • packages/rs-sdk/src/error.rs
🧠 Learnings (13)
📓 Common learnings
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-dapi-client/src/dapi_client.rs:267-267
Timestamp: 2024-10-18T13:52:36.233Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when handling retries, if `can_retry()` returns `None`, we should treat it as non-retryable and not retry. This is the preferred approach for safety, and future implementations of `CanRetry` may return `None` less frequently.
📚 Learning: 2024-10-18T13:52:36.233Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-dapi-client/src/dapi_client.rs:267-267
Timestamp: 2024-10-18T13:52:36.233Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when handling retries, if `can_retry()` returns `None`, we should treat it as non-retryable and not retry. This is the preferred approach for safety, and future implementations of `CanRetry` may return `None` less frequently.

Applied to files:

  • packages/rs-dapi-client/src/lib.rs
  • packages/rs-dapi-client/src/executor.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/error.rs
📚 Learning: 2024-11-28T13:49:17.301Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2317
File: packages/rs-dapi-client/src/address_list.rs:175-180
Timestamp: 2024-11-28T13:49:17.301Z
Learning: In Rust code in `packages/rs-dapi-client/src/address_list.rs`, do not change the interface of deprecated methods like `add_uri`, even to fix potential panics.

Applied to files:

  • packages/rs-dapi-client/src/lib.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/error.rs
📚 Learning: 2024-10-22T10:53:12.111Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/dapi_client.rs:137-139
Timestamp: 2024-10-22T10:53:12.111Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when passing data into asynchronous code, ensure that data structures are `Send + Sync`. Using `Arc<AtomicUsize>` is necessary for the retry counter.

Applied to files:

  • packages/rs-dapi-client/src/lib.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/platform/transition/broadcast.rs
  • packages/rs-sdk/src/error.rs
📚 Learning: 2024-10-29T10:42:00.521Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/Cargo.toml:22-22
Timestamp: 2024-10-29T10:42:00.521Z
Learning: In `packages/rs-dapi-client/Cargo.toml`, `backon` will not work without the `tokio-sleep` feature in our setup, so it's unnecessary to declare `tokio-sleep` as a separate feature in the `[features]` section.

Applied to files:

  • packages/rs-dapi-client/src/lib.rs
📚 Learning: 2024-10-28T10:38:49.538Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/executor.rs:49-56
Timestamp: 2024-10-28T10:38:49.538Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `ExecutionResponse` struct always has a non-optional `address` field of type `Address` because an address is always present when there's a successful response.

Applied to files:

  • packages/rs-dapi-client/src/lib.rs
  • packages/rs-dapi-client/src/executor.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/error.rs
📚 Learning: 2024-11-28T14:08:53.428Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2351
File: packages/rs-dapi-client/src/address_list.rs:241-250
Timestamp: 2024-11-28T14:08:53.428Z
Learning: In the `IntoIterator` implementation for `AddressList` in `packages/rs-dapi-client/src/address_list.rs`, since `self` is consumed, holding a write lock while converting to an iterator does not block other operations.

Applied to files:

  • packages/rs-dapi-client/src/lib.rs
📚 Learning: 2024-10-25T10:35:05.071Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2266
File: packages/rs-sdk/src/sync.rs:171-221
Timestamp: 2024-10-25T10:35:05.071Z
Learning: In `packages/rs-sdk/src/sync.rs`, within the `retry` function, the closure passed to `.when()` in `backon::Retryable::retry` is not executed concurrently, so using a mutable variable like `retries` inside it does not lead to data races.

Applied to files:

  • packages/rs-dapi-client/src/lib.rs
📚 Learning: 2024-10-29T14:44:01.184Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/src/executor.rs:38-38
Timestamp: 2024-10-29T14:44:01.184Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, for the structs `ExecutionError<E>` and `ExecutionResponse<R>`, only the serde derives (`Serialize`, `Deserialize`) should be conditional based on the "mocks" feature; the other derives (`Debug`, `Clone`, `Eq`, `PartialEq`) should always be present.

Applied to files:

  • packages/rs-dapi-client/src/executor.rs
  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-11-20T14:14:54.721Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2337
File: packages/rs-dapi-client/src/executor.rs:161-212
Timestamp: 2024-11-20T14:14:54.721Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `WrapWithExecutionResult` trait supports on-the-fly type conversions using the `From` trait, which is useful when using the `?` operator to return errors.

Applied to files:

  • packages/rs-dapi-client/src/executor.rs
📚 Learning: 2024-10-22T10:51:16.910Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/transport.rs:51-51
Timestamp: 2024-10-22T10:51:16.910Z
Learning: In implementations of `TransportClient`, only `DapiClientError` is used as the `Error` type, and it implements `std::error::Error`.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/error.rs
📚 Learning: 2024-10-29T14:13:35.584Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/dapi-grpc/src/mock.rs:95-109
Timestamp: 2024-10-29T14:13:35.584Z
Learning: In test code (e.g., `packages/dapi-grpc/src/mock.rs`), panicking on deserialization errors is acceptable behavior.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2025-10-09T15:59:28.329Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2716
File: packages/rs-dapi/src/services/platform_service/broadcast_state_transition.rs:181-187
Timestamp: 2025-10-09T15:59:28.329Z
Learning: In `packages/rs-dapi/src/services/platform_service/broadcast_state_transition.rs`, the maintainer requires logging full state transition bytes (`tx_bytes = hex::encode(st_bytes)`) at debug level when a state transition passes CheckTx but is removed from the block by the proposer, to facilitate debugging of this rare edge case.

Applied to files:

  • packages/rs-sdk/src/platform/transition/broadcast.rs
🧬 Code graph analysis (3)
packages/rs-dapi-client/src/lib.rs (3)
packages/rs-dapi-client/src/dapi_client.rs (1)
  • is_no_available_addresses (59-61)
packages/rs-dapi-client/src/executor.rs (1)
  • is_no_available_addresses (83-85)
packages/rs-sdk/src/error.rs (1)
  • is_no_available_addresses (253-258)
packages/rs-dapi-client/src/executor.rs (3)
packages/rs-dapi-client/src/dapi_client.rs (1)
  • is_no_available_addresses (59-61)
packages/rs-dapi-client/src/lib.rs (1)
  • is_no_available_addresses (92-94)
packages/rs-sdk/src/error.rs (1)
  • is_no_available_addresses (253-258)
packages/rs-dapi-client/src/dapi_client.rs (3)
packages/rs-dapi-client/src/executor.rs (1)
  • is_no_available_addresses (83-85)
packages/rs-dapi-client/src/lib.rs (1)
  • is_no_available_addresses (92-94)
packages/rs-sdk/src/error.rs (1)
  • is_no_available_addresses (253-258)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Rust packages (rs-dapi-client) / Linting
  • GitHub Check: Rust packages (wasm-sdk) / Tests
  • GitHub Check: Rust packages (dash-sdk) / Check each feature
  • GitHub Check: Rust packages (dash-sdk) / Unused dependencies
  • GitHub Check: Build Docker images (RS-DAPI, rs-dapi, rs-dapi) / Build RS-DAPI image
  • GitHub Check: Build Docker images (Drive, drive, drive-abci, SDK_TEST_DATA=true
    ) / Build Drive image
  • GitHub Check: Build JS packages / Build JS
  • GitHub Check: Rust crates security audit
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios-sim)
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios)
🔇 Additional comments (7)
packages/rs-sdk/src/platform/transition/broadcast.rs (4)

24-24: LGTM! Send bound is necessary for async safety.

The + Send bound on the generic type T is required for async methods where the returned future may be moved across threads. This ensures thread safety in async contexts.

Based on learnings, when passing data into asynchronous code, data structures must be Send + Sync compatible.


29-29: LGTM! Consistent with trait definition.

The + Send bound is correctly applied to maintain consistency with the trait method signature at Line 24.


94-94: LGTM! Implementation matches trait signature.

The + Send bound is correctly applied to the implementation, matching the trait definition.


263-263: LGTM! Complete and consistent implementation.

The + Send bound is correctly applied across all async methods that use this generic parameter, ensuring thread safety.

packages/rs-dapi-client/src/executor.rs (1)

82-85: LGTM! Proper delegation pattern.

The is_no_available_addresses method correctly delegates to the inner error's implementation, maintaining consistency with the existing can_retry delegation pattern at lines 79-81.

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

58-61: LGTM! Correct identification of NoAvailableAddresses.

The implementation correctly uses matches! to identify the DapiClientError::NoAvailableAddresses variant, consistent with the trait's default implementation and the broader retry logic pattern.

packages/rs-sdk/src/error.rs (1)

252-258: LGTM! Proper SDK-level integration.

The implementation correctly identifies the NoAvailableAddresses error through the SDK's error hierarchy, using nested pattern matching to check for Error::DapiClientError(DapiClientError::NoAvailableAddresses). This enables proper retry logic at the SDK layer.

@shumkov shumkov self-assigned this Jan 7, 2026
@shumkov shumkov moved this to In review / testing in Platform team Jan 7, 2026
Copy link
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: 0

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

231-236: Fixed retry delay may need to be configurable.

The retry_delay is hardcoded to 10ms. While the comment at line 258-259 in sync.rs suggests "we use different server, so no real delay needed," consider whether this should be configurable via RequestSettings for scenarios where longer backoff might be beneficial (e.g., rate limiting).


286-319: Consider simplifying the error handling flow.

The pattern of creating an ExecutionError, wrapping it in Err, passing to update_address_ban_status, then unwrapping it back is functional but awkward. Consider extracting the ban update into a helper that takes the error directly.

♻️ Suggested simplification
-                        let execution_error = ExecutionError {
-                            inner: DapiClientError::Transport(transport_error),
-                            retries,
-                            address: Some(address.clone()),
-                        };
-
-                        // Update ban status
-                        let error_result: ExecutionResult<R::Response, DapiClientError> =
-                            Err(execution_error);
-                        update_address_ban_status::<R::Response, DapiClientError>(
-                            &self.address_list,
-                            &error_result,
-                            &applied_settings,
-                        );
-
-                        // Unwrap the error back
-                        let execution_error = error_result.unwrap_err();
+                        let execution_error = ExecutionError {
+                            inner: DapiClientError::Transport(transport_error),
+                            retries,
+                            address: Some(address.clone()),
+                        };
+
+                        // Update ban status
+                        update_address_ban_status::<R::Response, DapiClientError>(
+                            &self.address_list,
+                            &Err(execution_error.clone()),
+                            &applied_settings,
+                        );

This would require ExecutionError to implement Clone, which may not be desirable. Alternatively, a helper function that accepts the error components directly could be cleaner.

packages/rs-sdk/src/sync.rs (1)

15-25: Duplicate sleep helpers across crates.

This sleep function is duplicated in three locations:

  • packages/rs-sdk/src/sync.rs (here)
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/transport/wasm_channel.rs

Consider re-exporting from rs-dapi-client to avoid duplication, or document why each crate needs its own copy.

♻️ Alternative: re-export from rs-dapi-client

In packages/rs-sdk/src/sync.rs:

-/// Sleep for the given duration.
-#[cfg(not(target_arch = "wasm32"))]
-pub async fn sleep(duration: Duration) {
-    tokio::time::sleep(duration).await;
-}
-
-/// Sleep for the given duration.
-#[cfg(target_arch = "wasm32")]
-pub async fn sleep(duration: Duration) {
-    gloo_timers::future::sleep(duration).await;
-}
+pub use rs_dapi_client::transport::sleep;

This would require making sleep public in rs_dapi_client::transport.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1da8ea2 and de0f4e8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • packages/rs-dapi-client/Cargo.toml
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-sdk/Cargo.toml
  • packages/rs-sdk/src/error.rs
  • packages/rs-sdk/src/sync.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rs-sdk/src/error.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Rust code must pass cargo clippy --workspace linter checks
Rust code must be formatted using cargo fmt --all

**/*.rs: Use 4-space indent for Rust files
Follow rustfmt defaults and keep code clippy-clean for Rust modules
Use snake_case for Rust module names
Use PascalCase for Rust type names
Use SCREAMING_SNAKE_CASE for Rust constants

Files:

  • packages/rs-sdk/src/sync.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/dapi_client.rs
🧠 Learnings (26)
📚 Learning: 2024-10-29T10:42:00.521Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/Cargo.toml:22-22
Timestamp: 2024-10-29T10:42:00.521Z
Learning: In `packages/rs-dapi-client/Cargo.toml`, `backon` will not work without the `tokio-sleep` feature in our setup, so it's unnecessary to declare `tokio-sleep` as a separate feature in the `[features]` section.

Applied to files:

  • packages/rs-sdk/Cargo.toml
  • packages/rs-sdk/src/sync.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/Cargo.toml
📚 Learning: 2025-01-19T07:36:46.042Z
Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 2431
File: packages/rs-drive/Cargo.toml:55-60
Timestamp: 2025-01-19T07:36:46.042Z
Learning: The grovedb dependencies in packages/rs-drive/Cargo.toml and related files are intentionally kept at specific revisions rather than using the latest stable version, with plans to update them at a later time.

Applied to files:

  • packages/rs-sdk/Cargo.toml
  • packages/rs-dapi-client/Cargo.toml
📚 Learning: 2024-10-18T15:39:51.172Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-sdk/src/sdk.rs:585-585
Timestamp: 2024-10-18T15:39:51.172Z
Learning: The 'platform' project uses Rust version 1.80, so code in 'packages/rs-sdk' can use features available in Rust 1.80, such as the `abs_diff()` method.

Applied to files:

  • packages/rs-sdk/Cargo.toml
  • packages/rs-sdk/src/sync.rs
  • packages/rs-dapi-client/Cargo.toml
📚 Learning: 2025-11-25T13:10:23.481Z
Learnt from: CR
Repo: dashpay/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T13:10:23.481Z
Learning: Use WASM bindings to connect Rust and JavaScript code

Applied to files:

  • packages/rs-sdk/Cargo.toml
📚 Learning: 2025-11-25T13:10:23.481Z
Learnt from: CR
Repo: dashpay/platform PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T13:10:23.481Z
Learning: Use WebAssembly as a bridge between Rust and JavaScript implementations

Applied to files:

  • packages/rs-sdk/Cargo.toml
📚 Learning: 2025-01-23T09:23:32.740Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2405
File: packages/rs-sdk/src/sync.rs:88-95
Timestamp: 2025-01-23T09:23:32.740Z
Learning: The `block_on` function in `packages/rs-sdk/src/sync.rs` is currently only used in tests, and its WebAssembly implementation is deferred until there's a user request for it.

Applied to files:

  • packages/rs-sdk/Cargo.toml
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-22T10:53:12.111Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/dapi_client.rs:137-139
Timestamp: 2024-10-22T10:53:12.111Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when passing data into asynchronous code, ensure that data structures are `Send + Sync`. Using `Arc<AtomicUsize>` is necessary for the retry counter.

Applied to files:

  • packages/rs-sdk/src/sync.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-dapi-client/Cargo.toml
📚 Learning: 2024-10-25T10:35:05.071Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2266
File: packages/rs-sdk/src/sync.rs:171-221
Timestamp: 2024-10-25T10:35:05.071Z
Learning: In `packages/rs-sdk/src/sync.rs`, within the `retry` function, the closure passed to `.when()` in `backon::Retryable::retry` is not executed concurrently, so using a mutable variable like `retries` inside it does not lead to data races.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:19.883Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/mock/sdk.rs:90-95
Timestamp: 2024-10-10T10:30:19.883Z
Learning: In `packages/rs-sdk/src/mock/sdk.rs`, the `load_expectations` method in `MockDashPlatformSdk` remains asynchronous (`async`) for backward compatibility, even though it now delegates to the synchronous `load_expectations_sync` method.

Applied to files:

  • packages/rs-sdk/src/sync.rs
  • packages/rs-dapi-client/src/transport.rs
📚 Learning: 2025-10-09T15:59:33.162Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2716
File: packages/rs-dapi/src/clients/tenderdash_client.rs:0-0
Timestamp: 2025-10-09T15:59:33.162Z
Learning: In the rs-dapi codebase, the `Workers::spawn` method (in `packages/rs-dapi/src/sync.rs`) accepts futures that return `Result<O, E>`, not `()`. Its signature is `fn spawn<F, O, E>(&self, fut: F) where F: Future<Output = Result<O, E>> + Send + 'static`. The method handles error logging internally, so spawned tasks should return Results directly without additional error handling wrappers.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-10-09T15:59:41.943Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2716
File: packages/rs-dapi/src/clients/tenderdash_client.rs:0-0
Timestamp: 2025-10-09T15:59:41.943Z
Learning: In packages/rs-dapi, the Workers::spawn method accepts futures that return Result<O, E> where E: Into<DapiError>. It handles error logging internally, so spawned tasks should return Result types rather than handling errors inside the task. The Workers implementation automatically logs worker failures.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-01-23T09:23:32.740Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2405
File: packages/rs-sdk/src/sync.rs:88-95
Timestamp: 2025-01-23T09:23:32.740Z
Learning: The `block_on` function in `packages/rs-sdk/src/sync.rs` requires true blocking behavior, which is why a non-blocking WebAssembly implementation using `wasm_bindgen_futures::spawn_local` is not suitable.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:24.653Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/sync.rs:68-71
Timestamp: 2024-10-10T10:30:24.653Z
Learning: In synchronous code within a Tokio runtime, we cannot await spawned task handles (`JoinHandle`), so it's acceptable to check if the task is finished and abort it if necessary.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:25.283Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/mock/sdk.rs:129-130
Timestamp: 2024-10-10T10:30:25.283Z
Learning: In the codebase `packages/rs-sdk/src/mock/sdk.rs`, `tokio::sync::Mutex` is used instead of `std::sync::Mutex` because `std::sync::MutexGuard` is not `Send` and cannot be used in async contexts.

Applied to files:

  • packages/rs-sdk/src/sync.rs
  • packages/rs-dapi-client/src/transport.rs
📚 Learning: 2024-10-18T13:52:36.233Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-dapi-client/src/dapi_client.rs:267-267
Timestamp: 2024-10-18T13:52:36.233Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when handling retries, if `can_retry()` returns `None`, we should treat it as non-retryable and not retry. This is the preferred approach for safety, and future implementations of `CanRetry` may return `None` less frequently.

Applied to files:

  • packages/rs-sdk/src/sync.rs
  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-28T10:38:49.538Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/executor.rs:49-56
Timestamp: 2024-10-28T10:38:49.538Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `ExecutionResponse` struct always has a non-optional `address` field of type `Address` because an address is always present when there's a successful response.

Applied to files:

  • packages/rs-sdk/src/sync.rs
  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-29T14:44:01.184Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/src/executor.rs:38-38
Timestamp: 2024-10-29T14:44:01.184Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, for the structs `ExecutionError<E>` and `ExecutionResponse<R>`, only the serde derives (`Serialize`, `Deserialize`) should be conditional based on the "mocks" feature; the other derives (`Debug`, `Clone`, `Eq`, `PartialEq`) should always be present.

Applied to files:

  • packages/rs-sdk/src/sync.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-29T14:16:40.972Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/src/address_list.rs:63-73
Timestamp: 2024-10-29T14:16:40.972Z
Learning: In the Rust SDK, it's acceptable to use `expect()` that panics on errors in the `Mockable` trait implementations during testing, and the `Mockable` trait will be refactored in a future PR.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-18T15:43:32.447Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-sdk/src/sdk.rs:654-658
Timestamp: 2024-10-18T15:43:32.447Z
Learning: In `packages/rs-sdk/src/sdk.rs`, within the `verify_metadata_height` function, when using `compare_exchange` on `last_seen_height`, it's acceptable to use `Ordering::Relaxed` for the failure ordering, as any inconsistency would only trigger an extra loop iteration without affecting correctness.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-22T10:51:16.910Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/transport.rs:51-51
Timestamp: 2024-10-22T10:51:16.910Z
Learning: In implementations of `TransportClient`, only `DapiClientError` is used as the `Error` type, and it implements `std::error::Error`.

Applied to files:

  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-11-28T13:49:17.301Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2317
File: packages/rs-dapi-client/src/address_list.rs:175-180
Timestamp: 2024-11-28T13:49:17.301Z
Learning: In Rust code in `packages/rs-dapi-client/src/address_list.rs`, do not change the interface of deprecated methods like `add_uri`, even to fix potential panics.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-dapi-client/Cargo.toml
📚 Learning: 2024-11-20T14:14:54.721Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2337
File: packages/rs-dapi-client/src/executor.rs:161-212
Timestamp: 2024-11-20T14:14:54.721Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `WrapWithExecutionResult` trait supports on-the-fly type conversions using the `From` trait, which is useful when using the `?` operator to return errors.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-12-05T12:59:22.044Z
Learnt from: lklimek
Repo: dashpay/platform PR: 1924
File: packages/rs-dapi-client/src/request_settings.rs:74-74
Timestamp: 2024-12-05T12:59:22.044Z
Learning: The `AppliedRequestSettings` struct in `packages/rs-dapi-client/src/request_settings.rs` is intended for internal use within the monorepo and is not part of the public API.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-29T14:13:35.584Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/dapi-grpc/src/mock.rs:95-109
Timestamp: 2024-10-29T14:13:35.584Z
Learning: In test code (e.g., `packages/dapi-grpc/src/mock.rs`), panicking on deserialization errors is acceptable behavior.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-30T11:04:33.634Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-sdk/src/platform/fetch_unproved.rs:0-0
Timestamp: 2024-10-30T11:04:33.634Z
Learning: In `packages/rs-sdk/src/platform/fetch_unproved.rs`, the `execute()` method consumes the request object, so cloning the request is necessary before passing it to `execute()` and `maybe_from_unproved_with_metadata`.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2025-09-03T16:37:11.605Z
Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 2756
File: packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/update_state_masternode_list/v0/mod.rs:11-11
Timestamp: 2025-09-03T16:37:11.605Z
Learning: In packages/rs-dpp/Cargo.toml, the abci feature already includes core_rpc_client, and core_rpc_client is defined as ["dep:dashcore-rpc"]. This means rs-drive-abci can access dashcore-rpc types through dpp when using the abci feature.

Applied to files:

  • packages/rs-dapi-client/Cargo.toml
🧬 Code graph analysis (2)
packages/rs-sdk/src/sync.rs (5)
packages/rs-dapi-client/src/dapi_client.rs (4)
  • update_address_ban_status (141-200)
  • update_address_ban_status (295-295)
  • update_address_ban_status (352-352)
  • address_list (134-136)
packages/rs-dapi-client/src/transport.rs (2)
  • sleep (29-31)
  • sleep (35-37)
packages/rs-dapi-client/src/transport/wasm_channel.rs (1)
  • sleep (100-102)
packages/rs-dapi-client/src/executor.rs (1)
  • default (101-107)
packages/rs-dapi-client/src/request_settings.rs (1)
  • default (39-46)
packages/rs-dapi-client/src/dapi_client.rs (4)
packages/rs-sdk/src/error.rs (1)
  • is_no_available_addresses (258-264)
packages/rs-dapi-client/src/lib.rs (1)
  • is_no_available_addresses (92-94)
packages/rs-dapi-client/src/transport.rs (4)
  • method_name (62-62)
  • request_name (52-54)
  • response_name (57-59)
  • with_uri_and_settings (145-149)
packages/rs-dapi-client/src/transport/grpc.rs (2)
  • with_uri_and_settings (30-49)
  • with_uri_and_settings (67-86)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Rust packages (rs-dapi-client) / Linting
  • GitHub Check: Rust packages (dash-sdk) / Linting
  • GitHub Check: Rust packages (rs-dapi-client) / Check each feature
  • GitHub Check: Rust packages (rs-sdk-ffi) / Tests
  • GitHub Check: Rust packages (wasm-sdk) / Tests
  • GitHub Check: Rust packages (dash-sdk) / Check each feature
  • GitHub Check: Rust packages (dash-sdk) / Unused dependencies
  • GitHub Check: Rust packages (dash-sdk) / Tests
  • GitHub Check: Build Docker images (Dashmate helper, dashmate-helper, dashmate-helper) / Build Dashmate helper image
  • GitHub Check: Build Docker images (Drive, drive, drive-abci, SDK_TEST_DATA=true
    ) / Build Drive image
  • GitHub Check: Build Docker images (RS-DAPI, rs-dapi, rs-dapi) / Build RS-DAPI image
  • GitHub Check: Rust crates security audit
  • GitHub Check: Build JS packages / Build JS
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios)
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios-sim)
🔇 Additional comments (11)
packages/rs-sdk/Cargo.toml (1)

48-50: LGTM!

The gloo-timers dependency with features = ["futures"] is correctly added for wasm32 targets, consistent with the same dependency in rs-dapi-client/Cargo.toml.

packages/rs-dapi-client/Cargo.toml (1)

24-28: LGTM!

The platform-specific tokio dependency with features = ["time"] is correctly added for non-wasm32 targets, enabling tokio::time::sleep used by the new sleep helper in transport.rs.

packages/rs-dapi-client/src/transport.rs (3)

27-37: LGTM!

The platform-specific sleep helpers correctly delegate to tokio::time::sleep for non-wasm and gloo_timers::future::sleep for wasm32 targets.


85-101: Clone implementation loses the original error source.

The comment at line 89-90 correctly documents that this manual Clone loses the original error source from tonic::Status. This is acceptable given that tonic::Status doesn't implement Clone, but callers should be aware that chained error context may be lost when cloning transport errors.


126-137: LGTM!

The Mockable implementation for Box<TransportError> correctly delegates to the underlying TransportError implementation.

packages/rs-dapi-client/src/dapi_client.rs (3)

34-40: LGTM!

The new NoAvailableAddressesToRetry variant correctly uses Box<TransportError> to preserve the last meaningful error while keeping the enum size reasonable.


63-70: LGTM!

The is_no_available_addresses implementation correctly identifies both address exhaustion variants.


238-382: LGTM on the retry loop refactor.

The loop-based retry mechanism correctly:

  • Tracks last_transport_error to preserve meaningful errors
  • Returns NoAvailableAddressesToRetry when addresses are exhausted after a transport error
  • Properly updates address ban status after each attempt
  • Respects max_retries limit with appropriate early exits
packages/rs-sdk/src/sync.rs (3)

176-184: Breaking change: retry function signature now uses concrete Error type.

The function signature changed from a generic error type E to the concrete Error type. This is a breaking change for callers that used custom error types. The PR is already marked as breaking (! in title), so this is expected.


194-269: LGTM on the retry logic refactor.

The loop-based retry mechanism correctly:

  • Tracks last_meaningful_error to preserve the last retryable error before address exhaustion
  • Returns NoAvailableAddressesToRetry wrapping the last meaningful error when is_no_available_addresses() is true
  • Properly updates settings for lower layers via current_settings.retries
  • Maintains accurate retry counting across nested retry attempts

418-514: LGTM on new test cases.

The new tests test_retry_returns_last_meaningful_error_on_no_addresses and test_retry_returns_no_addresses_if_no_previous_error provide excellent coverage for the new error wrapping behavior.

Copy link
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: 0

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

229-230: Consider making retry delay configurable with backoff.

The hardcoded 10ms delay is quite aggressive for network retries. This could lead to:

  • Rapid retry storms if multiple clients fail simultaneously
  • Unnecessary load on recovering nodes

Consider either:

  1. Making this configurable via RequestSettings
  2. Adding exponential backoff (e.g., retry_delay * 2^retries)
  3. Adding jitter to prevent thundering herd
Example with exponential backoff
 let max_retries = applied_settings.retries;
-let retry_delay = Duration::from_millis(10);
+let base_retry_delay = Duration::from_millis(10);
 
 // ... later in the retry logic:
-transport::sleep(retry_delay).await;
+let backoff_delay = base_retry_delay * (1 << retries.min(5)); // cap at 320ms
+transport::sleep(backoff_delay).await;
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de0f4e8 and 491e20d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 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/mock.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/transport/wasm_channel.rs
  • packages/rs-sdk/Cargo.toml
  • packages/rs-sdk/src/sync.rs
  • packages/wasm-sdk/src/error.rs
💤 Files with no reviewable changes (1)
  • packages/rs-sdk/Cargo.toml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Rust code must pass cargo clippy --workspace linter checks
Rust code must be formatted using cargo fmt --all

**/*.rs: Use 4-space indent for Rust files
Follow rustfmt defaults and keep code clippy-clean for Rust modules
Use snake_case for Rust module names
Use PascalCase for Rust type names
Use SCREAMING_SNAKE_CASE for Rust constants

Files:

  • packages/rs-dapi-client/src/mock.rs
  • packages/rs-dapi-client/src/address_list.rs
  • packages/wasm-sdk/src/error.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/transport/wasm_channel.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
🧠 Learnings (24)
📓 Common learnings
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/dapi_client.rs:137-139
Timestamp: 2024-10-22T10:53:12.111Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when passing data into asynchronous code, ensure that data structures are `Send + Sync`. Using `Arc<AtomicUsize>` is necessary for the retry counter.
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-dapi-client/src/dapi_client.rs:267-267
Timestamp: 2024-10-18T13:52:36.233Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when handling retries, if `can_retry()` returns `None`, we should treat it as non-retryable and not retry. This is the preferred approach for safety, and future implementations of `CanRetry` may return `None` less frequently.
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/transport.rs:51-51
Timestamp: 2024-10-22T10:51:16.910Z
Learning: In implementations of `TransportClient`, only `DapiClientError` is used as the `Error` type, and it implements `std::error::Error`.
Learnt from: shumkov
Repo: dashpay/platform PR: 2266
File: packages/rs-sdk/src/sync.rs:171-221
Timestamp: 2024-10-25T10:35:05.071Z
Learning: In `packages/rs-sdk/src/sync.rs`, within the `retry` function, the closure passed to `.when()` in `backon::Retryable::retry` is not executed concurrently, so using a mutable variable like `retries` inside it does not lead to data races.
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/Cargo.toml:22-22
Timestamp: 2024-10-29T10:42:00.521Z
Learning: In `packages/rs-dapi-client/Cargo.toml`, `backon` will not work without the `tokio-sleep` feature in our setup, so it's unnecessary to declare `tokio-sleep` as a separate feature in the `[features]` section.
📚 Learning: 2024-10-29T14:44:01.184Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/src/executor.rs:38-38
Timestamp: 2024-10-29T14:44:01.184Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, for the structs `ExecutionError<E>` and `ExecutionResponse<R>`, only the serde derives (`Serialize`, `Deserialize`) should be conditional based on the "mocks" feature; the other derives (`Debug`, `Clone`, `Eq`, `PartialEq`) should always be present.

Applied to files:

  • packages/rs-dapi-client/src/mock.rs
  • packages/rs-dapi-client/src/address_list.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T14:13:35.584Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/dapi-grpc/src/mock.rs:95-109
Timestamp: 2024-10-29T14:13:35.584Z
Learning: In test code (e.g., `packages/dapi-grpc/src/mock.rs`), panicking on deserialization errors is acceptable behavior.

Applied to files:

  • packages/rs-dapi-client/src/mock.rs
  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-29T14:16:40.972Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/src/address_list.rs:63-73
Timestamp: 2024-10-29T14:16:40.972Z
Learning: In the Rust SDK, it's acceptable to use `expect()` that panics on errors in the `Mockable` trait implementations during testing, and the `Mockable` trait will be refactored in a future PR.

Applied to files:

  • packages/rs-dapi-client/src/mock.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-11-28T13:49:17.301Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2317
File: packages/rs-dapi-client/src/address_list.rs:175-180
Timestamp: 2024-11-28T13:49:17.301Z
Learning: In Rust code in `packages/rs-dapi-client/src/address_list.rs`, do not change the interface of deprecated methods like `add_uri`, even to fix potential panics.

Applied to files:

  • packages/rs-dapi-client/src/address_list.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-11-28T14:08:53.428Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2351
File: packages/rs-dapi-client/src/address_list.rs:241-250
Timestamp: 2024-11-28T14:08:53.428Z
Learning: In the `IntoIterator` implementation for `AddressList` in `packages/rs-dapi-client/src/address_list.rs`, since `self` is consumed, holding a write lock while converting to an iterator does not block other operations.

Applied to files:

  • packages/rs-dapi-client/src/address_list.rs
📚 Learning: 2024-10-28T10:38:49.538Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/executor.rs:49-56
Timestamp: 2024-10-28T10:38:49.538Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `ExecutionResponse` struct always has a non-optional `address` field of type `Address` because an address is always present when there's a successful response.

Applied to files:

  • packages/rs-dapi-client/src/address_list.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-22T10:53:12.111Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/dapi_client.rs:137-139
Timestamp: 2024-10-22T10:53:12.111Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when passing data into asynchronous code, ensure that data structures are `Send + Sync`. Using `Arc<AtomicUsize>` is necessary for the retry counter.

Applied to files:

  • packages/rs-dapi-client/src/address_list.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/transport/wasm_channel.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-22T10:51:16.910Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/transport.rs:51-51
Timestamp: 2024-10-22T10:51:16.910Z
Learning: In implementations of `TransportClient`, only `DapiClientError` is used as the `Error` type, and it implements `std::error::Error`.

Applied to files:

  • packages/wasm-sdk/src/error.rs
  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-18T13:52:36.233Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-dapi-client/src/dapi_client.rs:267-267
Timestamp: 2024-10-18T13:52:36.233Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when handling retries, if `can_retry()` returns `None`, we should treat it as non-retryable and not retry. This is the preferred approach for safety, and future implementations of `CanRetry` may return `None` less frequently.

Applied to files:

  • packages/wasm-sdk/src/error.rs
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T10:42:00.521Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/Cargo.toml:22-22
Timestamp: 2024-10-29T10:42:00.521Z
Learning: In `packages/rs-dapi-client/Cargo.toml`, `backon` will not work without the `tokio-sleep` feature in our setup, so it's unnecessary to declare `tokio-sleep` as a separate feature in the `[features]` section.

Applied to files:

  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-dapi-client/src/transport/wasm_channel.rs
📚 Learning: 2024-10-10T10:30:19.883Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/mock/sdk.rs:90-95
Timestamp: 2024-10-10T10:30:19.883Z
Learning: In `packages/rs-sdk/src/mock/sdk.rs`, the `load_expectations` method in `MockDashPlatformSdk` remains asynchronous (`async`) for backward compatibility, even though it now delegates to the synchronous `load_expectations_sync` method.

Applied to files:

  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:25.283Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/mock/sdk.rs:129-130
Timestamp: 2024-10-10T10:30:25.283Z
Learning: In the codebase `packages/rs-sdk/src/mock/sdk.rs`, `tokio::sync::Mutex` is used instead of `std::sync::Mutex` because `std::sync::MutexGuard` is not `Send` and cannot be used in async contexts.

Applied to files:

  • packages/rs-dapi-client/src/transport.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T14:40:54.727Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-sdk/src/core/transaction.rs:0-0
Timestamp: 2024-10-29T14:40:54.727Z
Learning: In `packages/rs-sdk/src/platform/document_query.rs` and `packages/rs-sdk/src/core/transaction.rs`, certain places don't implement `IntoInner`, so direct error mappings cannot be simplified using `IntoInner`. A TODO comment has been added to address this in a future PR.

Applied to files:

  • packages/rs-dapi-client/src/transport.rs
📚 Learning: 2025-10-09T15:59:33.162Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2716
File: packages/rs-dapi/src/clients/tenderdash_client.rs:0-0
Timestamp: 2025-10-09T15:59:33.162Z
Learning: In the rs-dapi codebase, the `Workers::spawn` method (in `packages/rs-dapi/src/sync.rs`) accepts futures that return `Result<O, E>`, not `()`. Its signature is `fn spawn<F, O, E>(&self, fut: F) where F: Future<Output = Result<O, E>> + Send + 'static`. The method handles error logging internally, so spawned tasks should return Results directly without additional error handling wrappers.

Applied to files:

  • packages/rs-dapi-client/src/transport/wasm_channel.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-01-23T09:23:32.740Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2405
File: packages/rs-sdk/src/sync.rs:88-95
Timestamp: 2025-01-23T09:23:32.740Z
Learning: The `block_on` function in `packages/rs-sdk/src/sync.rs` is currently only used in tests, and its WebAssembly implementation is deferred until there's a user request for it.

Applied to files:

  • packages/rs-dapi-client/src/transport/wasm_channel.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-01-23T09:23:32.740Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2405
File: packages/rs-sdk/src/sync.rs:88-95
Timestamp: 2025-01-23T09:23:32.740Z
Learning: The `block_on` function in `packages/rs-sdk/src/sync.rs` requires true blocking behavior, which is why a non-blocking WebAssembly implementation using `wasm_bindgen_futures::spawn_local` is not suitable.

Applied to files:

  • packages/rs-dapi-client/src/transport/wasm_channel.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-11-20T14:14:54.721Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2337
File: packages/rs-dapi-client/src/executor.rs:161-212
Timestamp: 2024-11-20T14:14:54.721Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `WrapWithExecutionResult` trait supports on-the-fly type conversions using the `From` trait, which is useful when using the `?` operator to return errors.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-12-05T12:59:22.044Z
Learnt from: lklimek
Repo: dashpay/platform PR: 1924
File: packages/rs-dapi-client/src/request_settings.rs:74-74
Timestamp: 2024-12-05T12:59:22.044Z
Learning: The `AppliedRequestSettings` struct in `packages/rs-dapi-client/src/request_settings.rs` is intended for internal use within the monorepo and is not part of the public API.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-30T11:04:33.634Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-sdk/src/platform/fetch_unproved.rs:0-0
Timestamp: 2024-10-30T11:04:33.634Z
Learning: In `packages/rs-sdk/src/platform/fetch_unproved.rs`, the `execute()` method consumes the request object, so cloning the request is necessary before passing it to `execute()` and `maybe_from_unproved_with_metadata`.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-25T10:35:05.071Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2266
File: packages/rs-sdk/src/sync.rs:171-221
Timestamp: 2024-10-25T10:35:05.071Z
Learning: In `packages/rs-sdk/src/sync.rs`, within the `retry` function, the closure passed to `.when()` in `backon::Retryable::retry` is not executed concurrently, so using a mutable variable like `retries` inside it does not lead to data races.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-10-09T15:59:41.943Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2716
File: packages/rs-dapi/src/clients/tenderdash_client.rs:0-0
Timestamp: 2025-10-09T15:59:41.943Z
Learning: In packages/rs-dapi, the Workers::spawn method accepts futures that return Result<O, E> where E: Into<DapiError>. It handles error logging internally, so spawned tasks should return Result types rather than handling errors inside the task. The Workers implementation automatically logs worker failures.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:24.653Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/sync.rs:68-71
Timestamp: 2024-10-10T10:30:24.653Z
Learning: In synchronous code within a Tokio runtime, we cannot await spawned task handles (`JoinHandle`), so it's acceptable to check if the task is finished and abort it if necessary.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-18T15:43:32.447Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-sdk/src/sdk.rs:654-658
Timestamp: 2024-10-18T15:43:32.447Z
Learning: In `packages/rs-sdk/src/sdk.rs`, within the `verify_metadata_height` function, when using `compare_exchange` on `last_seen_height`, it's acceptable to use `Ordering::Relaxed` for the failure ordering, as any inconsistency would only trigger an extra loop iteration without affecting correctness.

Applied to files:

  • packages/rs-sdk/src/sync.rs
🧬 Code graph analysis (3)
packages/rs-dapi-client/src/transport.rs (1)
packages/rs-dapi-client/src/transport/wasm_channel.rs (3)
  • sleep (100-102)
  • into_send_sleep (108-110)
  • new (40-43)
packages/rs-dapi-client/src/transport/wasm_channel.rs (1)
packages/rs-dapi-client/src/transport.rs (2)
  • sleep (29-31)
  • sleep (35-37)
packages/rs-dapi-client/src/dapi_client.rs (2)
packages/rs-dapi-client/src/executor.rs (1)
  • is_no_available_addresses (83-85)
packages/rs-sdk/src/error.rs (1)
  • is_no_available_addresses (258-264)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Rust packages (wasm-sdk) / Linting
  • GitHub Check: Rust packages (rs-dapi-client) / Tests
  • GitHub Check: Rust packages (wasm-sdk) / Unused dependencies
  • GitHub Check: Rust packages (dash-sdk) / Formatting
  • GitHub Check: Rust packages (wasm-sdk) / Tests
  • GitHub Check: Rust packages (rs-sdk-ffi) / Tests
  • GitHub Check: Rust packages (dash-sdk) / Unused dependencies
  • GitHub Check: Rust packages (rs-dapi-client) / Check each feature
  • GitHub Check: Rust packages (rs-dapi-client) / Unused dependencies
  • GitHub Check: Rust packages (rs-dapi-client) / Linting
  • GitHub Check: Rust packages (dash-sdk) / Linting
  • GitHub Check: Rust packages (dash-sdk) / Tests
  • GitHub Check: Rust packages (dash-sdk) / Check each feature
  • GitHub Check: Build Docker images (RS-DAPI, rs-dapi, rs-dapi) / Build RS-DAPI image
  • GitHub Check: Build Docker images (Dashmate helper, dashmate-helper, dashmate-helper) / Build Dashmate helper image
  • GitHub Check: Build Docker images (Drive, drive, drive-abci, SDK_TEST_DATA=true
    ) / Build Drive image
  • GitHub Check: Build JS packages / Build JS
  • GitHub Check: Rust crates security audit
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios-sim)
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios)
🔇 Additional comments (16)
packages/rs-dapi-client/src/mock.rs (1)

208-208: LGTM! Clone derive added for error propagation.

Adding Clone to MockError aligns with the broader pattern across this PR where error types are equipped with Clone to support propagation through retry logic and error handling.

packages/rs-dapi-client/src/address_list.rs (1)

101-101: LGTM! Clone derive added for error handling.

Adding Clone to AddressListError is consistent with the error type enhancements across this PR, enabling error propagation through the new retry mechanism.

packages/rs-sdk/src/sync.rs (3)

165-174: Function signature correctly reflects breaking change.

The signature change from generic error E to concrete Error type aligns with the PR's breaking change objective. The addition of the R: Send bound and mut qualifier on future_factory_fn are appropriate for the new loop-based retry implementation.


175-259: Excellent retry loop implementation with proper error tracking.

The manual retry loop correctly implements the new behavior:

  1. Address ban status updates (line 187) are performed after each attempt, ensuring failed addresses are properly banned.
  2. NoAvailableAddresses handling (lines 193-210) correctly wraps the last meaningful error or returns as-is if no previous error exists.
  3. Retry counting (lines 212-214) properly accounts for both the initial request and lower-layer retries.
  4. Lower-layer retry limiting (line 245) uses saturating_sub to ensure the total retry budget isn't exceeded across layers.
  5. Error preservation (line 253) stores each meaningful error before the next iteration.

The logic handles all edge cases correctly and the flow is clear and maintainable.


408-503: Comprehensive test coverage for new error wrapping behavior.

The two new tests thoroughly validate the NoAvailableAddresses error handling:

  1. test_retry_returns_last_meaningful_error_on_no_addresses (lines 408-472) verifies that when addresses are exhausted after a meaningful error, the result wraps the last meaningful error in NoAvailableAddressesToRetry.

  2. test_retry_returns_no_addresses_if_no_previous_error (lines 474-503) ensures that if no addresses are available on the first attempt, the error is returned as-is without wrapping.

These tests cover the critical edge cases introduced by this PR.

packages/wasm-sdk/src/error.rs (1)

180-185: Correct error mapping for NoAvailableAddressesToRetry.

The mapping to WasmSdkErrorKind::DapiClientError is appropriate, and the message format clearly communicates both the address exhaustion and the underlying cause. Preserving the retriable flag maintains correct retry semantics.

packages/rs-dapi-client/src/transport/wasm_channel.rs (1)

105-110: Send-compatible sleep helper correctly implemented.

The into_send_sleep function properly wraps gloo_timers::future::sleep to provide a Send-compatible future for WASM targets. This complements the existing WasmBackonSleeper and supports the transport::sleep abstraction used in the retry logic.

packages/rs-dapi-client/src/transport.rs (3)

27-37: LGTM! Clean platform-specific sleep abstractions.

The sleep helpers correctly abstract over platform differences, using tokio::time::sleep for native and wasm_channel::into_send_sleep for WASM. This aligns with the existing pattern in wasm_channel.rs (lines 99-101, 107-109) and provides a unified API for the retry logic.


85-101: Reasonable workaround for cloning tonic::Status.

The reconstruction approach is sound, preserving the key fields (code, message, details, metadata). The comment on line 90 appropriately documents that the original error source is lost, which is an acceptable tradeoff for enabling the Clone requirement needed by the new retry mechanism.


126-137: LGTM!

Clean delegation to the underlying TransportError implementation, properly supporting the boxed error variant used in NoAvailableAddressesToRetry.

packages/rs-dapi-client/src/dapi_client.rs (6)

22-40: LGTM! Well-designed error variant for improved error reporting.

The new NoAvailableAddressesToRetry variant preserves the last meaningful error, which is the core improvement of this PR. Boxing the TransportError is appropriate to avoid bloating the enum size. The serde attribute correctly reuses the existing serde_mockable approach.


51-70: LGTM!

The CanRetry implementation correctly treats NoAvailableAddressesToRetry as non-retryable and the is_no_available_addresses() method properly identifies both exhaustion variants. This aligns with the pattern in packages/rs-sdk/src/error.rs (lines 257-263).


236-257: LGTM! Clean address exhaustion handling.

The loop correctly tracks last_transport_error and returns NoAvailableAddressesToRetry when addresses are exhausted after a transport failure, or NoAvailableAddresses when no addresses were ever available. The use of take() on line 241 properly moves ownership.


276-312: LGTM! Correct handling of transport client creation failures.

The error handling properly clones the transport error before moving it into ExecutionError, updates ban status, and stores the error for potential wrapping if addresses are exhausted. The retry logic correctly checks both can_retry_error and retries < max_retries.


314-348: LGTM! Well-instrumented transport execution.

The transport execution is properly wrapped with tracing spans for observability. The result handling correctly builds ExecutionResponse or ExecutionError and updates the address ban status appropriately.


350-375: LGTM! Correct retry and error propagation logic.

The result handling correctly:

  • Returns immediately on success
  • Stores transport errors for potential wrapping on address exhaustion
  • Respects retry limits and error retryability
  • Uses consistent sleep/log pattern with the client creation failure path

Copy link
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: 0

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

352-357: Consider logging on poisoned lock.

The code silently ignores lock poisoning. While this is defensive and unlikely to occur in practice, consider adding a trace/debug log when the lock fails to aid debugging in edge cases.

♻️ Optional: Add trace log for poisoned lock
                 if let DapiClientError::Transport(ref transport_error) = error.inner {
-                    if let Ok(mut last) = last_error.lock() {
-                        *last = Some(transport_error.clone());
+                    match last_error.lock() {
+                        Ok(mut last) => *last = Some(transport_error.clone()),
+                        Err(_) => tracing::trace!("failed to acquire last_error lock (poisoned)"),
                     }
                 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 491e20d and 87e95df.

📒 Files selected for processing (3)
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/Cargo.toml
  • packages/rs-sdk/src/sync.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Rust code must pass cargo clippy --workspace linter checks
Rust code must be formatted using cargo fmt --all

**/*.rs: Use 4-space indent for Rust files
Follow rustfmt defaults and keep code clippy-clean for Rust modules
Use snake_case for Rust module names
Use PascalCase for Rust type names
Use SCREAMING_SNAKE_CASE for Rust constants

Files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
🧠 Learnings (29)
📓 Common learnings
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-dapi-client/src/dapi_client.rs:267-267
Timestamp: 2024-10-18T13:52:36.233Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when handling retries, if `can_retry()` returns `None`, we should treat it as non-retryable and not retry. This is the preferred approach for safety, and future implementations of `CanRetry` may return `None` less frequently.
📚 Learning: 2024-10-29T10:42:00.521Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/Cargo.toml:22-22
Timestamp: 2024-10-29T10:42:00.521Z
Learning: In `packages/rs-dapi-client/Cargo.toml`, `backon` will not work without the `tokio-sleep` feature in our setup, so it's unnecessary to declare `tokio-sleep` as a separate feature in the `[features]` section.

Applied to files:

  • packages/rs-sdk/Cargo.toml
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-01-19T07:36:46.042Z
Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 2431
File: packages/rs-drive/Cargo.toml:55-60
Timestamp: 2025-01-19T07:36:46.042Z
Learning: The grovedb dependencies in packages/rs-drive/Cargo.toml and related files are intentionally kept at specific revisions rather than using the latest stable version, with plans to update them at a later time.

Applied to files:

  • packages/rs-sdk/Cargo.toml
📚 Learning: 2024-10-18T15:39:51.172Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-sdk/src/sdk.rs:585-585
Timestamp: 2024-10-18T15:39:51.172Z
Learning: The 'platform' project uses Rust version 1.80, so code in 'packages/rs-sdk' can use features available in Rust 1.80, such as the `abs_diff()` method.

Applied to files:

  • packages/rs-sdk/Cargo.toml
📚 Learning: 2024-12-05T09:29:38.918Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2375
File: packages/rs-drive-abci/Cargo.toml:61-63
Timestamp: 2024-12-05T09:29:38.918Z
Learning: In the `drive-abci` package, avoid adding unused dependencies like `hashbrown` to `Cargo.toml`. The team relies on CI to detect dependency version issues.

Applied to files:

  • packages/rs-sdk/Cargo.toml
📚 Learning: 2025-09-03T16:37:11.605Z
Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 2756
File: packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/update_state_masternode_list/v0/mod.rs:11-11
Timestamp: 2025-09-03T16:37:11.605Z
Learning: In packages/rs-dpp/Cargo.toml, the abci feature already includes core_rpc_client, and core_rpc_client is defined as ["dep:dashcore-rpc"]. This means rs-drive-abci can access dashcore-rpc types through dpp when using the abci feature.

Applied to files:

  • packages/rs-sdk/Cargo.toml
📚 Learning: 2025-03-11T09:39:23.071Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2489
File: packages/rs-dpp/Cargo.toml:32-32
Timestamp: 2025-03-11T09:39:23.071Z
Learning: In the Dash Platform project, dependencies are currently managed using Git repository references with tags (repo+tag format in Cargo.toml) rather than published crates, as the team is not currently publishing crates to crates.io.

Applied to files:

  • packages/rs-sdk/Cargo.toml
📚 Learning: 2024-10-22T10:53:12.111Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/dapi_client.rs:137-139
Timestamp: 2024-10-22T10:53:12.111Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when passing data into asynchronous code, ensure that data structures are `Send + Sync`. Using `Arc<AtomicUsize>` is necessary for the retry counter.

Applied to files:

  • packages/rs-sdk/Cargo.toml
  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-01-23T09:23:32.740Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2405
File: packages/rs-sdk/src/sync.rs:88-95
Timestamp: 2025-01-23T09:23:32.740Z
Learning: The `block_on` function in `packages/rs-sdk/src/sync.rs` is currently only used in tests, and its WebAssembly implementation is deferred until there's a user request for it.

Applied to files:

  • packages/rs-sdk/Cargo.toml
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-22T10:51:16.910Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/transport.rs:51-51
Timestamp: 2024-10-22T10:51:16.910Z
Learning: In implementations of `TransportClient`, only `DapiClientError` is used as the `Error` type, and it implements `std::error::Error`.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
📚 Learning: 2024-10-18T13:52:36.233Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-dapi-client/src/dapi_client.rs:267-267
Timestamp: 2024-10-18T13:52:36.233Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when handling retries, if `can_retry()` returns `None`, we should treat it as non-retryable and not retry. This is the preferred approach for safety, and future implementations of `CanRetry` may return `None` less frequently.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-11-28T13:49:17.301Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2317
File: packages/rs-dapi-client/src/address_list.rs:175-180
Timestamp: 2024-11-28T13:49:17.301Z
Learning: In Rust code in `packages/rs-dapi-client/src/address_list.rs`, do not change the interface of deprecated methods like `add_uri`, even to fix potential panics.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T14:44:01.184Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/src/executor.rs:38-38
Timestamp: 2024-10-29T14:44:01.184Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, for the structs `ExecutionError<E>` and `ExecutionResponse<R>`, only the serde derives (`Serialize`, `Deserialize`) should be conditional based on the "mocks" feature; the other derives (`Debug`, `Clone`, `Eq`, `PartialEq`) should always be present.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-28T10:38:49.538Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/executor.rs:49-56
Timestamp: 2024-10-28T10:38:49.538Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `ExecutionResponse` struct always has a non-optional `address` field of type `Address` because an address is always present when there's a successful response.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-11-20T14:14:54.721Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2337
File: packages/rs-dapi-client/src/executor.rs:161-212
Timestamp: 2024-11-20T14:14:54.721Z
Learning: In `packages/rs-dapi-client/src/executor.rs`, the `WrapWithExecutionResult` trait supports on-the-fly type conversions using the `From` trait, which is useful when using the `?` operator to return errors.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T14:40:54.727Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-sdk/src/core/transaction.rs:0-0
Timestamp: 2024-10-29T14:40:54.727Z
Learning: In `packages/rs-sdk/src/platform/document_query.rs` and `packages/rs-sdk/src/core/transaction.rs`, certain places don't implement `IntoInner`, so direct error mappings cannot be simplified using `IntoInner`. A TODO comment has been added to address this in a future PR.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T14:13:35.584Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/dapi-grpc/src/mock.rs:95-109
Timestamp: 2024-10-29T14:13:35.584Z
Learning: In test code (e.g., `packages/dapi-grpc/src/mock.rs`), panicking on deserialization errors is acceptable behavior.

Applied to files:

  • packages/rs-dapi-client/src/dapi_client.rs
  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-25T10:35:05.071Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2266
File: packages/rs-sdk/src/sync.rs:171-221
Timestamp: 2024-10-25T10:35:05.071Z
Learning: In `packages/rs-sdk/src/sync.rs`, within the `retry` function, the closure passed to `.when()` in `backon::Retryable::retry` is not executed concurrently, so using a mutable variable like `retries` inside it does not lead to data races.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-10-09T15:59:33.162Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2716
File: packages/rs-dapi/src/clients/tenderdash_client.rs:0-0
Timestamp: 2025-10-09T15:59:33.162Z
Learning: In the rs-dapi codebase, the `Workers::spawn` method (in `packages/rs-dapi/src/sync.rs`) accepts futures that return `Result<O, E>`, not `()`. Its signature is `fn spawn<F, O, E>(&self, fut: F) where F: Future<Output = Result<O, E>> + Send + 'static`. The method handles error logging internally, so spawned tasks should return Results directly without additional error handling wrappers.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:19.883Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/mock/sdk.rs:90-95
Timestamp: 2024-10-10T10:30:19.883Z
Learning: In `packages/rs-sdk/src/mock/sdk.rs`, the `load_expectations` method in `MockDashPlatformSdk` remains asynchronous (`async`) for backward compatibility, even though it now delegates to the synchronous `load_expectations_sync` method.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-12-05T12:59:22.044Z
Learnt from: lklimek
Repo: dashpay/platform PR: 1924
File: packages/rs-dapi-client/src/request_settings.rs:74-74
Timestamp: 2024-12-05T12:59:22.044Z
Learning: The `AppliedRequestSettings` struct in `packages/rs-dapi-client/src/request_settings.rs` is intended for internal use within the monorepo and is not part of the public API.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-10-09T15:59:41.943Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2716
File: packages/rs-dapi/src/clients/tenderdash_client.rs:0-0
Timestamp: 2025-10-09T15:59:41.943Z
Learning: In packages/rs-dapi, the Workers::spawn method accepts futures that return Result<O, E> where E: Into<DapiError>. It handles error logging internally, so spawned tasks should return Result types rather than handling errors inside the task. The Workers implementation automatically logs worker failures.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:24.653Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/sync.rs:68-71
Timestamp: 2024-10-10T10:30:24.653Z
Learning: In synchronous code within a Tokio runtime, we cannot await spawned task handles (`JoinHandle`), so it's acceptable to check if the task is finished and abort it if necessary.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2025-01-23T09:23:32.740Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2405
File: packages/rs-sdk/src/sync.rs:88-95
Timestamp: 2025-01-23T09:23:32.740Z
Learning: The `block_on` function in `packages/rs-sdk/src/sync.rs` requires true blocking behavior, which is why a non-blocking WebAssembly implementation using `wasm_bindgen_futures::spawn_local` is not suitable.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-10T10:30:25.283Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2232
File: packages/rs-sdk/src/mock/sdk.rs:129-130
Timestamp: 2024-10-10T10:30:25.283Z
Learning: In the codebase `packages/rs-sdk/src/mock/sdk.rs`, `tokio::sync::Mutex` is used instead of `std::sync::Mutex` because `std::sync::MutexGuard` is not `Send` and cannot be used in async contexts.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-11-25T01:17:02.001Z
Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 2347
File: packages/rs-drive/tests/query_tests.rs:438-460
Timestamp: 2024-11-25T01:17:02.001Z
Learning: In Rust test files (`packages/rs-drive/tests/query_tests.rs`), when code is used only in tests, defining explicit enums for fields (like the `status` field in the `Withdrawal` struct) may not be necessary; using primitive types is acceptable.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-30T11:19:59.163Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-sdk/tests/fetch/config.rs:233-233
Timestamp: 2024-10-30T11:19:59.163Z
Learning: In the Rust SDK's `rs-sdk/tests` integration tests (e.g., in `packages/rs-sdk/tests/fetch/config.rs`), we cannot create objects during tests because there is no support for object creation in this context. Therefore, hardcoded values for test identities must be used.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-29T14:16:40.972Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-dapi-client/src/address_list.rs:63-73
Timestamp: 2024-10-29T14:16:40.972Z
Learning: In the Rust SDK, it's acceptable to use `expect()` that panics on errors in the `Mockable` trait implementations during testing, and the `Mockable` trait will be refactored in a future PR.

Applied to files:

  • packages/rs-sdk/src/sync.rs
📚 Learning: 2024-10-30T11:04:33.634Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-sdk/src/platform/fetch_unproved.rs:0-0
Timestamp: 2024-10-30T11:04:33.634Z
Learning: In `packages/rs-sdk/src/platform/fetch_unproved.rs`, the `execute()` method consumes the request object, so cloning the request is necessary before passing it to `execute()` and `maybe_from_unproved_with_metadata`.

Applied to files:

  • packages/rs-sdk/src/sync.rs
🧬 Code graph analysis (1)
packages/rs-sdk/src/sync.rs (1)
packages/rs-dapi-client/src/dapi_client.rs (3)
  • update_address_ban_status (144-203)
  • update_address_ban_status (328-328)
  • address_list (137-139)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build Docker images (Drive, drive, drive-abci, SDK_TEST_DATA=true
    ) / Build Drive image
  • GitHub Check: Build JS packages / Build JS
  • GitHub Check: JS NPM security audit
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios-sim)
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios)
🔇 Additional comments (14)
packages/rs-dapi-client/src/dapi_client.rs (6)

10-10: LGTM!

Using std::sync::Mutex is appropriate here since the lock is held only briefly for cloning/storing the error, avoiding the overhead of an async mutex.


25-25: LGTM!

Adding Clone to DapiClientError is necessary for the new error-wrapping functionality and enables storing/propagating the last transport error.


37-43: Design choice acknowledged.

The separation between NoAvailableAddresses (empty list) and NoAvailableAddressesToRetry (exhausted after failures) provides distinct semantics. Using Box<TransportError> appropriately keeps the enum size balanced.


54-73: LGTM!

The CanRetry implementation correctly:

  • Marks NoAvailableAddressesToRetry as non-retryable
  • Provides is_no_available_addresses() to uniformly detect both address exhaustion variants

Based on learnings, treating these as non-retryable is the safe approach.


241-243: LGTM!

The Arc<Mutex<Option<TransportError>>> pattern correctly enables shared mutable state across retry iterations, consistent with how retries_counter_arc is used. Based on learnings, Arc is necessary for passing data into asynchronous code.


369-386: LGTM!

The error transformation logic correctly:

  • Detects NoAvailableAddresses errors
  • Wraps them with the last transport error when available
  • Preserves retry count and address metadata
  • Falls back gracefully when no last error exists
packages/rs-sdk/Cargo.toml (1)

43-43: LGTM! Dependency bump aligns with custom sleep implementation.

The backon dependency has been bumped to version 1.3, maintaining default-features = false without explicit feature flags. This configuration is intentional and aligns with the PR's introduction of custom platform sleep helpers for wasm/non-wasm environments.

Based on learnings, a previous setup required the tokio-sleep feature for backon. However, backon supports custom sleep implementations via the .sleep() method when default features are disabled, which appears to be the approach taken in this PR.

packages/rs-sdk/src/sync.rs (7)

7-7: LGTM: Import changes align with the new concrete Error type.

The addition of the concrete Error type import and the StdMutex alias are appropriate for the new last-error tracking mechanism. Using std::sync::Mutex (via StdMutex) instead of tokio::sync::Mutex is correct here since we're only storing a String and not holding the lock across await points.

Also applies to: 11-12, 17-17


175-183: LGTM: Function signature updated to use concrete Error type.

The signature change from generic E to concrete Error is a breaking change as noted in the PR objectives. This enables the new behavior of preserving and returning the last meaningful error when no addresses are available. The addition of the R: Send bound is necessary for cross-thread operations within the retry mechanism.


193-196: LGTM: Last error tracking implementation is sound.

The use of Arc<StdMutex<Option<String>>> to track the last meaningful error is appropriate given that Error doesn't implement Clone. Converting errors to strings trades type information for the ability to preserve error details, which is acceptable for this use case where we're providing better diagnostic information to the user.


252-256: LGTM: Last error tracking in notify closure handles lock failures gracefully.

The implementation stores the last error message on each retry attempt. Lock acquisition failures (due to poisoning or contention) are handled gracefully by continuing without storing the error, which is acceptable since the retry logic itself continues normally and this is just for improving error messages.


289-289: LGTM: Atomic imports for test counter.

The atomic imports are appropriate for tracking retry attempts across closure invocations in the tests. Based on learnings, using Arc<AtomicUsize> is the correct approach for counters in async code.


260-277: Wrapping logic correctly implements the PR objective. All requested Error API elements have been verified to exist:

  • is_no_available_addresses() method exists at packages/rs-sdk/src/error.rs:258
  • NoAvailableAddressesToRetry(Box<Error>) variant exists at packages/rs-sdk/src/error.rs:102
  • Generic(String) variant exists at packages/rs-sdk/src/error.rs:81

The implementation correctly preserves the retry count and address from the original error while wrapping the stored error message.


367-394: LGTM: Test function correctly uses concrete Error type for retry testing.

The test function properly uses Error::StaleNode with the Height variant, providing the required fields (expected_height, received_height, tolerance_blocks). Since StaleNode returns true for can_retry(), it's the appropriate error type for testing the retry logic.

@shumkov shumkov force-pushed the feat/last-meaningful-error branch from 87e95df to 491e20d Compare January 7, 2026 17:27
@shumkov shumkov requested a review from lklimek January 7, 2026 18:31
@shumkov shumkov merged commit de667e0 into v3.0-dev Jan 8, 2026
92 of 94 checks passed
@shumkov shumkov deleted the feat/last-meaningful-error branch January 8, 2026 14:45
@github-project-automation github-project-automation bot moved this from In review / testing to Done in Platform team Jan 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants