DragonX compatibility: crash fixes, reorg detection, server failover, sync perf
- Fix Rust FFI panics with catch_unwind wrappers and safe CString handling - Handle poisoned mutex/RwLock from prior panics instead of crashing - Add stuck sync detection (10s stall threshold) and chain reorg user prompt - Add "Skip Verification" button to seed phrase wizard - Update payment URIs from hush: to drgx: - Update branding strings throughout UI - Add all 6 lite servers (lite, lite1-5.dragonx.is) with random selection - Add server connectivity probing to skip unreachable servers - Reuse Tokio runtime across block fetch batches to reduce sync overhead - Update Cargo.lock dependencies
This commit is contained in:
579
lib/src/lib.rs
579
lib/src/lib.rs
@@ -1,276 +1,303 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use libc::{c_char};
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::sync::{Mutex, Arc};
|
||||
use std::cell::RefCell;
|
||||
use std::ptr;
|
||||
|
||||
use silentdragonxlitelib::{commands, lightclient::{LightClient, LightClientConfig}};
|
||||
|
||||
// We'll use a MUTEX to store a global lightclient instance,
|
||||
// so we don't have to keep creating it. We need to store it here, in rust
|
||||
// because we can't return such a complex structure back to C++
|
||||
lazy_static! {
|
||||
static ref LIGHTCLIENT: Mutex<RefCell<Option<Arc<LightClient>>>> = Mutex::new(RefCell::new(None));
|
||||
}
|
||||
|
||||
// Check if there is an existing wallet
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_wallet_exists(chain_name: *const c_char) -> bool {
|
||||
let chain_name_str = unsafe {
|
||||
assert!(!chain_name.is_null());
|
||||
|
||||
CStr::from_ptr(chain_name).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let config = LightClientConfig::create_unconnected(chain_name_str, None);
|
||||
|
||||
println!("Wallet exists: {}", config.wallet_exists());
|
||||
config.wallet_exists()
|
||||
}
|
||||
|
||||
//////hash blake3
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn blake3_PW(pw: *const c_char) -> *mut c_char{
|
||||
|
||||
let passwd = unsafe {
|
||||
assert!(!pw.is_null());
|
||||
|
||||
CStr::from_ptr(pw).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let data = passwd.as_bytes();
|
||||
// Hash an input all at once.
|
||||
let hash1 = blake3::hash(data).to_hex();
|
||||
// This is sensitive metadata, do not log it to stdout
|
||||
//println!("\nBlake3 Hash: {}", hash1);
|
||||
println!("\nBlake3 Hash calculated");
|
||||
|
||||
//let sttring = CString::new(hash1).unwrap();
|
||||
let e_str = CString::new(format!("{}", hash1)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
|
||||
/// Create a new wallet and return the seed for the newly created wallet.
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_initialize_new(dangerous: bool,server: *const c_char) -> *mut c_char {
|
||||
let server_str = unsafe {
|
||||
assert!(!server.is_null());
|
||||
|
||||
CStr::from_ptr(server).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let server = LightClientConfig::get_server_or_default(Some(server_str));
|
||||
let (config, latest_block_height) = match LightClientConfig::create(server, dangerous) {
|
||||
Ok((c, h)) => (c, h),
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
let lightclient = match LightClient::new(&config, latest_block_height) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize logging
|
||||
let _ = lightclient.init_logging();
|
||||
|
||||
let seed = match lightclient.do_seed_phrase() {
|
||||
Ok(s) => s.dump(),
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
let lc = Arc::new(lightclient);
|
||||
match LightClient::start_mempool_monitor(lc.clone()) {
|
||||
Ok(_) => {println!("Starting Mempool")},
|
||||
Err(e) => {
|
||||
println!("Couldnt start mempool {}", e)
|
||||
}
|
||||
}
|
||||
|
||||
LIGHTCLIENT.lock().unwrap().replace(Some(lc));
|
||||
|
||||
// Return the wallet's seed
|
||||
let s_str = CString::new(seed).unwrap();
|
||||
return s_str.into_raw();
|
||||
}
|
||||
|
||||
/// Restore a wallet from the seed phrase
|
||||
#[no_mangle]
|
||||
pub extern "C" fn litelib_initialize_new_from_phrase(dangerous: bool, server: *const c_char,
|
||||
seed: *const c_char, birthday: u64, number: u64, overwrite: bool) -> *mut c_char {
|
||||
if server.is_null() || seed.is_null() {
|
||||
println!("Server or seed is null");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let server_str = unsafe {
|
||||
CStr::from_ptr(server).to_string_lossy().into_owned()
|
||||
};
|
||||
let seed_str = unsafe {
|
||||
CStr::from_ptr(seed).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
//println!("Initializing with server: {}, seed: {}", server_str, seed_str);
|
||||
|
||||
let server = LightClientConfig::get_server_or_default(Some(server_str));
|
||||
let (config, _latest_block_height) = match LightClientConfig::create(server, dangerous) {
|
||||
Ok((c, h)) => {
|
||||
println!("Config created successfully");
|
||||
(c, h)
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error creating config: {}", e);
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap_or_else(|_| CString::new("Error creating CString").unwrap());
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
let lightclient = match LightClient::new_from_phrase(seed_str, &config, birthday, number, overwrite) {
|
||||
Ok(l) => {
|
||||
println!("LightClient created successfully");
|
||||
l
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error creating LightClient: {}", e);
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap_or_else(|_| CString::new("Error creating CString").unwrap());
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize logging
|
||||
let _ = lightclient.init_logging();
|
||||
|
||||
let lc = Arc::new(lightclient);
|
||||
match LightClient::start_mempool_monitor(lc.clone()) {
|
||||
Ok(_) => println!("Starting Mempool"),
|
||||
Err(e) => println!("Could not start mempool: {}", e)
|
||||
}
|
||||
|
||||
LIGHTCLIENT.lock().unwrap().replace(Some(lc));
|
||||
|
||||
let c_str = CString::new("OK").unwrap_or_else(|_| CString::new("CString creation failed").unwrap());
|
||||
return c_str.into_raw();
|
||||
}
|
||||
|
||||
// Initialize a new lightclient and store its value
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_initialize_existing(dangerous: bool, server: *const c_char) -> *mut c_char {
|
||||
let server_str = unsafe {
|
||||
assert!(!server.is_null());
|
||||
|
||||
CStr::from_ptr(server).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let server = LightClientConfig::get_server_or_default(Some(server_str));
|
||||
let (config, _latest_block_height) = match LightClientConfig::create(server,dangerous) {
|
||||
Ok((c, h)) => (c, h),
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
let lightclient = match LightClient::read_from_disk(&config) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize logging
|
||||
let _ = lightclient.init_logging();
|
||||
|
||||
let lc = Arc::new(lightclient);
|
||||
match LightClient::start_mempool_monitor(lc.clone()) {
|
||||
Ok(_) => {println!("Starting Mempool")},
|
||||
Err(e) => {
|
||||
println!("Couldnt start mempool {}",e)
|
||||
}
|
||||
}
|
||||
|
||||
LIGHTCLIENT.lock().unwrap().replace(Some(lc));
|
||||
|
||||
let c_str = CString::new("OK").unwrap();
|
||||
return c_str.into_raw();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_execute(cmd: *const c_char, args: *const c_char) -> *mut c_char {
|
||||
let cmd_str = unsafe {
|
||||
assert!(!cmd.is_null());
|
||||
|
||||
CStr::from_ptr(cmd).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let arg_str = unsafe {
|
||||
assert!(!args.is_null());
|
||||
|
||||
CStr::from_ptr(args).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let resp: String;
|
||||
{
|
||||
let lightclient: Arc<LightClient>;
|
||||
{
|
||||
let lc = LIGHTCLIENT.lock().unwrap();
|
||||
|
||||
if lc.borrow().is_none() {
|
||||
let e_str = CString::new("Error: Light Client is not initialized").unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
|
||||
lightclient = lc.borrow().as_ref().unwrap().clone();
|
||||
};
|
||||
|
||||
let args = if arg_str.is_empty() { vec![] } else { vec![arg_str.as_ref()] };
|
||||
|
||||
resp = commands::do_user_command(&cmd_str, &args, lightclient.as_ref()).clone();
|
||||
};
|
||||
|
||||
let c_str = CString::new(resp.as_bytes()).unwrap();
|
||||
return c_str.into_raw();
|
||||
}
|
||||
|
||||
// Check is Server Connection is fine
|
||||
#[no_mangle]
|
||||
pub extern "C" fn litelib_check_server_online(server: *const c_char) -> bool {
|
||||
let server_str = unsafe {
|
||||
assert!(!server.is_null());
|
||||
|
||||
CStr::from_ptr(server).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let server = LightClientConfig::get_server_or_default(Some(server_str));
|
||||
let result = LightClientConfig::create(server, false);
|
||||
|
||||
match result {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callers that receive string return values from other functions should call this to return the string
|
||||
* back to rust, so it can be freed. Failure to call this function will result in a memory leak
|
||||
*/
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_rust_free_string(s: *mut c_char) {
|
||||
unsafe {
|
||||
if s.is_null() { return }
|
||||
CString::from_raw(s)
|
||||
};
|
||||
}
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use libc::{c_char};
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::sync::{Mutex, Arc};
|
||||
use std::cell::RefCell;
|
||||
use std::ptr;
|
||||
use std::panic;
|
||||
|
||||
use silentdragonxlitelib::{commands, lightclient::{LightClient, LightClientConfig}};
|
||||
|
||||
/// Helper to create a CString, replacing null bytes to avoid panics
|
||||
fn safe_cstring(s: &str) -> CString {
|
||||
let cleaned: String = s.replace('\0', "");
|
||||
CString::new(cleaned).unwrap_or_else(|_| CString::new("Error: failed to create CString").unwrap())
|
||||
}
|
||||
|
||||
/// Helper to create an error CString
|
||||
fn error_cstring(msg: &str) -> *mut c_char {
|
||||
safe_cstring(&format!("Error: {}", msg)).into_raw()
|
||||
}
|
||||
|
||||
// We'll use a MUTEX to store a global lightclient instance,
|
||||
// so we don't have to keep creating it. We need to store it here, in rust
|
||||
// because we can't return such a complex structure back to C++
|
||||
lazy_static! {
|
||||
static ref LIGHTCLIENT: Mutex<RefCell<Option<Arc<LightClient>>>> = Mutex::new(RefCell::new(None));
|
||||
}
|
||||
|
||||
// Check if there is an existing wallet
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_wallet_exists(chain_name: *const c_char) -> bool {
|
||||
let chain_name_str = unsafe {
|
||||
assert!(!chain_name.is_null());
|
||||
|
||||
CStr::from_ptr(chain_name).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let config = LightClientConfig::create_unconnected(chain_name_str, None);
|
||||
|
||||
println!("Wallet exists: {}", config.wallet_exists());
|
||||
config.wallet_exists()
|
||||
}
|
||||
|
||||
//////hash blake3
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn blake3_PW(pw: *const c_char) -> *mut c_char{
|
||||
|
||||
let passwd = unsafe {
|
||||
assert!(!pw.is_null());
|
||||
|
||||
CStr::from_ptr(pw).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let data = passwd.as_bytes();
|
||||
// Hash an input all at once.
|
||||
let hash1 = blake3::hash(data).to_hex();
|
||||
// This is sensitive metadata, do not log it to stdout
|
||||
//println!("\nBlake3 Hash: {}", hash1);
|
||||
println!("\nBlake3 Hash calculated");
|
||||
|
||||
//let sttring = CString::new(hash1).unwrap();
|
||||
let e_str = CString::new(format!("{}", hash1)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
|
||||
/// Create a new wallet and return the seed for the newly created wallet.
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_initialize_new(dangerous: bool,server: *const c_char) -> *mut c_char {
|
||||
let server_str = unsafe {
|
||||
assert!(!server.is_null());
|
||||
|
||||
CStr::from_ptr(server).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let server = LightClientConfig::get_server_or_default(Some(server_str));
|
||||
let (config, latest_block_height) = match LightClientConfig::create(server, dangerous) {
|
||||
Ok((c, h)) => (c, h),
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
let lightclient = match LightClient::new(&config, latest_block_height) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize logging
|
||||
let _ = lightclient.init_logging();
|
||||
|
||||
let seed = match lightclient.do_seed_phrase() {
|
||||
Ok(s) => s.dump(),
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
let lc = Arc::new(lightclient);
|
||||
match LightClient::start_mempool_monitor(lc.clone()) {
|
||||
Ok(_) => {println!("Starting Mempool")},
|
||||
Err(e) => {
|
||||
println!("Couldnt start mempool {}", e)
|
||||
}
|
||||
}
|
||||
|
||||
match LIGHTCLIENT.lock() {
|
||||
Ok(l) => { l.replace(Some(lc)); },
|
||||
Err(poisoned) => { poisoned.into_inner().replace(Some(lc)); },
|
||||
};
|
||||
|
||||
// Return the wallet's seed
|
||||
let s_str = safe_cstring(&seed);
|
||||
return s_str.into_raw();
|
||||
}
|
||||
|
||||
/// Restore a wallet from the seed phrase
|
||||
#[no_mangle]
|
||||
pub extern "C" fn litelib_initialize_new_from_phrase(dangerous: bool, server: *const c_char,
|
||||
seed: *const c_char, birthday: u64, number: u64, overwrite: bool) -> *mut c_char {
|
||||
if server.is_null() || seed.is_null() {
|
||||
println!("Server or seed is null");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let server_str = unsafe {
|
||||
CStr::from_ptr(server).to_string_lossy().into_owned()
|
||||
};
|
||||
let seed_str = unsafe {
|
||||
CStr::from_ptr(seed).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
//println!("Initializing with server: {}, seed: {}", server_str, seed_str);
|
||||
|
||||
let server = LightClientConfig::get_server_or_default(Some(server_str));
|
||||
let (config, _latest_block_height) = match LightClientConfig::create(server, dangerous) {
|
||||
Ok((c, h)) => {
|
||||
println!("Config created successfully");
|
||||
(c, h)
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error creating config: {}", e);
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap_or_else(|_| CString::new("Error creating CString").unwrap());
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
let lightclient = match LightClient::new_from_phrase(seed_str, &config, birthday, number, overwrite) {
|
||||
Ok(l) => {
|
||||
println!("LightClient created successfully");
|
||||
l
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error creating LightClient: {}", e);
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap_or_else(|_| CString::new("Error creating CString").unwrap());
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize logging
|
||||
let _ = lightclient.init_logging();
|
||||
|
||||
let lc = Arc::new(lightclient);
|
||||
match LightClient::start_mempool_monitor(lc.clone()) {
|
||||
Ok(_) => println!("Starting Mempool"),
|
||||
Err(e) => println!("Could not start mempool: {}", e)
|
||||
}
|
||||
|
||||
match LIGHTCLIENT.lock() {
|
||||
Ok(l) => { l.replace(Some(lc)); },
|
||||
Err(poisoned) => { poisoned.into_inner().replace(Some(lc)); },
|
||||
};
|
||||
|
||||
let c_str = safe_cstring("OK");
|
||||
return c_str.into_raw();
|
||||
}
|
||||
|
||||
// Initialize a new lightclient and store its value
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_initialize_existing(dangerous: bool, server: *const c_char) -> *mut c_char {
|
||||
let server_str = unsafe {
|
||||
assert!(!server.is_null());
|
||||
|
||||
CStr::from_ptr(server).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let server = LightClientConfig::get_server_or_default(Some(server_str));
|
||||
let (config, _latest_block_height) = match LightClientConfig::create(server,dangerous) {
|
||||
Ok((c, h)) => (c, h),
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
let lightclient = match LightClient::read_from_disk(&config) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
let e_str = CString::new(format!("Error: {}", e)).unwrap();
|
||||
return e_str.into_raw();
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize logging
|
||||
let _ = lightclient.init_logging();
|
||||
|
||||
let lc = Arc::new(lightclient);
|
||||
match LightClient::start_mempool_monitor(lc.clone()) {
|
||||
Ok(_) => {println!("Starting Mempool")},
|
||||
Err(e) => {
|
||||
println!("Couldnt start mempool {}",e)
|
||||
}
|
||||
}
|
||||
|
||||
match LIGHTCLIENT.lock() {
|
||||
Ok(l) => { l.replace(Some(lc)); },
|
||||
Err(poisoned) => { poisoned.into_inner().replace(Some(lc)); },
|
||||
};
|
||||
|
||||
let c_str = safe_cstring("OK");
|
||||
return c_str.into_raw();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_execute(cmd: *const c_char, args: *const c_char) -> *mut c_char {
|
||||
let result = panic::catch_unwind(|| {
|
||||
let cmd_str = unsafe {
|
||||
assert!(!cmd.is_null());
|
||||
CStr::from_ptr(cmd).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let arg_str = unsafe {
|
||||
assert!(!args.is_null());
|
||||
CStr::from_ptr(args).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let resp: String;
|
||||
{
|
||||
let lightclient: Arc<LightClient>;
|
||||
{
|
||||
let lc = match LIGHTCLIENT.lock() {
|
||||
Ok(l) => l,
|
||||
Err(poisoned) => poisoned.into_inner(),
|
||||
};
|
||||
|
||||
if lc.borrow().is_none() {
|
||||
return error_cstring("Light Client is not initialized");
|
||||
}
|
||||
|
||||
lightclient = lc.borrow().as_ref().unwrap().clone();
|
||||
};
|
||||
|
||||
let args = if arg_str.is_empty() { vec![] } else { vec![arg_str.as_ref()] };
|
||||
|
||||
resp = commands::do_user_command(&cmd_str, &args, lightclient.as_ref()).clone();
|
||||
};
|
||||
|
||||
safe_cstring(&resp).into_raw()
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(ptr) => ptr,
|
||||
Err(_) => error_cstring("Rust panic in litelib_execute"),
|
||||
}
|
||||
}
|
||||
|
||||
// Check is Server Connection is fine
|
||||
#[no_mangle]
|
||||
pub extern "C" fn litelib_check_server_online(server: *const c_char) -> bool {
|
||||
let server_str = unsafe {
|
||||
assert!(!server.is_null());
|
||||
|
||||
CStr::from_ptr(server).to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let server = LightClientConfig::get_server_or_default(Some(server_str));
|
||||
let result = LightClientConfig::create(server, false);
|
||||
|
||||
match result {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callers that receive string return values from other functions should call this to return the string
|
||||
* back to rust, so it can be freed. Failure to call this function will result in a memory leak
|
||||
*/
|
||||
#[no_mangle]
|
||||
pub extern fn litelib_rust_free_string(s: *mut c_char) {
|
||||
unsafe {
|
||||
if s.is_null() { return }
|
||||
CString::from_raw(s)
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user