|
1 | 1 | use std::cmp::Ordering; |
2 | 2 | use std::os::raw::c_char; |
3 | 3 | use std::ffi::{CString, CStr, c_void}; |
4 | | -use std::path::Path; |
| 4 | +use std::path::{Path, PathBuf}; |
5 | 5 | use std::sync::Arc; |
| 6 | +use std::sync::Once; |
6 | 7 | use rand::thread_rng; |
7 | 8 | use serde::{Deserialize, Serialize}; |
8 | 9 | use uuid::Uuid; |
@@ -102,6 +103,57 @@ macro_rules! ensure_wallet ( |
102 | 103 | ) |
103 | 104 | ); |
104 | 105 |
|
| 106 | +static PANIC_HOOK_INIT: Once = Once::new(); |
| 107 | + |
| 108 | +// Appends panics to ~/.stackwallet/flutter_libmwc-panic.log so worker-thread |
| 109 | +// panics that bypass stderr still leave a trail. |
| 110 | +fn install_panic_hook() { |
| 111 | + PANIC_HOOK_INIT.call_once(|| { |
| 112 | + let log_path = std::env::var_os("HOME") |
| 113 | + .map(PathBuf::from) |
| 114 | + .map(|p| p.join(".stackwallet/flutter_libmwc-panic.log")) |
| 115 | + .unwrap_or_else(|| PathBuf::from("/tmp/flutter_libmwc-panic.log")); |
| 116 | + if let Some(parent) = log_path.parent() { |
| 117 | + let _ = std::fs::create_dir_all(parent); |
| 118 | + } |
| 119 | + let previous = std::panic::take_hook(); |
| 120 | + std::panic::set_hook(Box::new(move |info| { |
| 121 | + let payload = info.payload(); |
| 122 | + let msg = if let Some(s) = payload.downcast_ref::<&str>() { |
| 123 | + (*s).to_string() |
| 124 | + } else if let Some(s) = payload.downcast_ref::<String>() { |
| 125 | + s.clone() |
| 126 | + } else { |
| 127 | + "<non-string panic payload>".to_string() |
| 128 | + }; |
| 129 | + let location = info.location() |
| 130 | + .map(|l| format!("{}:{}:{}", l.file(), l.line(), l.column())) |
| 131 | + .unwrap_or_else(|| "<unknown>".to_string()); |
| 132 | + let backtrace = std::backtrace::Backtrace::force_capture(); |
| 133 | + let timestamp = std::time::SystemTime::now() |
| 134 | + .duration_since(std::time::UNIX_EPOCH) |
| 135 | + .map(|d| d.as_secs()) |
| 136 | + .unwrap_or(0); |
| 137 | + let thread = std::thread::current(); |
| 138 | + let thread_name = thread.name().unwrap_or("<unnamed>"); |
| 139 | + let entry = format!( |
| 140 | + "\n=== flutter_libmwc panic at unix={} ===\nthread: {}\nlocation: {}\nmessage: {}\nbacktrace:\n{}\n", |
| 141 | + timestamp, thread_name, location, msg, backtrace, |
| 142 | + ); |
| 143 | + if let Ok(mut f) = std::fs::OpenOptions::new() |
| 144 | + .create(true) |
| 145 | + .append(true) |
| 146 | + .open(&log_path) |
| 147 | + { |
| 148 | + use std::io::Write; |
| 149 | + let _ = f.write_all(entry.as_bytes()); |
| 150 | + let _ = f.flush(); |
| 151 | + } |
| 152 | + previous(info); |
| 153 | + })); |
| 154 | + }); |
| 155 | +} |
| 156 | + |
105 | 157 |
|
106 | 158 | fn init_logger() { |
107 | 159 | android_logger::init_once( |
@@ -276,6 +328,7 @@ pub unsafe extern "C" fn mwc_rust_open_wallet( |
276 | 328 | config: *const c_char, |
277 | 329 | password: *const c_char, |
278 | 330 | ) -> *const c_char { |
| 331 | + install_panic_hook(); |
279 | 332 | //init_logger(); |
280 | 333 | let result = match _open_wallet( |
281 | 334 | config, |
@@ -475,36 +528,49 @@ pub unsafe extern "C" fn mwc_rust_wallet_scan_outputs( |
475 | 528 | start_height: *const c_char, |
476 | 529 | number_of_blocks: *const c_char, |
477 | 530 | ) -> *const c_char { |
478 | | - let wallet_ptr = CStr::from_ptr(wallet); |
479 | | - let c_start_height = CStr::from_ptr(start_height); |
480 | | - let c_number_of_blocks = CStr::from_ptr(number_of_blocks); |
481 | | - let start_height: u64 = c_start_height.to_str().unwrap().to_string().parse().unwrap(); |
482 | | - let number_of_blocks: u64 = c_number_of_blocks.to_str().unwrap().to_string().parse().unwrap(); |
483 | | - |
484 | | - let wallet_data = wallet_ptr.to_str().unwrap(); |
485 | | - let tuple_wallet_data: (u64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap(); |
486 | | - let wlt = tuple_wallet_data.0; |
487 | | - let sek_key = tuple_wallet_data.1; |
488 | | - |
489 | | - ensure_wallet!(wlt, wallet); |
| 531 | + install_panic_hook(); |
| 532 | + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { |
| 533 | + let wallet_ptr = CStr::from_ptr(wallet); |
| 534 | + let c_start_height = CStr::from_ptr(start_height); |
| 535 | + let c_number_of_blocks = CStr::from_ptr(number_of_blocks); |
| 536 | + let start_height: u64 = c_start_height.to_str().unwrap().to_string().parse().unwrap(); |
| 537 | + let number_of_blocks: u64 = c_number_of_blocks.to_str().unwrap().to_string().parse().unwrap(); |
| 538 | + |
| 539 | + let wallet_data = wallet_ptr.to_str().unwrap(); |
| 540 | + let tuple_wallet_data: (u64, Option<SecretKey>) = serde_json::from_str(wallet_data).unwrap(); |
| 541 | + let wlt = tuple_wallet_data.0; |
| 542 | + let sek_key = tuple_wallet_data.1; |
490 | 543 |
|
491 | | - let result = match _wallet_scan_outputs( |
492 | | - wallet, |
493 | | - sek_key, |
494 | | - start_height, |
495 | | - number_of_blocks |
496 | | - ) { |
497 | | - Ok(scan) => { |
498 | | - scan |
499 | | - }, Err(e ) => { |
500 | | - let error_msg = format!("Error {}", &e.to_string()); |
501 | | - let error_msg_ptr = CString::new(error_msg).unwrap(); |
502 | | - let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s |
503 | | - std::mem::forget(error_msg_ptr); |
| 544 | + ensure_wallet!(wlt, wallet); |
| 545 | + |
| 546 | + match _wallet_scan_outputs( |
| 547 | + wallet, |
| 548 | + sek_key, |
| 549 | + start_height, |
| 550 | + number_of_blocks |
| 551 | + ) { |
| 552 | + Ok(scan) => scan, |
| 553 | + Err(e) => { |
| 554 | + let error_msg = format!("Error {}", &e.to_string()); |
| 555 | + let error_msg_ptr = CString::new(error_msg).unwrap(); |
| 556 | + let ptr = error_msg_ptr.as_ptr(); |
| 557 | + std::mem::forget(error_msg_ptr); |
| 558 | + ptr |
| 559 | + } |
| 560 | + } |
| 561 | + })); |
| 562 | + match result { |
| 563 | + Ok(ptr) => ptr, |
| 564 | + Err(_) => { |
| 565 | + let error_msg = CString::new( |
| 566 | + "Error scanOutputs panicked; see flutter_libmwc-panic.log", |
| 567 | + ) |
| 568 | + .unwrap(); |
| 569 | + let ptr = error_msg.as_ptr(); |
| 570 | + std::mem::forget(error_msg); |
504 | 571 | ptr |
505 | 572 | } |
506 | | - }; |
507 | | - result |
| 573 | + } |
508 | 574 | } |
509 | 575 |
|
510 | 576 | fn _wallet_scan_outputs( |
@@ -751,6 +817,7 @@ fn _tx_cancel( |
751 | 817 | pub unsafe extern "C" fn mwc_rust_get_chain_height( |
752 | 818 | config: *const c_char, |
753 | 819 | ) -> *const c_char { |
| 820 | + install_panic_hook(); |
754 | 821 | let result = match _get_chain_height( |
755 | 822 | config |
756 | 823 | ) { |
@@ -1376,6 +1443,13 @@ pub fn get_chain_height(config: &str) -> Result<u64, Error> { |
1376 | 1443 | return Err(e); |
1377 | 1444 | } |
1378 | 1445 | }; |
| 1446 | + // get_chain_tip reads chain type; init it here since this entry point |
| 1447 | + // can fire before any get_wallet call. |
| 1448 | + let target_chaintype = wallet_config.chain_type.unwrap_or(ChainTypes::Mainnet); |
| 1449 | + global::set_global_chain_type(target_chaintype); |
| 1450 | + if global::get_chain_type() != target_chaintype { |
| 1451 | + global::set_local_chain_type(target_chaintype); |
| 1452 | + }; |
1379 | 1453 | let node_api_secret = get_first_line(wallet_config.node_api_secret_path.clone()); |
1380 | 1454 | let node_client = HTTPNodeClient::new(vec![wallet_config.check_node_api_http_addr], node_api_secret) |
1381 | 1455 | .map_err(|e| Error::ClientCallback(format!("{}", e)))?; |
@@ -1902,6 +1976,13 @@ impl Task for Listener { |
1902 | 1976 | type Output = usize; |
1903 | 1977 |
|
1904 | 1978 | fn run(&self, cancel_tok: &CancellationToken) -> Result<Self::Output, anyhow::Error> { |
| 1979 | + install_panic_hook(); |
| 1980 | + // export_task! runs us on a fresh thread; seed CHAIN_TYPE for it. |
| 1981 | + let target_chaintype = ChainTypes::Mainnet; |
| 1982 | + global::set_global_chain_type(target_chaintype); |
| 1983 | + if global::get_chain_type() != target_chaintype { |
| 1984 | + global::set_local_chain_type(target_chaintype); |
| 1985 | + } |
1905 | 1986 | let mut spins = 0; |
1906 | 1987 | let tuple_wallet_data: (u64, Option<SecretKey>) = serde_json::from_str(&self.wallet_ptr_str)?; |
1907 | 1988 | let wlt = tuple_wallet_data.0; |
@@ -1947,6 +2028,7 @@ pub unsafe extern "C" fn mwc_rust_mwcmqs_listener_start( |
1947 | 2028 | wallet: *const c_char, |
1948 | 2029 | mwcmqs_config: *const c_char, |
1949 | 2030 | ) -> *mut c_void { |
| 2031 | + install_panic_hook(); |
1950 | 2032 | let wallet_ptr = CStr::from_ptr(wallet); |
1951 | 2033 | let mwcmqs_config = CStr::from_ptr(mwcmqs_config); |
1952 | 2034 | let mwcmqs_config = mwcmqs_config.to_str().unwrap(); |
|
0 commit comments