Skip to content

feat(rs-sdk-ffi): expose SDK protocol version pinning#3734

Draft
thepastaclaw wants to merge 2 commits into
dashpay:v3.1-devfrom
thepastaclaw:feature/ffi-sdk-protocol-version
Draft

feat(rs-sdk-ffi): expose SDK protocol version pinning#3734
thepastaclaw wants to merge 2 commits into
dashpay:v3.1-devfrom
thepastaclaw:feature/ffi-sdk-protocol-version

Conversation

@thepastaclaw
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw commented May 25, 2026

Summary

Adds additive FFI and Swift SDK support for explicitly pinning the Dash
Platform protocol version used by SdkBuilder.

  • Adds dash_sdk_*_with_protocol_version constructors for plain, extended,
    trusted, and callback-backed FFI SDK creation
  • Keeps existing constructors ABI-compatible and mapped to protocol_version = 0
    auto-detect behavior
  • Exposes Swift SDK(network:protocolVersion:), defaulting to 0, so iOS
    callers can pass 11 to pin Platform protocol version v11
  • Documents C, Swift, and Python usage examples

Validation

  • cargo fmt --all -- --check
  • cargo test -p rs-sdk-ffi sdk::tests --lib — 4 passed
  • Commit hook: workspace cargo check
  • code-review dashpay/platform upstream/v3.1-dev feature/ffi-sdk-protocol-version
    — Recommendation: ship
  • Verified generated cbindgen header contains the new protocol-version FFI
    functions

Summary by CodeRabbit

  • New Features

    • SDK initialization now supports optional protocol version pinning (defaults to auto-detect).
    • Added skip_asset_lock_proof_verification configuration option.
  • Documentation

    • Updated code examples across C, Swift, and Python with new SDK initialization patterns and configuration options.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

📝 Walkthrough

Walkthrough

This PR adds protocol-version-aware SDK creation across Rust FFI, Swift, and documentation. New C API constructors accept an explicit protocol version parameter; internal helpers validate and resolve protocol versions or default to auto-detect. Trusted SDK setup now enforces stricter DAPI defaults and error handling. Swift wrapper and docs are updated to demonstrate both pinned and auto-detect usage patterns.

Changes

Protocol Version Pinning Feature

Layer / File(s) Summary
Protocol version resolution and DAPI parsing helpers
packages/rs-sdk-ffi/src/sdk.rs
Import PlatformVersion; add resolve_platform_version (maps 0 to auto-detect, validates others via PlatformVersion::get), parse_dapi_addresses, build_sdk_builder, and apply_platform_version helper functions to centralize protocol-version pinning and DAPI address parsing logic.
Context provider wiring and SDK creation internals
packages/rs-sdk-ffi/src/sdk.rs
Introduce apply_context_provider (handles explicit per-instance provider, core-sdk-handle-driven creation via global callbacks, or fallback), make_sdk_result (unified FFI result construction), and refactor create_sdk_from_config and create_extended_sdk_from_config to use shared builder/config helpers with standardized error conversions.
Trusted SDK builder construction and DAPI handling
packages/rs-sdk-ffi/src/sdk.rs
Update trusted provider construction with regtest-local sidecar URL support; refactor DAPI defaults to apply only for Testnet/Mainnet when null (reject other networks), reject empty DAPI as InvalidParameter; attach provider to builder and spawn async task to prefetch quorum caches.
C API constructors with protocol version support
packages/rs-sdk-ffi/src/sdk.rs
Add four new protocol-version-aware exported constructors (dash_sdk_create_with_protocol_version, dash_sdk_create_extended_with_protocol_version, dash_sdk_create_trusted_with_protocol_version, dash_sdk_create_with_callbacks_and_protocol_version); replace existing constructors with thin ABI wrappers delegating to protocol-version-aware internals.
Protocol version behavior and validation tests
packages/rs-sdk-ffi/src/sdk.rs
Add unit test module asserting protocol version pinning works for SDK creation, invalid protocol versions are rejected for untrusted and trusted constructors, and resolve_platform_version(11) returns pinned platform version.
Swift SDK wrapper protocol version integration
packages/swift-sdk/Sources/SwiftDashSDK/SDK.swift
Extend SDK initializer to accept optional protocolVersion parameter (default 0 for auto-detect); update documentation and both local and non-local FFI calls to use dash_sdk_create_trusted_with_protocol_version passing through the protocol version.
Documentation and integration examples
packages/rs-sdk-ffi/README.md
Update C and Python examples to add skip_asset_lock_proof_verification field and switch to dash_sdk_create_with_protocol_version(..., 11); replace Swift example's DashSDKConfig + dash_sdk_create with SDK initializer showing auto-detect and pinned variants; extend API reference with new constructor entry.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • QuantumExplorer
  • shumkov

