diff --git a/third_party/silentdragonxlite/lib/src/lib.rs b/third_party/silentdragonxlite/lib/src/lib.rs index 1b1fcbd..0267f21 100644 --- a/third_party/silentdragonxlite/lib/src/lib.rs +++ b/third_party/silentdragonxlite/lib/src/lib.rs @@ -22,6 +22,15 @@ fn error_cstring(msg: &str) -> *mut c_char { safe_cstring(&format!("Error: {}", msg)).into_raw() } +/// Run a C entry point's body and turn a Rust panic into an "Error: ..." string (C244): a panic must never unwind +/// across the C ABI. This relies on panic = "unwind" (lib/Cargo.toml [profile.release]). +fn ffi_guard *mut c_char>(name: &str, f: F) -> *mut c_char { + match panic::catch_unwind(panic::AssertUnwindSafe(f)) { + Ok(p) => p, + Err(_) => error_cstring(&format!("Rust panic in {}", name)), + } +} + // 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++ @@ -70,6 +79,10 @@ pub extern fn blake3_PW(pw: *const c_char) -> *mut c_char{ /// 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 { + ffi_guard("litelib_initialize_new", || litelib_initialize_new_impl(dangerous, server)) +} + +fn litelib_initialize_new_impl(dangerous: bool, server: *const c_char) -> *mut c_char { let server_str = unsafe { assert!(!server.is_null()); @@ -126,6 +139,12 @@ pub extern fn litelib_initialize_new(dangerous: bool,server: *const c_char) -> * #[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 { + ffi_guard("litelib_initialize_new_from_phrase", + || litelib_initialize_new_from_phrase_impl(dangerous, server, seed, birthday, number, overwrite)) +} + +fn litelib_initialize_new_from_phrase_impl(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(); @@ -197,6 +216,10 @@ pub extern "C" fn litelib_initialize_new_from_phrase(dangerous: bool, server: *c // 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 { + ffi_guard("litelib_initialize_existing", || litelib_initialize_existing_impl(dangerous, server)) +} + +fn litelib_initialize_existing_impl(dangerous: bool, server: *const c_char) -> *mut c_char { let server_str = unsafe { assert!(!server.is_null()); @@ -212,8 +235,10 @@ pub extern fn litelib_initialize_existing(dangerous: bool, server: *const c_char } }; - let lightclient = match LightClient::read_from_disk(&config) { - Ok(l) => l, + // s2-D14: the core reports a recovery from the backup; pass it on (the C++ side treats any non-error string as + // a successful open). + let (lightclient, recovered) = match LightClient::read_from_disk_reporting(&config) { + Ok(r) => r, Err(e) => { let e_str = CString::new(format!("Error: {}", e)).unwrap(); return e_str.into_raw(); @@ -236,7 +261,10 @@ pub extern fn litelib_initialize_existing(dangerous: bool, server: *const c_char Err(poisoned) => { poisoned.into_inner().replace(Some(lc)); }, }; - let c_str = safe_cstring("OK"); + let c_str = match recovered { + Some(note) => safe_cstring(&format!("OK: {}", note)), + None => safe_cstring("OK"), + }; return c_str.into_raw(); } @@ -286,6 +314,11 @@ pub extern fn litelib_execute(cmd: *const c_char, args: *const c_char) -> *mut c // Check is Server Connection is fine #[no_mangle] pub extern "C" fn litelib_check_server_online(server: *const c_char) -> bool { + // A malformed server URL panics in get_server_or_default (and get_client); never unwind across the C ABI. + panic::catch_unwind(move || litelib_check_server_online_impl(server)).unwrap_or(false) +} + +fn litelib_check_server_online_impl(server: *const c_char) -> bool { let server_str = unsafe { assert!(!server.is_null());