Skip to content

feat(key-wallet-ffi): expose asset lock transaction builder#600

Merged
QuantumExplorer merged 5 commits intov0.42-devfrom
feat/ffi-asset-lock-builder
Mar 30, 2026
Merged

feat(key-wallet-ffi): expose asset lock transaction builder#600
QuantumExplorer merged 5 commits intov0.42-devfrom
feat/ffi-asset-lock-builder

Conversation

@QuantumExplorer
Copy link
Copy Markdown
Member

@QuantumExplorer QuantumExplorer commented Mar 30, 2026

Summary

Adds wallet_build_and_sign_asset_lock_transaction FFI function for building Core special transactions (type 8) with AssetLockPayload. This enables iOS/mobile clients to perform Core → Platform credit transfers.

Closes #597

What it does

The new function:

  1. Selects UTXOs from the specified BIP44 account using coin selection
  2. Derives a one-time private key from the AssetLockAddressTopUp account
  3. Builds credit outputs from provided scripts and amounts
  4. Calls TransactionBuilder::build_asset_lock(credit_outputs) to create the special transaction
  5. Signs the transaction using the wallet's private keys
  6. Returns serialized tx bytes, fee, output index, and the one-time private key

FFI signature

bool wallet_build_and_sign_asset_lock_transaction(
    const FFIWalletManager *manager,
    const FFIWallet *wallet,
    uint32_t account_index,
    const uint8_t *const *credit_output_scripts,
    const size_t *credit_output_script_lens,
    const uint64_t *credit_output_amounts,
    size_t credit_outputs_count,
    uint64_t fee_per_kb,
    uint64_t *fee_out,
    uint8_t **tx_bytes_out,
    size_t *tx_len_out,
    uint32_t *output_index_out,
    uint8_t (*private_key_out)[32],
    FFIError *error
);

iOS usage flow

// 1. Build asset lock transaction (this PR)
let (txBytes, outputIndex, privateKey, fee) = try walletManager.buildAssetLockTransaction(...)

// 2. Broadcast on Core network (existing)
try spvClient.broadcastTransaction(txBytes)

// 3. Wait for InstantSend lock (existing SPV events)
let isLockBytes = await waitForInstantSendLock(txid: txid)

// 4. Submit to Platform (existing rs-sdk-ffi)
try await sdk.topUpFromAssetLock(proofType: .instant, ...)

Test plan

  • cargo check -p key-wallet-ffi compiles with no errors or warnings
  • C header auto-generated by cbindgen
  • CI passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added support for constructing and signing asset-lock transactions for Core-to-Platform transfers.
    • Supports multiple credit outputs per transaction and automatic fee selection based on outputs.
    • Automatically selects funding UTXOs, derives a one-time authorization key (and marks it used), and returns the serialized signed transaction along with fee, output index, and the one-time key.

Adds `wallet_build_and_sign_asset_lock_transaction` FFI function that
builds a Core special transaction (type 8) with AssetLockPayload for
Core → Platform credit transfers.

The function:
- Selects UTXOs from the specified BIP44 account
- Derives a one-time private key from the asset lock top-up account
- Builds credit outputs from the provided scripts and amounts
- Signs the transaction using the wallet's private keys
- Returns serialized tx bytes, output index, and the one-time key

This is the missing piece for end-to-end iOS asset lock flow:
build tx → broadcast → wait for IS lock → submit to Platform.

Closes #597

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Warning

Rate limit exceeded

@QuantumExplorer has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 25 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 5 minutes and 25 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bb3f27d4-bcf1-456d-ab0c-b8183b810798

📥 Commits

Reviewing files that changed from the base of the PR and between b2742fd and 3353be9.

📒 Files selected for processing (2)
  • key-wallet-ffi/FFI_API.md
  • key-wallet-ffi/src/transaction.rs
📝 Walkthrough

Walkthrough