🐰 A version pinned, a builder refined,
Protocol paths more carefully designed,
With helpers and tests, the SDK stands tall,
From Rust to Swift, it answers the call!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: exposing SDK protocol version pinning functionality across FFI and Swift, matching the primary objective and the substantial implementation across multiple files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added this to the v3.1.0 milestone May 25, 2026
@thepastaclaw thepastaclaw requested a review from lklimek May 25, 2026 09:12
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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

Inline comments:
In `@packages/rs-sdk-ffi/src/sdk.rs`:
- Around line 341-349: The branch that checks config.core_sdk_handle only tests
for non-null but never uses it, so per-instance handles in DashSDKConfigExtended
are ignored; update the logic in dash_sdk_create_extended_with_protocol_version
to use the provided handle when non-null (e.g. obtain a context provider tied to
core_sdk_handle instead of unconditionally calling
CallbackContextProvider::from_global()), and pass that provider into
builder.with_context_provider; ensure you still fall back to from_global() only
when core_sdk_handle is null or when a handle-specific provider cannot be
created, producing the same error path via internal_error_result when
appropriate.
- Around line 139-163: The trusted setup logic (trusted_builder/trusted_setup)
currently re-parses dapi_addresses and rejects an empty string while
parse_dapi_addresses treats "" as None; change the trusted setup to call
parse_dapi_addresses(dapi_addresses) and branch on the resulting
Option<AddressList> (None = fallback to network defaults, Some(list) = use
provided addresses) instead of re-parsing the raw C string; apply the same
replacement at the other duplicate site that currently re-parses (the second
block that mirrors this logic) so both places treat an empty C string
consistently as “not provided.”
- Around line 165-176: The shared builder functions (e.g., base_builder and the
protocol-version constructors that call builder_with_protocol_version and
ultimately SdkBuilder::new or SdkBuilder::new_mock) only apply
network/dapi_addresses/protocol_version and ignore DashSDKConfig fields
request_retry_count, request_timeout_ms, and skip_asset_lock_proof_verification;
update these builders to propagate those config fields into the SdkBuilder
before returning (e.g., chain calls to the builder to set retry count, request
timeout, and enable/disable asset-lock-proof verification using the SdkBuilder
methods that control transport retry/timeout and verification flags) so the
values from DashSDKConfig are honored when build() is called.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e421833c-e05a-4604-9e8c-e2755527c8a3

📥 Commits

Reviewing files that changed from the base of the PR and between 9f3bfa3 and 29648e9.

📒 Files selected for processing (3)
  • packages/rs-sdk-ffi/README.md
  • packages/rs-sdk-ffi/src/sdk.rs
  • packages/swift-sdk/Sources/SwiftDashSDK/SDK.swift

Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
@thepastaclaw thepastaclaw force-pushed the feature/ffi-sdk-protocol-version branch from 29648e9 to 65f0822 Compare May 25, 2026 09:36
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Addressed the CodeRabbit findings in the PR branch: trusted setup now reuses the shared DAPI address parser (so empty strings fall back to defaults), shared FFI builders propagate retry/timeout/proof settings, and extended SDK creation binds global callbacks to the provided per-instance Core SDK handle when present.\n\nValidation:\n- cargo test -p rs-sdk-ffi --no-default-features --features mocks\n- cargo check -p rs-sdk-ffi

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Addressed CodeRabbit finding 1 on the current PR branch.

  • Updated dash_sdk_create_extended_with_protocol_version / create_extended_internal to use a handle-specific callback context provider when core_sdk_handle is non-null, with fallback to global callbacks only if the handle-specific provider cannot be created.
  • Added CallbackContextProvider::from_core_sdk_handle(...) and a targeted test covering handle override behavior.
  • Verified findings 2 and 3 against the refreshed branch tip (65f0822ac) before changing code. They were already present there: trusted setup already routes through parse_dapi_addresses(...), there is no duplicate stale block, and shared builder config already propagates request settings / proof flags.

