Skip to content

refactor(sdk-ffi): migrate pre-existing block_on sites to dash_async::block_on #3535

@Claudius-Maginificent

Description

@Claudius-Maginificent

Context

PR #3432 / #3497 introduced the workspace-canonical async-sync bridge dash_async::block_on (crate packages/rs-dash-async), which is runtime-aware and safely handles the no-runtime, current-thread, and multi-thread tokio flavors — avoiding the deadlocks that motivated that fix.

PR #3492 (async Signer trait) migrated exactly one call site — packages/rs-sdk-ffi/src/signer_simple.rs::dash_sdk_signer_sign — to use dash_async::block_on. However, the workspace still contains roughly 30 pre-existing sync-to-async bridging sites that spin up an ad-hoc tokio runtime inline (tokio::runtime::Builder::new_current_thread() or Runtime::new() followed by .block_on(...), or wrapper.runtime.block_on(async { ... })).

Affected sites

A non-exhaustive grep turned up these modules using the legacy pattern:

packages/rs-dash-platform-macros/src/lib.rs (CMT-002, marked with TODO(CMT-002, issue #3535)): the #[stack_size] proc-macro expands async functions into an inline Builder::new_current_thread().build().block_on(async move #block) on a spawned thread. Used by many test bodies; same deadlock class PR #3490 / #3497 addressed.

packages/rs-sdk-ffi FFI entry points:

  • packages/rs-sdk-ffi/src/sdk.rs
  • packages/rs-sdk-ffi/src/dashpay/contact_request.rs
  • packages/rs-sdk-ffi/src/address_sync/mod.rs
  • packages/rs-sdk-ffi/src/shielded/transitions/*.rs (shield, unshield, shielded_transfer, shielded_withdrawal, shield_from_asset_lock, broadcast, builders)
  • packages/rs-sdk-ffi/src/shielded/queries/*.rs (anchors, nullifiers, pool_state, most_recent_anchor, encrypted_notes)
  • packages/rs-sdk-ffi/src/shielded/pool_client/sync.rs
  • packages/rs-sdk-ffi/src/data_contract/queries/fetch_json.rs

Motivation

Consistency. Any FFI entry point or proc-macro-expanded test that is re-entered while another tokio runtime is already active on the calling thread currently risks a block_in_place-style panic or deadlock (the exact class of bug that dash-async was built to fix). Migrating all sites puts every bridging call through the same vetted code path.

Scope

  • Replace tokio::runtime::Builder::new_current_thread()...build().block_on(...) and Runtime::new().block_on(...) patterns with dash_async::block_on(...).
  • For sites using wrapper.runtime.block_on(...) (where wrapper.runtime is a long-lived runtime handle), evaluate per call whether the long-lived runtime is still needed or whether dash_async::block_on suffices. If the runtime is only used for this single block_on, prefer dash_async.
  • Adjust error handling to distinguish bridging failures (DashSDKErrorCode::InternalError) from domain failures.
  • Where the future borrows locals, own them up front (Vec::from / clone) to satisfy Send + 'static.
  • For rs-dash-platform-macros: add a dash-async dep and switch the generated block_on(async move #block) to dash_async::block_on(async move #block).expect(...).

Non-goals

  • No behavioral change for callers.
  • No changes to public FFI signatures.

Out of scope for this issue

  • wasm-sdk code — dash_async::block_on is a WASM stub that returns an error.

🤖 Co-authored by Claudius the Magnificent AI Agent

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions