-
Notifications
You must be signed in to change notification settings - Fork 54
feat(rs-platform-wallet-ffi): expose devnet name and LLMQ_DEVNET override in spv_start #3758
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::ffi::CStr; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::os::raw::c_char; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use dashcore::sml::llmq_type::LlmqDevnetParams; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use platform_wallet::spv::{ClientConfig, ProgressPercentage, SyncProgress, SyncState}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::error::*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -197,6 +198,17 @@ pub unsafe extern "C" fn platform_wallet_manager_spv_tip_unix_seconds( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Hardcoded to `true` here; if a future caller has a real reason to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// run SPV without masternode sync, it can construct the wallet | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// manager without the asset-lock path instead. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Devnet support: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// - `devnet_name` must be set (non-null) when `network == Devnet` and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// must be null on every other network. When set, the SPV user agent | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// is rewritten to embed the `devnet.devnet-<name>` substring Dash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Core peers gate inbound devnet handshakes on (matches the format | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// the `dash-spv` binary uses). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// - `llmq_devnet_size` / `llmq_devnet_threshold` mirror Dash Core's | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// `-llmqdevnetparams=<size>:<threshold>`. Both zero means "no | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// override". Setting one without the other, or setting them on a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// non-devnet network, is rejected. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[no_mangle] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[allow(clippy::field_reassign_with_default)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub unsafe extern "C" fn platform_wallet_manager_spv_start( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -208,17 +220,63 @@ pub unsafe extern "C" fn platform_wallet_manager_spv_start( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| peer_count: usize, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| restrict_to_configured_peers: bool, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start_from_height: u32, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| devnet_name: *const c_char, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| llmq_devnet_size: u32, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| llmq_devnet_threshold: u32, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> PlatformWalletFFIResult { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| check_ptr!(data_dir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let data_dir_str = unwrap_result_or_return!(CStr::from_ptr(data_dir).to_str()).to_string(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let user_agent_str = if user_agent.is_null() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut user_agent_str = if user_agent.is_null() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some(unwrap_result_or_return!(CStr::from_ptr(user_agent).to_str()).to_string()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let net: crate::types::Network = network.into(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let devnet_name_str = if devnet_name.is_null() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some(unwrap_result_or_return!(CStr::from_ptr(devnet_name).to_str()).to_string()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if net == crate::types::Network::Devnet && devnet_name_str.is_none() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return PlatformWalletFFIResult::err( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PlatformWalletFFIResultCode::ErrorInvalidParameter, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "devnet_name is required when network=Devnet", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if net != crate::types::Network::Devnet && devnet_name_str.is_some() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return PlatformWalletFFIResult::err( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PlatformWalletFFIResultCode::ErrorInvalidParameter, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "devnet_name is only valid on devnet", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (llmq_devnet_size > 0) ^ (llmq_devnet_threshold > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return PlatformWalletFFIResult::err( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PlatformWalletFFIResultCode::ErrorInvalidParameter, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "llmq_devnet_size and llmq_devnet_threshold must both be set or both zero", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if llmq_devnet_size > 0 && net != crate::types::Network::Devnet { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return PlatformWalletFFIResult::err( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PlatformWalletFFIResultCode::ErrorInvalidParameter, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "llmq_devnet_params is only valid on devnet", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+255
to
+266
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 Nitpick: No The XOR check ensures both LLMQ params are zero or both nonzero, but accepts
Suggested change
source: ['claude'] |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Dash Core devnet peers disconnect any inbound connection whose | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // user agent doesn't contain `devnet.devnet-<name>`. Rebuild the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // user agent to embed the substring while keeping whatever base | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the caller (or our default) provided. Mirrors the dash-spv | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // binary's `/rust-dash-spv:VERSION(devnet.devnet-NAME)/` format. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(name) = devnet_name_str.as_deref() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let base = user_agent_str | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .clone() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .unwrap_or_else(|| format!("platform-wallet-ffi:{}", env!("CARGO_PKG_VERSION"))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_agent_str = Some(format!("/{base}(devnet.devnet-{name})/")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+273
to
+278
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 Nitpick: User-agent rebuild double-wraps an already-BIP14-formatted base If the caller supplies a base already in BIP14 form (e.g.
Suggested change
source: ['claude'] |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut peer_list: Vec<String> = Vec::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !peers.is_null() && peer_count > 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for i in 0..peer_count { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -256,6 +314,12 @@ pub unsafe extern "C" fn platform_wallet_manager_spv_start( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| config.peers.push(addr); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if llmq_devnet_size > 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| config.llmq_devnet_params = Some(LlmqDevnetParams { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size: llmq_devnet_size, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| threshold: llmq_devnet_threshold, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let _guard = runtime().enter(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| manager.spv_arc().spawn_in_background(config); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Suggestion: Empty
devnet_namepasses the required-on-devnet checkThe check on line 243 only rejects
NULL. A caller passing an empty (but non-null) C string fordevnet_namewhilenetwork == Devnetsatisfies validation, and the user agent is then rebuilt as/{base}(devnet.devnet-)/. That trailingdevnet.devnet-substring will not match any real devnet's gating prefix, so the SPV client returnsok()but silently fails the handshake — precisely the failure mode this PR exists to prevent. Because the Swift wrapper takesdevnetName: String?(not a validated enum), the FFI is the right place to enforce non-empty.source: ['claude', 'codex']