Adds a new exported FFI function wallet_build_and_sign_asset_lock_transaction that builds and signs Core asset-lock (type 8) transactions from provided credit outputs, selects funding UTXOs, derives a one‑time asset-lock private key, and returns serialized tx bytes, output index, fee, and the one‑time key.

Changes

Cohort / File(s) Summary
Asset Lock Transaction FFI
key-wallet-ffi/src/transaction.rs
Added wallet_build_and_sign_asset_lock_transaction(...). Validates pointers and counts, converts parallel arrays into Vec<TxOut>, selects funding UTXOs (BranchAndBound), derives and marks a one-time private key from AssetLockAddressTopUp, builds and signs asset-lock transaction via TransactionBuilder::build_asset_lock, computes fee (handles extra-output fee path), writes output_index_out, copies 32‑byte private_key_out, serializes tx into heap buffer, and returns tx_bytes_out/tx_len_out. (+315/-0)
FFI docs update
key-wallet-ffi/FFI_API.md
Documented new exported function wallet_build_and_sign_asset_lock_transaction in the transaction module; updated totals (functions 259→260, wallet ops 63→64) and added C signature, safety, and ownership notes. (+19/-2)

Sequence Diagram(s)

sequenceDiagram
    participant Client as Mobile App
    participant FFI as wallet_build_and_sign_asset_lock_transaction
    participant Manager as FFIWalletManager/FFIWallet
    participant Builder as TransactionBuilder
    participant UTXO as Wallet UTXO Set
    participant KeyAcc as AssetLockAddressTopUp

    Client->>FFI: call with credit_output_scripts, amounts, fee_per_kb, etc.
    FFI->>Manager: validate handles & pointers
    FFI->>Builder: construct credit outputs Vec<TxOut>
    Builder->>UTXO: select funding UTXOs (BranchAndBound)
    Builder->>KeyAcc: derive one-time private key & mark address used
    Builder->>Builder: build_asset_lock(credit_outputs) and sign
    Builder->>FFI: return tx bytes, tx len, output index, fee, private key
    FFI->>Client: return success (true) + outputs or false + error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 I hopped through scripts and hid a key,
Built a lock where Core coins flee,
Signed and packed with careful art,
A one‑time secret close to heart,
Mobile wallets dance with glee ✨

🚥 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 'feat(key-wallet-ffi): expose asset lock transaction builder' clearly and concisely describes the main change: exposing an FFI function for asset lock transaction building.
Linked Issues check ✅ Passed The PR implements all key requirements from issue #597: exposes the FFI function with proper UTXO selection, one-time key derivation, credit output handling, transaction building/signing, and returns all required outputs (tx bytes, fee, output index, private key).
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the asset lock transaction builder FFI function and documenting it; no extraneous modifications were introduced.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ffi-asset-lock-builder

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


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.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 30, 2026

Codecov Report

❌ Patch coverage is 0% with 232 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.14%. Comparing base (5004b52) to head (3353be9).
⚠️ Report is 2 commits behind head on v0.42-dev.

Files with missing lines Patch % Lines
key-wallet-ffi/src/transaction.rs 0.00% 232 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           v0.42-dev     #600      +/-   ##
=============================================
- Coverage      67.24%   67.14%   -0.11%     
=============================================
  Files            318      318              
  Lines          67018    67250     +232     
=============================================
+ Hits           45066    45153      +87     
- Misses         21952    22097     +145     
Flag Coverage Δ
core 75.21% <ø> (ø)
ffi 36.09% <0.00%> (+0.07%) ⬆️
rpc 19.92% <ø> (ø)
spv 83.81% <ø> (-0.03%) ⬇️
wallet 66.64% <ø> (ø)
Files with missing lines Coverage Δ
key-wallet-ffi/src/transaction.rs 0.00% <0.00%> (ø)

... and 17 files with indirect coverage changes

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

🧹 Nitpick comments (1)
key-wallet-ffi/src/transaction.rs (1)

995-1280: Please add #[cfg(test)] coverage for this new FFI path.

A small test matrix here would quickly catch underfunded selection, key reuse when the next index is unavailable, and output_index_out/credit-output mapping regressions.

As per coding guidelines, "Write unit tests for new functionality in Rust code" and "Place unit tests alongside code with #[cfg(test)] attribute`.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-ffi/src/transaction.rs` around lines 995 - 1280, Add a
#[cfg(test)] mod with unit tests for the FFI path implemented by
wallet_build_and_sign_asset_lock_transaction: write tests that (1) exercise an
underfunded UTXO set to assert coin selection fails and the FFIError is set, (2)
simulate the asset_lock_address_topup account having no next address to verify
key reuse/handling of get_next_address_index(), and (3) build a successful case
verifying fee_out, tx_len_out/tx_bytes_out, output_index_out and that
credit_outputs map to the expected transaction outputs and the one-time private
key written to private_key_out; use the FFI types (FFIWalletManager, FFIWallet),
populate credit_output_scripts/credit_output_script_lens/credit_output_amounts
and credit_outputs_count, and assert expected success/failure and side-effects
for each scenario.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@key-wallet-ffi/src/transaction.rs`:
- Around line 1172-1196: The derived one-time address index
(asset_lock_account.get_next_address_index()) is only read and never consumed,
so mark the index as used after deriving the key (e.g., call the account's "mark
as used" or "consume/increment" method on asset_lock_account with key_index—if
the API exposes something like mark_address_as_used(key_index) or
increment_next_address_index(), invoke that immediately after deriving
asset_lock_path) so repeated calls return new addresses; also stop hardcoding
output_index_out = 0: when credit_outputs_count > 1 either accept an explicit
output_index parameter or generate/return a distinct one-time key per output
(iterate credit_outputs_count, derive separate addresses/keys and mark each
index used, or compute output_index_out from the mapping between derived
addresses and the requested output) so each credit output is unambiguously tied
to its own one-time key.

---

Nitpick comments:
In `@key-wallet-ffi/src/transaction.rs`:
- Around line 995-1280: Add a #[cfg(test)] mod with unit tests for the FFI path
implemented by wallet_build_and_sign_asset_lock_transaction: write tests that
(1) exercise an underfunded UTXO set to assert coin selection fails and the
FFIError is set, (2) simulate the asset_lock_address_topup account having no
next address to verify key reuse/handling of get_next_address_index(), and (3)
build a successful case verifying fee_out, tx_len_out/tx_bytes_out,
output_index_out and that credit_outputs map to the expected transaction outputs
and the one-time private key written to private_key_out; use the FFI types
(FFIWalletManager, FFIWallet), populate
credit_output_scripts/credit_output_script_lens/credit_output_amounts and
credit_outputs_count, and assert expected success/failure and side-effects for
each scenario.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ec4b5a82-644c-4f87-b2ca-955754dad68f

📥 Commits

Reviewing files that changed from the base of the PR and between 5004b52 and 357257d.

📒 Files selected for processing (1)
  • key-wallet-ffi/src/transaction.rs

QuantumExplorer and others added 2 commits March 30, 2026 19:39
Addresses CodeRabbit review feedback:

1. Mark the derived asset lock address as used after key derivation
   so repeated calls derive fresh keys instead of reusing the same one.

2. Document that output_index_out = 0 refers to the first credit
   output in the payload. For multi-output scenarios, the caller
   should invoke the function once per credit output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add wallet_build_and_sign_asset_lock_transaction to FFI_API.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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 the current code and only fix it if needed.

Inline comments:
In `@key-wallet-ffi/FFI_API.md`:
- Around line 1287-1289: The documented C signature for
wallet_build_and_sign_asset_lock_transaction uses Rust-specific syntax for
private_key_out; update the signature to use a C-compatible type (e.g., a
pointer to 32 bytes) so FFI consumers can parse it correctly. Locate the
documented function header for wallet_build_and_sign_asset_lock_transaction and
replace the Rust-style private_key_out: *mut [u8; 32] with a C-compatible
declaration such as uint8_t (*)[32] or uint8_t * (with an explanatory note that
it points to 32 bytes) and ensure the rest of the pointer types remain
unchanged.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e94a27ac-1e0a-4ce8-aa65-64ef8b51aa17

📥 Commits

Reviewing files that changed from the base of the PR and between 986cda2 and b2742fd.

📒 Files selected for processing (1)
  • key-wallet-ffi/FFI_API.md

Comment on lines +1287 to +1289
```c
wallet_build_and_sign_asset_lock_transaction(manager: *const FFIWalletManager, wallet: *const FFIWallet, account_index: u32, credit_output_scripts: *const *const u8, credit_output_script_lens: *const usize, credit_output_amounts: *const u64, credit_outputs_count: usize, fee_per_kb: u64, fee_out: *mut u64, tx_bytes_out: *mut *mut u8, tx_len_out: *mut usize, output_index_out: *mut u32, private_key_out: *mut [u8; 32], error: *mut FFIError,) -> bool
```
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify doc signature vs generated C header for wallet_build_and_sign_asset_lock_transaction
rg -n -C2 'wallet_build_and_sign_asset_lock_transaction' key-wallet-ffi/FFI_API.md
fd -e h -i | xargs rg -n -C3 'wallet_build_and_sign_asset_lock_transaction'

Repository: dashpay/rust-dashcore

Length of output: 3883


🏁 Script executed:

# View the full function signature in the Rust implementation
sed -n '996,1020p' key-wallet-ffi/src/transaction.rs

Repository: dashpay/rust-dashcore

Length of output: 918


🏁 Script executed:

# Search for any .h header files that might contain the C signature
fd -e h | head -20

Repository: dashpay/rust-dashcore

Length of output: 47


Use C-compatible type notation in the documented C signature.

The signature is marked as a c code block but uses private_key_out: *mut [u8; 32], which is Rust-specific syntax. In C, a mutable pointer to a 32-byte array should be documented as uint8_t (*)[32] or uint8_t *. Update the documentation to reflect proper C type notation for FFI consumers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-ffi/FFI_API.md` around lines 1287 - 1289, The documented C
signature for wallet_build_and_sign_asset_lock_transaction uses Rust-specific
syntax for private_key_out; update the signature to use a C-compatible type
(e.g., a pointer to 32 bytes) so FFI consumers can parse it correctly. Locate
the documented function header for wallet_build_and_sign_asset_lock_transaction
and replace the Rust-style private_key_out: *mut [u8; 32] with a C-compatible
declaration such as uint8_t (*)[32] or uint8_t * (with an explanatory note that
it points to 32 bytes) and ensure the rest of the pointer types remain
unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ZocoLini
ZocoLini previously approved these changes Mar 30, 2026
The asset lock transaction can be funded from different account types:
- IdentityRegistration (m/9'/5'/5'/0'/index')
- IdentityTopUp (m/9'/5'/5'/1'/reg_index'/index')
- IdentityTopUpNotBound (m/9'/5'/5'/1'/index')
- IdentityInvitation (m/9'/5'/5'/3'/index')
- AssetLockAddressTopUp (m/9'/5'/5'/4'/index')
- AssetLockShieldedAddressTopUp (m/9'/5'/5'/5'/index')

The caller now specifies which funding type to use via the
`funding_type` parameter. For IdentityTopUp, the `identity_index`
parameter identifies which identity is being topped up.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@QuantumExplorer QuantumExplorer merged commit 6ef8b9a into v0.42-dev Mar 30, 2026
39 checks passed
@QuantumExplorer QuantumExplorer deleted the feat/ffi-asset-lock-builder branch March 30, 2026 17:15
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.

feat(key-wallet-ffi): expose asset lock transaction builder for iOS/mobile

2 participants