From 933fb07f42a7555fb0910b5765975a2c3bbf97c8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 12 Feb 2019 02:56:17 +0000 Subject: [PATCH] Wrap all exceptions and panics to relay them over JNI --- src/main/rust/lib.rs | 320 ++++++++++++++++++++----------------------- src/main/rust/sql.rs | 4 +- 2 files changed, 154 insertions(+), 170 deletions(-) diff --git a/src/main/rust/lib.rs b/src/main/rust/lib.rs index c3a574b4..792e006b 100644 --- a/src/main/rust/lib.rs +++ b/src/main/rust/lib.rs @@ -7,13 +7,16 @@ mod utils; const SAPLING_CONSENSUS_BRANCH_ID: u32 = 0x76b8_09bb; use android_logger::Filter; +use failure::format_err; use jni::{ objects::{JClass, JString}, sys::{jboolean, jbyteArray, jint, jlong, jobjectArray, jstring, JNI_FALSE, JNI_TRUE}, JNIEnv, }; use log::Level; +use std::panic; use std::path::Path; +use std::ptr; use zcash_client_backend::{ constants::{HRP_SAPLING_EXTENDED_SPENDING_KEY_TEST, HRP_SAPLING_PAYMENT_ADDRESS_TEST}, encoding::{ @@ -26,9 +29,12 @@ use zcash_client_backend::{ use zcash_primitives::transaction::components::Amount; use zip32::ExtendedFullViewingKey; -use crate::sql::{ - get_address, get_balance, init_accounts_table, init_blocks_table, init_data_database, - scan_cached_blocks, send_to_address, +use crate::{ + sql::{ + get_address, get_balance, init_accounts_table, init_blocks_table, init_data_database, + scan_cached_blocks, send_to_address, + }, + utils::exception::unwrap_exc_or, }; #[no_mangle] @@ -52,15 +58,14 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_initDataDb( _: JClass<'_>, db_data: JString<'_>, ) -> jboolean { - let db_data = utils::java_string_to_rust(&env, db_data); + let res = panic::catch_unwind(|| { + let db_data = utils::java_string_to_rust(&env, db_data); - match init_data_database(&db_data) { - Ok(()) => JNI_TRUE, - Err(e) => { - error!("Error while initializing data DB: {}", e); - JNI_FALSE - } - } + init_data_database(&db_data) + .map(|()| JNI_TRUE) + .map_err(|e| format_err!("Error while initializing data DB: {}", e)) + }); + unwrap_exc_or(&env, res, JNI_FALSE) } #[no_mangle] @@ -71,11 +76,16 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_initAccountsTab seed: jbyteArray, accounts: jint, ) -> jobjectArray { - let db_data = utils::java_string_to_rust(&env, db_data); - let seed = env.convert_byte_array(seed).unwrap(); + let res = panic::catch_unwind(|| { + let db_data = utils::java_string_to_rust(&env, db_data); + let seed = env.convert_byte_array(seed).unwrap(); + let accounts = if accounts >= 0 { + accounts as u32 + } else { + return Err(format_err!("accounts argument must be positive")); + }; - let ret = if accounts >= 0 { - let extsks: Vec<_> = (0..accounts as u32) + let extsks: Vec<_> = (0..accounts) .map(|account| spending_key(&seed, 1, account)) .collect(); let extfvks: Vec<_> = extsks.iter().map(ExtendedFullViewingKey::from).collect(); @@ -83,32 +93,23 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_initAccountsTab match init_accounts_table(&db_data, &extfvks) { Ok(()) => { // Return the ExtendedSpendingKeys for the created accounts - extsks - } - Err(e) => { - error!("Error while initializing accounts: {}", e); - // Return an empty array to indicate an error - vec![] + Ok(utils::rust_vec_to_java( + &env, + extsks, + "java/lang/String", + |env, extsk| { + env.new_string(encode_extended_spending_key( + HRP_SAPLING_EXTENDED_SPENDING_KEY_TEST, + &extsk, + )) + }, + |env| env.new_string(""), + )) } + Err(e) => Err(format_err!("Error while initializing accounts: {}", e)), } - } else { - error!("accounts argument must be positive"); - // Return an empty array to indicate an error - vec![] - }; - - utils::rust_vec_to_java( - &env, - ret, - "java/lang/String", - |env, extsk| { - env.new_string(encode_extended_spending_key( - HRP_SAPLING_EXTENDED_SPENDING_KEY_TEST, - &extsk, - )) - }, - |env| env.new_string(""), - ) + }); + unwrap_exc_or(&env, res, ptr::null_mut()) } #[no_mangle] @@ -120,22 +121,21 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_initBlocksTable time: jlong, sapling_tree: jbyteArray, ) -> jboolean { - let db_data = utils::java_string_to_rust(&env, db_data); - let time = if time >= 0 && time <= jlong::from(u32::max_value()) { - time as u32 - } else { - error!("time argument must fit in a u32"); - return JNI_FALSE; - }; - let sapling_tree = env.convert_byte_array(sapling_tree).unwrap(); + let res = panic::catch_unwind(|| { + let db_data = utils::java_string_to_rust(&env, db_data); + let time = if time >= 0 && time <= jlong::from(u32::max_value()) { + time as u32 + } else { + return Err(format_err!("time argument must fit in a u32")); + }; + let sapling_tree = env.convert_byte_array(sapling_tree).unwrap(); - match init_blocks_table(&db_data, height, time, &sapling_tree) { - Ok(()) => JNI_TRUE, - Err(e) => { - error!("Error while initializing data DB: {}", e); - JNI_FALSE + match init_blocks_table(&db_data, height, time, &sapling_tree) { + Ok(()) => Ok(JNI_TRUE), + Err(e) => Err(format_err!("Error while initializing blocks table: {}", e)), } - } + }); + unwrap_exc_or(&env, res, JNI_FALSE) } #[no_mangle] @@ -145,26 +145,23 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_getAddress( db_data: JString<'_>, account: jint, ) -> jstring { - let db_data = utils::java_string_to_rust(&env, db_data); + let res = panic::catch_unwind(|| { + let db_data = utils::java_string_to_rust(&env, db_data); + let account = if account >= 0 { + account as u32 + } else { + return Err(format_err!("account argument must be positive")); + }; - let addr = match account { - acc if acc >= 0 => match get_address(&db_data, acc as u32) { - Ok(addr) => addr, - Err(e) => { - error!("Error while fetching address: {}", e); - // Return an empty string to indicate an error - String::default() + match get_address(&db_data, account) { + Ok(addr) => { + let output = env.new_string(addr).expect("Couldn't create Java string!"); + Ok(output.into_inner()) } - }, - _ => { - error!("account argument must be positive"); - // Return an empty string to indicate an error - String::default() + Err(e) => Err(format_err!("Error while fetching address: {}", e)), } - }; - - let output = env.new_string(addr).expect("Couldn't create Java string!"); - output.into_inner() + }); + unwrap_exc_or(&env, res, ptr::null_mut()) } #[no_mangle] @@ -174,21 +171,20 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_getBalance( db_data: JString<'_>, account: jint, ) -> jlong { - let db_data = utils::java_string_to_rust(&env, db_data); - let account = if account >= 0 { - account as u32 - } else { - error!("account argument must be positive"); - return -1; - }; + let res = panic::catch_unwind(|| { + let db_data = utils::java_string_to_rust(&env, db_data); + let account = if account >= 0 { + account as u32 + } else { + return Err(format_err!("account argument must be positive")); + }; - match get_balance(&db_data, account) { - Ok(balance) => balance.0, - Err(e) => { - error!("Error while fetching balance: {}", e); - -1 + match get_balance(&db_data, account) { + Ok(balance) => Ok(balance.0), + Err(e) => Err(format_err!("Error while fetching balance: {}", e)), } - } + }); + unwrap_exc_or(&env, res, -1) } #[no_mangle] @@ -198,19 +194,18 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_getReceivedMemo db_data: JString<'_>, id_note: jlong, ) -> jstring { - let db_data = utils::java_string_to_rust(&env, db_data); + let res = panic::catch_unwind(|| { + let db_data = utils::java_string_to_rust(&env, db_data); - let memo = match crate::sql::get_received_memo_as_utf8(db_data, id_note) { - Ok(memo) => memo.unwrap_or_default(), - Err(e) => { - error!("Error while fetching memo: {}", e); - // Return an empty string to indicate an error - String::default() - } - }; + let memo = match crate::sql::get_received_memo_as_utf8(db_data, id_note) { + Ok(memo) => memo.unwrap_or_default(), + Err(e) => return Err(format_err!("Error while fetching memo: {}", e)), + }; - let output = env.new_string(memo).expect("Couldn't create Java string!"); - output.into_inner() + let output = env.new_string(memo).expect("Couldn't create Java string!"); + Ok(output.into_inner()) + }); + unwrap_exc_or(&env, res, ptr::null_mut()) } #[no_mangle] @@ -220,19 +215,18 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_getSentMemoAsUt db_data: JString<'_>, id_note: jlong, ) -> jstring { - let db_data = utils::java_string_to_rust(&env, db_data); + let res = panic::catch_unwind(|| { + let db_data = utils::java_string_to_rust(&env, db_data); - let memo = match crate::sql::get_sent_memo_as_utf8(db_data, id_note) { - Ok(memo) => memo.unwrap_or_default(), - Err(e) => { - error!("Error while fetching memo: {}", e); - // Return an empty string to indicate an error - String::default() - } - }; + let memo = match crate::sql::get_sent_memo_as_utf8(db_data, id_note) { + Ok(memo) => memo.unwrap_or_default(), + Err(e) => return Err(format_err!("Error while fetching memo: {}", e)), + }; - let output = env.new_string(memo).expect("Couldn't create Java string!"); - output.into_inner() + let output = env.new_string(memo).expect("Couldn't create Java string!"); + Ok(output.into_inner()) + }); + unwrap_exc_or(&env, res, ptr::null_mut()) } #[no_mangle] @@ -242,16 +236,16 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_scanBlocks( db_cache: JString<'_>, db_data: JString<'_>, ) -> jboolean { - let db_cache = utils::java_string_to_rust(&env, db_cache); - let db_data = utils::java_string_to_rust(&env, db_data); + let res = panic::catch_unwind(|| { + let db_cache = utils::java_string_to_rust(&env, db_cache); + let db_data = utils::java_string_to_rust(&env, db_data); - match scan_cached_blocks(&db_cache, &db_data) { - Ok(()) => JNI_TRUE, - Err(e) => { - error!("Error while scanning blocks: {}", e); - JNI_FALSE + match scan_cached_blocks(&db_cache, &db_data) { + Ok(()) => Ok(JNI_TRUE), + Err(e) => Err(format_err!("Error while scanning blocks: {}", e)), } - } + }); + unwrap_exc_or(&env, res, JNI_FALSE) } #[no_mangle] @@ -267,64 +261,54 @@ pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_sendToAddress( spend_params: JString<'_>, output_params: JString<'_>, ) -> jlong { - let db_data = utils::java_string_to_rust(&env, db_data); - let account = if account >= 0 { - account as u32 - } else { - error!("account argument must be positive"); - return -1; - }; - let extsk = utils::java_string_to_rust(&env, extsk); - let to = utils::java_string_to_rust(&env, to); - let value = Amount(value); - let memo = utils::java_string_to_rust(&env, memo); - let spend_params = utils::java_string_to_rust(&env, spend_params); - let output_params = utils::java_string_to_rust(&env, output_params); + let res = panic::catch_unwind(|| { + let db_data = utils::java_string_to_rust(&env, db_data); + let account = if account >= 0 { + account as u32 + } else { + return Err(format_err!("account argument must be positive")); + }; + let extsk = utils::java_string_to_rust(&env, extsk); + let to = utils::java_string_to_rust(&env, to); + let value = Amount(value); + let memo = utils::java_string_to_rust(&env, memo); + let spend_params = utils::java_string_to_rust(&env, spend_params); + let output_params = utils::java_string_to_rust(&env, output_params); - let extsk = match decode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY_TEST, &extsk) { - Ok(extsk) => extsk, - Err(e) => { - error!("Invalid ExtendedSpendingKey: {}", e); - return -1; - } - }; + let extsk = + match decode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY_TEST, &extsk) { + Ok(extsk) => extsk, + Err(e) => { + return Err(format_err!("Invalid ExtendedSpendingKey: {}", e)); + } + }; - let to = match decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS_TEST, &to) { - Ok(to) => to, - Err(e) => { - error!("Invalid PaymentAddress: {}", e); - return -1; - } - }; + let to = match decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS_TEST, &to) { + Ok(to) => to, + Err(e) => { + return Err(format_err!("Invalid PaymentAddress: {}", e)); + } + }; - let memo = match Memo::from_str(&memo) { - Ok(memo) => Some(memo), - Err(e) => { - error!("{}", e); - return -1; - } - }; + let memo = Some(Memo::from_str(&memo)?); - let prover = LocalTxProver::new( - Path::new(&spend_params), - "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c", - Path::new(&output_params), - "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028", - ); + let prover = LocalTxProver::new( + Path::new(&spend_params), + "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c", + Path::new(&output_params), + "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028", + ); - match send_to_address( - &db_data, - SAPLING_CONSENSUS_BRANCH_ID, - prover, - (account, &extsk), - &to, - value, - memo, - ) { - Ok(tx_row) => tx_row, - Err(e) => { - error!("Error while sending funds: {}", e); - -1 - } - } + send_to_address( + &db_data, + SAPLING_CONSENSUS_BRANCH_ID, + prover, + (account, &extsk), + &to, + value, + memo, + ) + .map_err(|e| format_err!("Error while sending funds: {}", e)) + }); + unwrap_exc_or(&env, res, -1) } diff --git a/src/main/rust/sql.rs b/src/main/rust/sql.rs index c01dc983..9467380e 100644 --- a/src/main/rust/sql.rs +++ b/src/main/rust/sql.rs @@ -33,7 +33,7 @@ fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS_TEST, &addr) } -pub fn init_cache_database>(db_cache: P) -> rusqlite::Result<()> { +pub fn init_cache_database>(db_cache: P) -> Result<(), Error> { let cache = Connection::open(db_cache)?; cache.execute( "CREATE TABLE IF NOT EXISTS compactblocks ( @@ -45,7 +45,7 @@ pub fn init_cache_database>(db_cache: P) -> rusqlite::Result<()> Ok(()) } -pub fn init_data_database>(db_data: P) -> rusqlite::Result<()> { +pub fn init_data_database>(db_data: P) -> Result<(), Error> { let data = Connection::open(db_data)?; data.execute( "CREATE TABLE IF NOT EXISTS accounts (