Validation run:

  • cargo test -p rs-sdk-ffi from_core_sdk_handle_overrides_global_handle -- --nocapture -> passed
  • cargo test -p rs-sdk-ffi dash_sdk_create_trusted_pins_protocol_version -- --nocapture -> passed
  • cargo test -p rs-sdk-ffi trusted_builder_treats_empty_dapi_addresses_as_defaults -- --nocapture -> passed

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds an additive API across the Rust FFI layer and Swift wrapper to allow callers to explicitly pin the Dash Platform protocol version used by SdkBuilder, while preserving existing constructors’ default auto-detect behavior (protocol_version = 0).

Changes:

  • Added new *_with_protocol_version FFI constructors and refactored SDK creation to share common builder/config logic.
  • Updated Swift SDK initializer to accept an optional protocolVersion parameter (defaulting to 0) and call the new trusted FFI constructor.
  • Updated rs-sdk-ffi README examples for C/Swift/Python to show protocol-version pinning usage.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
packages/swift-sdk/Sources/SwiftDashSDK/SDK.swift Adds protocolVersion parameter (default 0) and forwards it into the trusted FFI constructor.
packages/rs-sdk-ffi/src/sdk.rs Introduces protocol-version-aware creation entrypoints, shared builder helpers, and adds tests around pinning/validation.
packages/rs-sdk-ffi/src/context_callbacks.rs Adds support for overriding the global callback handle with a per-instance Core SDK handle, plus unit tests.
packages/rs-sdk-ffi/README.md Updates language binding examples and API reference to include the new protocol-version pinning constructor.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
Comment on lines +179 to +181
builder
.with_settings(request_settings_from_config(config))
.with_proofs(!config.skip_asset_lock_proof_verification)
Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
Comment on lines +341 to +347
} else if !config.core_sdk_handle.is_null() {
let core_sdk_handle = unsafe { &*config.core_sdk_handle };
if let Some(callback_provider) =
crate::context_callbacks::CallbackContextProvider::from_core_sdk_handle(core_sdk_handle)
.or_else(crate::context_callbacks::CallbackContextProvider::from_global)
{
builder = builder.with_context_provider(callback_provider);
Comment on lines +255 to +260
unsafe { set_global_callbacks(callbacks).expect("set callbacks") };

let provider = CallbackContextProvider::from_core_sdk_handle(&CoreSDKHandle {
client: per_instance_handle,
})
.expect("provider from callbacks");
Comment on lines 80 to 86
DashSDKConfig config = {
.network = DASH_SDK_NETWORK_TESTNET,
.dapi_addresses = "seed-1.testnet.networks.dash.org",
.skip_asset_lock_proof_verification = false,
.request_retry_count = 3,
.request_timeout_ms = 30000
};
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@packages/rs-sdk-ffi/src/context_callbacks.rs`:
- Around line 245-269: This test mutates global callback state via
set_global_callbacks and doesn't restore it; wrap the mutation so
GLOBAL_CALLBACKS is restored after the test (e.g., capture the previous
callbacks before calling set_global_callbacks and restore them in a
drop/teardown or use a scoped helper) to avoid leaking state to other tests;
update the test around ContextProviderCallbacks / set_global_callbacks /
CallbackContextProvider::from_core_sdk_handle to save the prior
GLOBAL_CALLBACKS, set the test callbacks, run the assertions, and then restore
the saved callbacks in all paths (including on panic) so parallel tests remain
deterministic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4053e0f6-0c16-453a-8c68-aae168b65dc7

📥 Commits

Reviewing files that changed from the base of the PR and between 29648e9 and 3e624bc.

📒 Files selected for processing (4)
  • packages/rs-sdk-ffi/README.md
  • packages/rs-sdk-ffi/src/context_callbacks.rs
  • packages/rs-sdk-ffi/src/sdk.rs
  • packages/swift-sdk/Sources/SwiftDashSDK/SDK.swift

Comment on lines +245 to +269
#[test]
fn from_core_sdk_handle_overrides_global_handle() {
let registered_handle = std::ptr::dangling_mut::<c_void>();
let per_instance_handle = 0x1234usize as *mut c_void;
let callbacks = ContextProviderCallbacks {
core_handle: registered_handle,
get_platform_activation_height: get_height_cb,
get_quorum_public_key: get_quorum_pk_cb,
};

unsafe { set_global_callbacks(callbacks).expect("set callbacks") };

let provider = CallbackContextProvider::from_core_sdk_handle(&CoreSDKHandle {
client: per_instance_handle,
})
.expect("provider from callbacks");
let height = provider
.get_platform_activation_height()
.expect("activation height");

assert_eq!(height, 42);
assert_eq!(
LAST_HEIGHT_HANDLE.load(Ordering::Relaxed),
per_instance_handle as usize
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Isolate this test from global callback state.

set_global_callbacks(...) mutates GLOBAL_CALLBACKS for the whole process, and this test never restores it. Rust tests run in parallel by default, so this can leak state into other tests or race with any other test that touches callback registration.

🧪 One way to make the test deterministic
 #[cfg(test)]
 mod tests {
     use super::*;
     use drive_proof_verifier::ContextProvider;
-    use std::sync::atomic::{AtomicUsize, Ordering};
+    use std::sync::{
+        atomic::{AtomicUsize, Ordering},
+        Mutex, OnceLock,
+    };
 
     static LAST_HEIGHT_HANDLE: AtomicUsize = AtomicUsize::new(0);
+    static CALLBACK_TEST_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
@@
     #[test]
     fn from_core_sdk_handle_overrides_global_handle() {
+        let _guard = CALLBACK_TEST_LOCK
+            .get_or_init(|| Mutex::new(()))
+            .lock()
+            .expect("lock callback tests");
+        let previous = get_global_callbacks();
+
         let registered_handle = std::ptr::dangling_mut::<c_void>();
         let per_instance_handle = 0x1234usize as *mut c_void;
         let callbacks = ContextProviderCallbacks {
             core_handle: registered_handle,
             get_platform_activation_height: get_height_cb,
@@
         assert_eq!(
             LAST_HEIGHT_HANDLE.load(Ordering::Relaxed),
             per_instance_handle as usize
         );
+
+        if let Some(previous) = previous {
+            unsafe { set_global_callbacks(previous).expect("restore callbacks") };
+        } else if let Some(storage) = GLOBAL_CALLBACKS.get() {
+            *storage.write().expect("clear callbacks") = None;
+        }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/rs-sdk-ffi/src/context_callbacks.rs` around lines 245 - 269, This
test mutates global callback state via set_global_callbacks and doesn't restore
it; wrap the mutation so GLOBAL_CALLBACKS is restored after the test (e.g.,
capture the previous callbacks before calling set_global_callbacks and restore
them in a drop/teardown or use a scoped helper) to avoid leaking state to other
tests; update the test around ContextProviderCallbacks / set_global_callbacks /
CallbackContextProvider::from_core_sdk_handle to save the prior
GLOBAL_CALLBACKS, set the test callbacks, run the assertions, and then restore
the saved callbacks in all paths (including on panic) so parallel tests remain
deterministic.

@thepastaclaw thepastaclaw force-pushed the feature/ffi-sdk-protocol-version branch 3 times, most recently from feca7c0 to 22365d9 Compare May 25, 2026 10:07
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Addressed the latest CodeRabbit/Copilot review items in feca7c0:

  • isolated/restored global callback state in the context callback test
  • stopped falling back to global callbacks when a non-null Core SDK handle cannot produce a provider; now returns InvalidParameter
  • clarified the legacy skip_asset_lock_proof_verification flag as toggling SDK request proofs in code and README examples
  • updated the context provider integration test to use a valid handle with a null client instead of a dangling pointer

Validation:

  • cargo test -p rs-sdk-ffi --no-default-features --features mocks
  • cargo check -p rs-sdk-ffi

@thepastaclaw thepastaclaw force-pushed the feature/ffi-sdk-protocol-version branch 2 times, most recently from feca7c0 to 22365d9 Compare May 25, 2026 10:33
@lklimek lklimek requested a review from Copilot May 25, 2026 13:50
Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
/// - The returned handle inside `DashSDKResult` must be destroyed using the SDK destroy function to avoid leaks.
#[no_mangle]
pub unsafe extern "C" fn dash_sdk_create_extended_with_protocol_version(
config: *const DashSDKConfigExtended,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not just add protocol_version to DashSDKConfigExtended ? New constructors won't be needed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
Comment on lines +873 to +877
debug!("dash_sdk_create_trusted: testing basic HTTP connectivity");
match reqwest::get("https://www.google.com").await {
Ok(_) => debug!("dash_sdk_create_trusted: basic HTTP test successful (Google)"),
Err(e) => warn!(error = %e, "dash_sdk_create_trusted: basic HTTP test failed"),
}
Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
Comment on lines +879 to +886
// Try the quorums endpoint directly
debug!("dash_sdk_create_trusted: testing quorums endpoint directly");
match reqwest::get("https://quorums.testnet.networks.dash.org/quorums").await {
Ok(resp) => debug!(status = %resp.status(), "dash_sdk_create_trusted: direct quorums endpoint test successful"),
Err(e) => warn!(error = %e, "dash_sdk_create_trusted: direct quorums endpoint test failed"),
}

// Now try through the provider
Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
Comment on lines +708 to +709
/// - `config` must be a valid pointer to a DashSDKConfig structure
/// # Safety
Comment thread packages/rs-sdk-ffi/src/sdk.rs Outdated
Comment on lines +188 to +216
/// Create a new SDK instance with an explicit protocol version override
///
/// `protocol_version == 0` preserves the default auto-detect behavior.
///
/// # Safety
/// - `config` must be a valid pointer to a DashSDKConfig structure for the duration of the call.
/// - The returned handle inside `DashSDKResult` must be destroyed using the SDK destroy function to avoid leaks.
#[no_mangle]
pub unsafe extern "C" fn dash_sdk_create_with_protocol_version(
config: *const DashSDKConfig,
protocol_version: u32,
) -> DashSDKResult {
if config.is_null() {
return DashSDKResult::error(DashSDKError::new(
DashSDKErrorCode::InvalidParameter,
"Config is null".to_string(),
));
}

let config = &*config;

// Parse configuration
let network: Network = config.network.into();

let platform_version = match resolve_platform_version(protocol_version) {
Ok(platform_version) => platform_version,
Err(result) => return result,
};

Comment on lines +145 to +147
# Create SDK instance pinned to protocol version 11.
# Pass 0 to keep the default auto-detect behavior.
result = lib.dash_sdk_create_with_protocol_version(byref(config), 11)
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Addressed the valid review points on this branch:

  • Factored the SDK creation/config parsing paths in packages/rs-sdk-ffi/src/sdk.rs so the protocol-version entry points reuse shared helpers instead of duplicating builder setup.
  • Removed the extra trusted-prefetch reqwest::get() probes to google.com and the hardcoded testnet quorums URL; trusted setup now only warms caches through the configured TrustedHttpContextProvider.
  • Removed the duplicate rustdoc # Safety headings.
  • Updated the README C/Python config examples to include the current DashSDKConfig fields.

I did not move protocol_version onto DashSDKConfigExtended. Adding a field to that exported #[repr(C)] struct would change its ABI/layout for existing dash_sdk_create_extended callers and can cause older compiled clients to pass a shorter struct than Rust now expects. I kept the current exported functions and reduced the duplication internally instead.

Validation run:

  • cargo fmt --all
  • cargo test -p rs-sdk-ffi
  • cargo clippy -p rs-sdk-ffi --all-targets -- -D warnings

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

@thepastaclaw Thanks for the thorough update! The changes look well-reasoned — in particular, keeping protocol_version out of DashSDKConfigExtended to preserve the #[repr(C)] ABI is the right call for existing callers. Triggering the incremental review now.


✏️ Learnings added
Learnt from: thepastaclaw
URL: https://github.com/dashpay/platform/pull/3734

Timestamp: 2026-05-25T14:23:56.241Z
Learning: In `packages/rs-sdk-ffi/src/sdk.rs`, `protocol_version` was intentionally NOT added as a field to `DashSDKConfigExtended` (a `#[repr(C)]` struct exported via FFI) because adding a field would change its ABI/memory layout and cause older compiled C/Swift/Python clients that pass a shorter struct to mismatch what Rust expects. Instead, `protocol_version` is passed as a separate parameter to new `_with_protocol_version` FFI entry points, keeping existing `dash_sdk_create_extended` callers ABI-compatible.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
packages/rs-sdk-ffi/src/sdk.rs (2)

137-145: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Thread the rest of DashSDKConfig through both builder helpers.

These paths still only apply network/DAPI/protocol version. skip_asset_lock_proof_verification, request_retry_count, and request_timeout_ms are never consumed before build(), so callers can set them and still get the default SDK behavior.

#!/bin/bash
set -euo pipefail

echo "== DashSDKConfig field usage in sdk.rs and types.rs =="
rg -n -C1 'skip_asset_lock_proof_verification|request_retry_count|request_timeout_ms' \
  packages/rs-sdk-ffi/src/sdk.rs \
  packages/rs-sdk-ffi/src/types.rs

echo
echo "== Candidate SdkBuilder setters/call sites in Rust sources =="
rg -n -C2 'with_.*(retry|timeout)|asset.*lock.*proof|proof.*verification|skip_asset_lock' . --glob '*.rs'

Also applies to: 303-357

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

In `@packages/rs-sdk-ffi/src/sdk.rs` around lines 137 - 145, The builder helpers
(both SdkBuilder::new(address_list) and SdkBuilder::new_mock()) fail to apply
DashSDKConfig fields skip_asset_lock_proof_verification, request_retry_count,
and request_timeout_ms; update build_sdk_builder (and the other helper path
around lines 303-357) to call the corresponding SdkBuilder setters after
creating the builder and before returning: read
config.skip_asset_lock_proof_verification and call the builder method that
disables/enables proof verification (e.g.,
with_skip_asset_lock_proof_verification or equivalent), set request retry count
via the builder method for retries (e.g., with_request_retry_count), and convert
config.request_timeout_ms into the builder timeout setter (e.g.,
with_request_timeout_ms or with_request_timeout using Duration::from_millis) so
both branches (SdkBuilder::new and SdkBuilder::new_mock) consistently consume
these config values.

166-175: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use core_sdk_handle instead of falling back to globals here.

Once this branch sees a non-null handle, it still calls CallbackContextProvider::from_global() and never reads the handle value. That makes the per-instance handle in DashSDKConfigExtended a no-op and can bind the SDK to the wrong Core SDK instance or fail with the global-registration error even when a valid handle was passed.

#!/bin/bash
set -euo pipefail

echo "== core_sdk_handle branch in packages/rs-sdk-ffi/src/sdk.rs =="
rg -n -C2 'core_sdk_handle|from_global\(' packages/rs-sdk-ffi/src/sdk.rs

echo
echo "== handle-specific provider helpers in packages/rs-sdk-ffi/src/context_callbacks.rs =="
rg -n -C2 'from_core_sdk_handle|from_global' packages/rs-sdk-ffi/src/context_callbacks.rs
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/rs-sdk-ffi/src/sdk.rs` around lines 166 - 175, The branch that
checks config.core_sdk_handle.is_null() incorrectly always calls
CallbackContextProvider::from_global(), ignoring the per-instance handle; change
it to construct the provider from the passed handle when config.core_sdk_handle
is non-null (e.g. call
crate::context_callbacks::CallbackContextProvider::from_core_sdk_handle(core_sdk_handle)
or equivalent) and pass that provider into
builder.with_context_provider(callback_provider); if from_core_sdk_handle
returns None or an error, return the same DashSDKResult::error as the current
global-failure path. Ensure you reference config.core_sdk_handle (or the local
core_sdk_handle variable) and use CallbackContextProvider::from_core_sdk_handle
instead of CallbackContextProvider::from_global().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/rs-sdk-ffi/src/sdk.rs`:
- Around line 137-145: The builder helpers (both SdkBuilder::new(address_list)
and SdkBuilder::new_mock()) fail to apply DashSDKConfig fields
skip_asset_lock_proof_verification, request_retry_count, and request_timeout_ms;
update build_sdk_builder (and the other helper path around lines 303-357) to
call the corresponding SdkBuilder setters after creating the builder and before
returning: read config.skip_asset_lock_proof_verification and call the builder
method that disables/enables proof verification (e.g.,
with_skip_asset_lock_proof_verification or equivalent), set request retry count
via the builder method for retries (e.g., with_request_retry_count), and convert
config.request_timeout_ms into the builder timeout setter (e.g.,
with_request_timeout_ms or with_request_timeout using Duration::from_millis) so
both branches (SdkBuilder::new and SdkBuilder::new_mock) consistently consume
these config values.
- Around line 166-175: The branch that checks config.core_sdk_handle.is_null()
incorrectly always calls CallbackContextProvider::from_global(), ignoring the
per-instance handle; change it to construct the provider from the passed handle
when config.core_sdk_handle is non-null (e.g. call
crate::context_callbacks::CallbackContextProvider::from_core_sdk_handle(core_sdk_handle)
or equivalent) and pass that provider into
builder.with_context_provider(callback_provider); if from_core_sdk_handle
returns None or an error, return the same DashSDKResult::error as the current
global-failure path. Ensure you reference config.core_sdk_handle (or the local
core_sdk_handle variable) and use CallbackContextProvider::from_core_sdk_handle
instead of CallbackContextProvider::from_global().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 88e7bbd9-96a3-4710-aadd-8a459c530616

📥 Commits

Reviewing files that changed from the base of the PR and between 3e624bc and ef353c1.

📒 Files selected for processing (3)
  • packages/rs-sdk-ffi/README.md
  • packages/rs-sdk-ffi/src/sdk.rs
  • packages/swift-sdk/Sources/SwiftDashSDK/SDK.swift

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants