From 759bf25efa75691d425a94c4af0dbd1c2e46d8b5 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 2 Sep 2025 04:43:48 +0700 Subject: [PATCH 1/3] feat: add functions to update client configuration and enable masternode sync --- dash-spv-ffi/FFI_API.md | 38 +++++++++++++++++++++-- dash-spv-ffi/include/dash_spv_ffi.h | 48 +++++++++++++++++++++++++++++ dash-spv-ffi/src/client.rs | 37 ++++++++++++++++++++++ dash-spv-ffi/src/config.rs | 17 ++++++++++ dash-spv/src/client/mod.rs | 32 +++++++++++++++++++ 5 files changed, 170 insertions(+), 2 deletions(-) diff --git a/dash-spv-ffi/FFI_API.md b/dash-spv-ffi/FFI_API.md index 0e4b18fa5..97dd84617 100644 --- a/dash-spv-ffi/FFI_API.md +++ b/dash-spv-ffi/FFI_API.md @@ -4,7 +4,7 @@ This document provides a comprehensive reference for all FFI (Foreign Function I **Auto-generated**: This documentation is automatically generated from the source code. Do not edit manually. -**Total Functions**: 61 +**Total Functions**: 63 ## Table of Contents @@ -34,10 +34,11 @@ Functions: 4 ### Configuration -Functions: 23 +Functions: 25 | Function | Description | Module | |----------|-------------|--------| +| `dash_spv_ffi_client_update_config` | Update the running client's configuration | client | | `dash_spv_ffi_config_add_peer` | Adds a peer address to the configuration # Safety - `config` must be a valid... | config | | `dash_spv_ffi_config_destroy` | Destroys an FFIClientConfig and frees its memory # Safety - `config` must be... | config | | `dash_spv_ffi_config_get_data_dir` | Gets the data directory path from the configuration # Safety - `config` must... | config | @@ -49,6 +50,7 @@ Functions: 23 | `dash_spv_ffi_config_set_data_dir` | Sets the data directory for storing blockchain data # Safety - `config` must... | config | | `dash_spv_ffi_config_set_fetch_mempool_transactions` | Sets whether to fetch full mempool transaction data # Safety - `config` must... | config | | `dash_spv_ffi_config_set_filter_load` | Sets whether to load bloom filters # Safety - `config` must be a valid point... | config | +| `dash_spv_ffi_config_set_masternode_sync_enabled` | Enables or disables masternode synchronization # Safety - `config` must be a... | config | | `dash_spv_ffi_config_set_max_mempool_transactions` | Sets the maximum number of mempool transactions to track # Safety - `config`... | config | | `dash_spv_ffi_config_set_max_peers` | Sets the maximum number of peers to connect to # Safety - `config` must be a... | config | | `dash_spv_ffi_config_set_mempool_strategy` | Sets the mempool synchronization strategy # Safety - `config` must be a vali... | config | @@ -198,6 +200,22 @@ dash_spv_ffi_client_stop(client: *mut FFIDashSpvClient) -> i32 ### Configuration - Detailed +#### `dash_spv_ffi_client_update_config` + +```c +dash_spv_ffi_client_update_config(client: *mut FFIDashSpvClient, config: *const FFIClientConfig,) -> i32 +``` + +**Description:** +Update the running client's configuration. # Safety - `client` must be a valid pointer to an `FFIDashSpvClient`. - `config` must be a valid pointer to an `FFIClientConfig`. - The network in `config` must match the client's network; changing networks at runtime is not supported. + +**Safety:** +- `client` must be a valid pointer to an `FFIDashSpvClient`. - `config` must be a valid pointer to an `FFIClientConfig`. - The network in `config` must match the client's network; changing networks at runtime is not supported. + +**Module:** `client` + +--- + #### `dash_spv_ffi_config_add_peer` ```c @@ -362,6 +380,22 @@ Sets whether to load bloom filters # Safety - `config` must be a valid pointer --- +#### `dash_spv_ffi_config_set_masternode_sync_enabled` + +```c +dash_spv_ffi_config_set_masternode_sync_enabled(config: *mut FFIClientConfig, enable: bool,) -> i32 +``` + +**Description:** +Enables or disables masternode synchronization # Safety - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call + +**Safety:** +- `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet - The caller must ensure the config pointer remains valid for the duration of this call + +**Module:** `config` + +--- + #### `dash_spv_ffi_config_set_max_mempool_transactions` ```c diff --git a/dash-spv-ffi/include/dash_spv_ffi.h b/dash-spv-ffi/include/dash_spv_ffi.h index 8ed308121..33c06a1a4 100644 --- a/dash-spv-ffi/include/dash_spv_ffi.h +++ b/dash-spv-ffi/include/dash_spv_ffi.h @@ -198,6 +198,17 @@ typedef struct FFIUnconfirmedTransaction { struct FFIDashSpvClient *dash_spv_ffi_client_new(const FFIClientConfig *config); +/** + * Update the running client's configuration. + * + * # Safety + * - `client` must be a valid pointer to an `FFIDashSpvClient`. + * - `config` must be a valid pointer to an `FFIClientConfig`. + * - The network in `config` must match the client's network; changing networks at runtime is not supported. + */ +int32_t dash_spv_ffi_client_update_config(struct FFIDashSpvClient *client, + const FFIClientConfig *config); + int32_t dash_spv_ffi_client_start(struct FFIDashSpvClient *client); int32_t dash_spv_ffi_client_stop(struct FFIDashSpvClient *client); @@ -414,6 +425,16 @@ int32_t dash_spv_ffi_config_set_relay_transactions(FFIClientConfig *config, int32_t dash_spv_ffi_config_set_filter_load(FFIClientConfig *config, bool load_filters); +/** + * Enables or disables masternode synchronization + * + * # Safety + * - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet + * - The caller must ensure the config pointer remains valid for the duration of this call + */ +int32_t dash_spv_ffi_config_set_masternode_sync_enabled(FFIClientConfig *config, + bool enable); + /** * Gets the network type from the configuration * @@ -541,18 +562,45 @@ int32_t dash_spv_ffi_config_set_start_from_height(FFIClientConfig *config, int32_t dash_spv_ffi_config_set_wallet_creation_time(FFIClientConfig *config, uint32_t timestamp); +/** + * Get the latest checkpoint for the given network. + * + * # Safety + * - `out_height` must be a valid pointer to a `u32`. + * - `out_hash` must point to at least 32 writable bytes. + */ int32_t dash_spv_ffi_checkpoint_latest(FFINetwork network, uint32_t *out_height, uint8_t *out_hash); +/** + * Get the last checkpoint at or before a given height. + * + * # Safety + * - `out_height` must be a valid pointer to a `u32`. + * - `out_hash` must point to at least 32 writable bytes. + */ int32_t dash_spv_ffi_checkpoint_before_height(FFINetwork network, uint32_t height, uint32_t *out_height, uint8_t *out_hash); +/** + * Get the last checkpoint at or before a given UNIX timestamp (seconds). + * + * # Safety + * - `out_height` must be a valid pointer to a `u32`. + * - `out_hash` must point to at least 32 writable bytes. + */ int32_t dash_spv_ffi_checkpoint_before_timestamp(FFINetwork network, uint32_t timestamp, uint32_t *out_height, uint8_t *out_hash); +/** + * Get all checkpoints between two heights (inclusive). + * + * Returns an `FFIArray` of `FFICheckpoint` items. The caller owns the memory and + * must free the array buffer using `dash_spv_ffi_array_destroy` when done. + */ struct FFIArray dash_spv_ffi_checkpoints_between_heights(FFINetwork network, uint32_t start_height, uint32_t end_height); diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index 2b7148e14..71d09a649 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -339,6 +339,43 @@ impl FFIDashSpvClient { } } +/// Update the running client's configuration. +/// +/// # Safety +/// - `client` must be a valid pointer to an `FFIDashSpvClient`. +/// - `config` must be a valid pointer to an `FFIClientConfig`. +/// - The network in `config` must match the client's network; changing networks at runtime is not supported. +#[no_mangle] +pub unsafe extern "C" fn dash_spv_ffi_client_update_config( + client: *mut FFIDashSpvClient, + config: *const FFIClientConfig, +) -> i32 { + null_check!(client); + null_check!(config); + + let client = &(*client); + let new_config = (&*config).clone_inner(); + + let result = client.runtime.block_on(async { + let mut guard = client.inner.lock().unwrap(); + if let Some(ref mut spv_client) = *guard { + spv_client.update_config(new_config).await.map_err(|e| e) + } else { + Err(dash_spv::SpvError::Config( + "Client not initialized".to_string(), + )) + } + }); + + match result { + Ok(()) => FFIErrorCode::Success as i32, + Err(e) => { + set_last_error(&e.to_string()); + FFIErrorCode::from(e) as i32 + } + } +} + #[no_mangle] pub unsafe extern "C" fn dash_spv_ffi_client_start(client: *mut FFIDashSpvClient) -> i32 { null_check!(client); diff --git a/dash-spv-ffi/src/config.rs b/dash-spv-ffi/src/config.rs index fb52a0eb0..31eb16501 100644 --- a/dash-spv-ffi/src/config.rs +++ b/dash-spv-ffi/src/config.rs @@ -208,6 +208,23 @@ pub unsafe extern "C" fn dash_spv_ffi_config_set_filter_load( FFIErrorCode::Success as i32 } +/// Enables or disables masternode synchronization +/// +/// # Safety +/// - `config` must be a valid pointer to an FFIClientConfig created by dash_spv_ffi_config_new/mainnet/testnet +/// - The caller must ensure the config pointer remains valid for the duration of this call +#[no_mangle] +pub unsafe extern "C" fn dash_spv_ffi_config_set_masternode_sync_enabled( + config: *mut FFIClientConfig, + enable: bool, +) -> i32 { + null_check!(config); + + let config = &mut (*config).inner; + config.enable_masternodes = enable; + FFIErrorCode::Success as i32 +} + /// Gets the network type from the configuration /// /// # Safety diff --git a/dash-spv/src/client/mod.rs b/dash-spv/src/client/mod.rs index 4cabe073d..742ce8f31 100644 --- a/dash-spv/src/client/mod.rs +++ b/dash-spv/src/client/mod.rs @@ -611,6 +611,38 @@ impl< Ok(()) } + /// Update the client's configuration at runtime. + /// + /// This applies non-network-critical settings without restarting the client. + /// Changing the network is not supported at runtime. + pub async fn update_config(&mut self, new_config: ClientConfig) -> Result<()> { + if new_config.network != self.config.network { + return Err(SpvError::Config( + "Cannot change network at runtime".to_string(), + )); + } + + // Track changes that may require reinitialization of helpers + let mempool_changed = + new_config.enable_mempool_tracking != self.config.enable_mempool_tracking + || new_config.mempool_strategy != self.config.mempool_strategy + || new_config.max_mempool_transactions != self.config.max_mempool_transactions + || new_config.recent_send_window_secs != self.config.recent_send_window_secs; + + // Apply full config replacement, preserving network (already checked equal) + self.config = new_config; + + // Update validation manager according to new mode + self.validation = ValidationManager::new(self.config.validation_mode); + + // Rebuild mempool filter if needed + if mempool_changed { + self.update_mempool_filter().await; + } + + Ok(()) + } + /// Synchronize to the tip of the blockchain. pub async fn sync_to_tip(&mut self) -> Result { let running = self.running.read().await; From 77a3d84385a7799dcdd68354700ae4e22d5b3a23 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 2 Sep 2025 04:46:22 +0700 Subject: [PATCH 2/3] fmt --- dash-spv-ffi/src/checkpoints.rs | 2 +- dash-spv-ffi/src/client.rs | 4 +--- dash-spv-ffi/src/lib.rs | 4 ++-- dash-spv/src/client/mod.rs | 14 ++++++-------- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/dash-spv-ffi/src/checkpoints.rs b/dash-spv-ffi/src/checkpoints.rs index 8c949a76a..0a919f125 100644 --- a/dash-spv-ffi/src/checkpoints.rs +++ b/dash-spv-ffi/src/checkpoints.rs @@ -1,7 +1,7 @@ use crate::{set_last_error, FFIArray, FFIErrorCode}; use dash_spv::chain::checkpoints::{mainnet_checkpoints, testnet_checkpoints, CheckpointManager}; -use dashcore::Network; use dashcore::hashes::Hash; +use dashcore::Network; use key_wallet_ffi::FFINetwork; /// FFI representation of a checkpoint (height + block hash) diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index 71d09a649..635f41b80 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -361,9 +361,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_update_config( if let Some(ref mut spv_client) = *guard { spv_client.update_config(new_config).await.map_err(|e| e) } else { - Err(dash_spv::SpvError::Config( - "Client not initialized".to_string(), - )) + Err(dash_spv::SpvError::Config("Client not initialized".to_string())) } }); diff --git a/dash-spv-ffi/src/lib.rs b/dash-spv-ffi/src/lib.rs index f6beb755d..84e0ccb9a 100644 --- a/dash-spv-ffi/src/lib.rs +++ b/dash-spv-ffi/src/lib.rs @@ -1,16 +1,16 @@ pub mod callbacks; +pub mod checkpoints; pub mod client; pub mod config; -pub mod checkpoints; pub mod error; pub mod platform_integration; pub mod types; pub mod utils; pub use callbacks::*; +pub use checkpoints::*; pub use client::*; pub use config::*; -pub use checkpoints::*; pub use error::*; pub use platform_integration::*; pub use types::*; diff --git a/dash-spv/src/client/mod.rs b/dash-spv/src/client/mod.rs index 742ce8f31..fec1c8624 100644 --- a/dash-spv/src/client/mod.rs +++ b/dash-spv/src/client/mod.rs @@ -617,17 +617,15 @@ impl< /// Changing the network is not supported at runtime. pub async fn update_config(&mut self, new_config: ClientConfig) -> Result<()> { if new_config.network != self.config.network { - return Err(SpvError::Config( - "Cannot change network at runtime".to_string(), - )); + return Err(SpvError::Config("Cannot change network at runtime".to_string())); } // Track changes that may require reinitialization of helpers - let mempool_changed = - new_config.enable_mempool_tracking != self.config.enable_mempool_tracking - || new_config.mempool_strategy != self.config.mempool_strategy - || new_config.max_mempool_transactions != self.config.max_mempool_transactions - || new_config.recent_send_window_secs != self.config.recent_send_window_secs; + let mempool_changed = new_config.enable_mempool_tracking + != self.config.enable_mempool_tracking + || new_config.mempool_strategy != self.config.mempool_strategy + || new_config.max_mempool_transactions != self.config.max_mempool_transactions + || new_config.recent_send_window_secs != self.config.recent_send_window_secs; // Apply full config replacement, preserving network (already checked equal) self.config = new_config; From 1dc3471eefe6c633283cfcdc0426ff793bc4a371 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 2 Sep 2025 04:51:40 +0700 Subject: [PATCH 3/3] clippy fixes --- dash-spv-ffi/include/dash_spv_ffi.h | 124 ++++++++++++++-------------- dash-spv-ffi/src/checkpoints.rs | 30 +++---- 2 files changed, 74 insertions(+), 80 deletions(-) diff --git a/dash-spv-ffi/include/dash_spv_ffi.h b/dash-spv-ffi/include/dash_spv_ffi.h index 33c06a1a4..57fd5942f 100644 --- a/dash-spv-ffi/include/dash_spv_ffi.h +++ b/dash-spv-ffi/include/dash_spv_ffi.h @@ -30,6 +30,25 @@ typedef enum FFIValidationMode { */ typedef struct FFIDashSpvClient FFIDashSpvClient; +/** + * FFI-safe array that transfers ownership of memory to the C caller. + * + * # Safety + * + * This struct represents memory that has been allocated by Rust but ownership + * has been transferred to the C caller. The caller is responsible for: + * - Not accessing the memory after it has been freed + * - Calling `dash_spv_ffi_array_destroy` to properly deallocate the memory + * - Ensuring the data, len, and capacity fields remain consistent + */ +typedef struct FFIArray { + void *data; + uintptr_t len; + uintptr_t capacity; + uintptr_t elem_size; + uintptr_t elem_align; +} FFIArray; + typedef ClientConfig FFIClientConfig; typedef struct FFIString { @@ -129,25 +148,6 @@ typedef struct FFIEventCallbacks { void *user_data; } FFIEventCallbacks; -/** - * FFI-safe array that transfers ownership of memory to the C caller. - * - * # Safety - * - * This struct represents memory that has been allocated by Rust but ownership - * has been transferred to the C caller. The caller is responsible for: - * - Not accessing the memory after it has been freed - * - Calling `dash_spv_ffi_array_destroy` to properly deallocate the memory - * - Ensuring the data, len, and capacity fields remain consistent - */ -typedef struct FFIArray { - void *data; - uintptr_t len; - uintptr_t capacity; - uintptr_t elem_size; - uintptr_t elem_align; -} FFIArray; - /** * Handle for Core SDK that can be passed to Platform SDK */ @@ -196,6 +196,49 @@ typedef struct FFIUnconfirmedTransaction { uintptr_t addresses_len; } FFIUnconfirmedTransaction; +/** + * Get the latest checkpoint for the given network. + * + * # Safety + * - `out_height` must be a valid pointer to a `u32`. + * - `out_hash` must point to at least 32 writable bytes. + */ +int32_t dash_spv_ffi_checkpoint_latest(FFINetwork network, uint32_t *out_height, uint8_t *out_hash); + +/** + * Get the last checkpoint at or before a given height. + * + * # Safety + * - `out_height` must be a valid pointer to a `u32`. + * - `out_hash` must point to at least 32 writable bytes. + */ +int32_t dash_spv_ffi_checkpoint_before_height(FFINetwork network, + uint32_t height, + uint32_t *out_height, + uint8_t *out_hash); + +/** + * Get the last checkpoint at or before a given UNIX timestamp (seconds). + * + * # Safety + * - `out_height` must be a valid pointer to a `u32`. + * - `out_hash` must point to at least 32 writable bytes. + */ +int32_t dash_spv_ffi_checkpoint_before_timestamp(FFINetwork network, + uint32_t timestamp, + uint32_t *out_height, + uint8_t *out_hash); + +/** + * Get all checkpoints between two heights (inclusive). + * + * Returns an `FFIArray` of `FFICheckpoint` items. The caller owns the memory and + * must free the array buffer using `dash_spv_ffi_array_destroy` when done. + */ +struct FFIArray dash_spv_ffi_checkpoints_between_heights(FFINetwork network, + uint32_t start_height, + uint32_t end_height); + struct FFIDashSpvClient *dash_spv_ffi_client_new(const FFIClientConfig *config); /** @@ -562,49 +605,6 @@ int32_t dash_spv_ffi_config_set_start_from_height(FFIClientConfig *config, int32_t dash_spv_ffi_config_set_wallet_creation_time(FFIClientConfig *config, uint32_t timestamp); -/** - * Get the latest checkpoint for the given network. - * - * # Safety - * - `out_height` must be a valid pointer to a `u32`. - * - `out_hash` must point to at least 32 writable bytes. - */ -int32_t dash_spv_ffi_checkpoint_latest(FFINetwork network, uint32_t *out_height, uint8_t *out_hash); - -/** - * Get the last checkpoint at or before a given height. - * - * # Safety - * - `out_height` must be a valid pointer to a `u32`. - * - `out_hash` must point to at least 32 writable bytes. - */ -int32_t dash_spv_ffi_checkpoint_before_height(FFINetwork network, - uint32_t height, - uint32_t *out_height, - uint8_t *out_hash); - -/** - * Get the last checkpoint at or before a given UNIX timestamp (seconds). - * - * # Safety - * - `out_height` must be a valid pointer to a `u32`. - * - `out_hash` must point to at least 32 writable bytes. - */ -int32_t dash_spv_ffi_checkpoint_before_timestamp(FFINetwork network, - uint32_t timestamp, - uint32_t *out_height, - uint8_t *out_hash); - -/** - * Get all checkpoints between two heights (inclusive). - * - * Returns an `FFIArray` of `FFICheckpoint` items. The caller owns the memory and - * must free the array buffer using `dash_spv_ffi_array_destroy` when done. - */ -struct FFIArray dash_spv_ffi_checkpoints_between_heights(FFINetwork network, - uint32_t start_height, - uint32_t end_height); - const char *dash_spv_ffi_get_last_error(void); void dash_spv_ffi_clear_error(void); diff --git a/dash-spv-ffi/src/checkpoints.rs b/dash-spv-ffi/src/checkpoints.rs index 0a919f125..177a67d81 100644 --- a/dash-spv-ffi/src/checkpoints.rs +++ b/dash-spv-ffi/src/checkpoints.rs @@ -26,7 +26,7 @@ fn manager_for_network(network: FFINetwork) -> Result /// - `out_height` must be a valid pointer to a `u32`. /// - `out_hash` must point to at least 32 writable bytes. #[no_mangle] -pub extern "C" fn dash_spv_ffi_checkpoint_latest( +pub unsafe extern "C" fn dash_spv_ffi_checkpoint_latest( network: FFINetwork, out_height: *mut u32, out_hash: *mut u8, // expects at least 32 bytes @@ -43,11 +43,9 @@ pub extern "C" fn dash_spv_ffi_checkpoint_latest( } }; if let Some(cp) = mgr.last_checkpoint() { - unsafe { - *out_height = cp.height; - let hash = cp.block_hash.to_byte_array(); - std::ptr::copy_nonoverlapping(hash.as_ptr(), out_hash, 32); - } + *out_height = cp.height; + let hash = cp.block_hash.to_byte_array(); + std::ptr::copy_nonoverlapping(hash.as_ptr(), out_hash, 32); FFIErrorCode::Success as i32 } else { set_last_error("No checkpoints available for network"); @@ -61,7 +59,7 @@ pub extern "C" fn dash_spv_ffi_checkpoint_latest( /// - `out_height` must be a valid pointer to a `u32`. /// - `out_hash` must point to at least 32 writable bytes. #[no_mangle] -pub extern "C" fn dash_spv_ffi_checkpoint_before_height( +pub unsafe extern "C" fn dash_spv_ffi_checkpoint_before_height( network: FFINetwork, height: u32, out_height: *mut u32, @@ -79,11 +77,9 @@ pub extern "C" fn dash_spv_ffi_checkpoint_before_height( } }; if let Some(cp) = mgr.last_checkpoint_before_height(height) { - unsafe { - *out_height = cp.height; - let hash = cp.block_hash.to_byte_array(); - std::ptr::copy_nonoverlapping(hash.as_ptr(), out_hash, 32); - } + *out_height = cp.height; + let hash = cp.block_hash.to_byte_array(); + std::ptr::copy_nonoverlapping(hash.as_ptr(), out_hash, 32); FFIErrorCode::Success as i32 } else { set_last_error("No checkpoint at or before given height"); @@ -97,7 +93,7 @@ pub extern "C" fn dash_spv_ffi_checkpoint_before_height( /// - `out_height` must be a valid pointer to a `u32`. /// - `out_hash` must point to at least 32 writable bytes. #[no_mangle] -pub extern "C" fn dash_spv_ffi_checkpoint_before_timestamp( +pub unsafe extern "C" fn dash_spv_ffi_checkpoint_before_timestamp( network: FFINetwork, timestamp: u32, out_height: *mut u32, @@ -115,11 +111,9 @@ pub extern "C" fn dash_spv_ffi_checkpoint_before_timestamp( } }; if let Some(cp) = mgr.last_checkpoint_before_timestamp(timestamp) { - unsafe { - *out_height = cp.height; - let hash = cp.block_hash.to_byte_array(); - std::ptr::copy_nonoverlapping(hash.as_ptr(), out_hash, 32); - } + *out_height = cp.height; + let hash = cp.block_hash.to_byte_array(); + std::ptr::copy_nonoverlapping(hash.as_ptr(), out_hash, 32); FFIErrorCode::Success as i32 } else { set_last_error("No checkpoint at or before given timestamp");