2018-11-28 09:19:43 -08:00
#[ macro_use ]
extern crate log ;
2019-02-11 07:44:49 -08:00
mod utils ;
2018-11-21 08:40:18 -08:00
2019-02-07 04:58:33 -08:00
use android_logger ::Filter ;
2019-02-11 18:56:17 -08:00
use failure ::format_err ;
2019-02-07 04:58:33 -08:00
use jni ::{
objects ::{ JClass , JString } ,
2019-02-11 08:20:11 -08:00
sys ::{ jboolean , jbyteArray , jint , jlong , jobjectArray , jstring , JNI_FALSE , JNI_TRUE } ,
2019-02-07 04:58:33 -08:00
JNIEnv ,
} ;
2019-02-07 04:56:04 -08:00
use log ::Level ;
2020-06-09 19:14:22 -07:00
use std ::convert ::TryFrom ;
2019-02-11 18:56:17 -08:00
use std ::panic ;
2019-02-07 04:56:04 -08:00
use std ::path ::Path ;
2019-02-11 18:56:17 -08:00
use std ::ptr ;
2019-02-07 04:56:04 -08:00
use zcash_client_backend ::{
2019-11-17 20:45:05 -08:00
encoding ::{
2020-07-27 11:39:43 -07:00
decode_extended_full_viewing_key , decode_extended_spending_key ,
encode_extended_full_viewing_key , encode_extended_spending_key , encode_payment_address ,
2019-11-17 20:45:05 -08:00
} ,
2019-04-10 10:39:06 -07:00
keys ::spending_key ,
2019-02-07 04:56:04 -08:00
} ;
2019-04-10 10:39:06 -07:00
use zcash_client_sqlite ::{
2019-05-24 08:06:19 -07:00
address ::RecipientAddress ,
2019-05-02 15:40:58 -07:00
chain ::{ rewind_to_height , validate_combined_chain } ,
2019-08-29 10:04:53 -07:00
error ::ErrorKind ,
init ::{ init_accounts_table , init_blocks_table , init_data_database } ,
query ::{
get_address , get_balance , get_received_memo_as_utf8 , get_sent_memo_as_utf8 ,
get_verified_balance ,
} ,
2020-03-12 21:41:17 -07:00
scan ::{ decrypt_and_store_transaction , scan_cached_blocks } ,
2020-07-28 21:34:36 -07:00
transact ::{ create_to_address , OvkPolicy } ,
2019-04-10 10:39:06 -07:00
} ;
2020-02-11 16:57:57 -08:00
2019-04-10 10:39:06 -07:00
use zcash_primitives ::{
2019-11-17 20:45:05 -08:00
block ::BlockHash ,
2019-12-13 07:58:20 -08:00
consensus ::BranchId ,
2019-11-17 20:45:05 -08:00
note_encryption ::Memo ,
2020-03-12 21:41:17 -07:00
transaction ::{ components ::Amount , Transaction } ,
2019-11-17 20:45:05 -08:00
zip32 ::{ ExtendedFullViewingKey , ExtendedSpendingKey } ,
2019-04-10 10:39:06 -07:00
} ;
use zcash_proofs ::prover ::LocalTxProver ;
2019-02-07 04:56:04 -08:00
2019-03-08 19:47:49 -08:00
use crate ::utils ::exception ::unwrap_exc_or ;
2019-02-07 04:56:04 -08:00
2020-06-09 19:14:22 -07:00
#[ cfg(feature = " mainnet " ) ]
use zcash_primitives ::consensus ::MainNetwork as Network ;
#[ cfg(not(feature = " mainnet " )) ]
use zcash_primitives ::consensus ::TestNetwork as Network ;
2019-04-10 10:43:24 -07:00
#[ cfg(feature = " mainnet " ) ]
2019-11-17 20:45:05 -08:00
use zcash_client_backend ::constants ::mainnet ::{
2020-02-12 05:39:30 -08:00
COIN_TYPE , HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY , HRP_SAPLING_EXTENDED_SPENDING_KEY ,
HRP_SAPLING_PAYMENT_ADDRESS ,
2019-11-17 20:45:05 -08:00
} ;
2019-04-10 10:43:24 -07:00
#[ cfg(not(feature = " mainnet " )) ]
2019-11-17 20:45:05 -08:00
use zcash_client_backend ::constants ::testnet ::{
2020-02-12 05:39:30 -08:00
COIN_TYPE , HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY , HRP_SAPLING_EXTENDED_SPENDING_KEY ,
HRP_SAPLING_PAYMENT_ADDRESS ,
2019-11-17 20:45:05 -08:00
} ;
2019-04-10 10:43:24 -07:00
2020-08-13 18:03:19 -07:00
// /////////////////////////////////////////////////////////////////////////////////////////////////
// Temporary Imports
mod local_rpc_types ;
2020-09-22 07:44:31 -07:00
use base58 ::ToBase58 ;
use jni ::errors ::Result as JniResult ;
use local_rpc_types ::{ TransactionDataList , TransparentTransaction , TransparentTransactionList } ;
2020-08-13 18:03:19 -07:00
use protobuf ::{ parse_from_bytes , Message } ;
2020-09-22 07:44:31 -07:00
use sha2 ::{ Digest , Sha256 } ;
use zcash_client_backend ::constants ::{ mainnet , testnet } ;
2020-08-13 18:03:19 -07:00
use zcash_primitives ::legacy ::{ Script , TransparentAddress } ;
use bs58 ::{ self , decode ::Error as Bs58Error } ;
use hdwallet ::{ ExtendedPrivKey , KeyIndex } ;
use secp256k1 ::{ PublicKey , Secp256k1 , SecretKey , SignOnly , VerifyOnly } ;
use zcash_client_backend ::constants ::mainnet ::B58_PUBKEY_ADDRESS_PREFIX ;
// use crate::extended_key::{key_index::KeyIndex, ExtendedPrivKey, ExtendedPubKey, KeySeed};
// /////////////////////////////////////////////////////////////////////////////////////////////////
2020-07-31 23:12:15 -07:00
#[ cfg(debug_assertions) ]
fn print_debug_state ( ) {
debug! ( " WARNING! Debugging enabled! This will likely slow things down 10X! " ) ;
}
#[ cfg(not(debug_assertions)) ]
fn print_debug_state ( ) {
debug! ( " Release enabled (congrats, this is NOT a debug build). " ) ;
}
2019-02-07 04:56:04 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initLogs (
2019-02-11 07:11:09 -08:00
_env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
) {
2019-02-07 04:56:04 -08:00
android_logger ::init_once (
2019-11-17 20:45:05 -08:00
Filter ::default ( ) . with_min_level ( Level ::Debug ) ,
2019-02-07 04:56:04 -08:00
Some ( " cash.z.rust.logs " ) ,
) ;
log_panics ::init ( ) ;
debug! ( " logs have been initialized successfully " ) ;
2020-07-31 23:12:15 -07:00
print_debug_state ( )
2019-02-07 04:56:04 -08:00
}
2018-11-28 09:19:43 -08:00
2019-02-07 04:56:04 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initDataDb (
2019-02-11 07:11:09 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
2019-02-07 04:56:04 -08:00
) -> jboolean {
2019-02-11 18:56:17 -08:00
let res = panic ::catch_unwind ( | | {
let db_data = utils ::java_string_to_rust ( & env , db_data ) ;
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 )
2019-02-07 04:56:04 -08:00
}
2018-11-28 09:19:43 -08:00
2019-02-07 04:56:04 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initAccountsTable (
2019-02-11 07:11:09 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
2019-02-07 04:56:04 -08:00
seed : jbyteArray ,
accounts : jint ,
) -> jobjectArray {
2019-02-11 18:56:17 -08:00
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 extsks : Vec < _ > = ( 0 .. accounts )
2019-12-18 20:30:19 -08:00
. map ( | account | spending_key ( & seed , COIN_TYPE , account ) )
2019-02-07 04:56:04 -08:00
. collect ( ) ;
2019-02-07 05:01:54 -08:00
let extfvks : Vec < _ > = extsks . iter ( ) . map ( ExtendedFullViewingKey ::from ) . collect ( ) ;
2019-02-07 04:56:04 -08:00
match init_accounts_table ( & db_data , & extfvks ) {
Ok ( ( ) ) = > {
// Return the ExtendedSpendingKeys for the created accounts
2019-02-11 18:56:17 -08:00
Ok ( utils ::rust_vec_to_java (
& env ,
extsks ,
" java/lang/String " ,
| env , extsk | {
env . new_string ( encode_extended_spending_key (
2019-04-10 10:39:06 -07:00
HRP_SAPLING_EXTENDED_SPENDING_KEY ,
2019-02-11 18:56:17 -08:00
& extsk ,
) )
} ,
| env | env . new_string ( " " ) ,
) )
2019-01-28 05:05:12 -08:00
}
2019-02-11 18:56:17 -08:00
Err ( e ) = > Err ( format_err! ( " Error while initializing accounts: {} " , e ) ) ,
2019-01-28 05:05:12 -08:00
}
2019-02-11 18:56:17 -08:00
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
2019-02-07 04:56:04 -08:00
}
2019-01-29 17:41:50 -08:00
2019-11-17 20:45:05 -08:00
#[ no_mangle ]
2020-09-11 00:23:08 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initAccountsTableWithKeys (
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
extfvks_arr : jobjectArray ,
) -> jboolean {
let res = panic ::catch_unwind ( | | {
let db_data = utils ::java_string_to_rust ( & env , db_data ) ;
// TODO: avoid all this unwrapping and also surface erros, better
let count = env . get_array_length ( extfvks_arr ) . unwrap ( ) ;
let extfvks = ( 0 .. count )
. map ( | i | env . get_object_array_element ( extfvks_arr , i ) )
. map ( | jstr | utils ::java_string_to_rust ( & env , jstr . unwrap ( ) . into ( ) ) )
2020-09-22 07:44:31 -07:00
. map ( | vkstr | {
decode_extended_full_viewing_key ( HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY , & vkstr )
. unwrap ( )
. unwrap ( )
} )
2020-09-11 00:23:08 -07:00
. collect ::< Vec < _ > > ( ) ;
match init_accounts_table ( & db_data , & extfvks ) {
Ok ( ( ) ) = > Ok ( JNI_TRUE ) ,
Err ( e ) = > Err ( format_err! ( " Error while initializing accounts: {} " , e ) ) ,
}
} ) ;
unwrap_exc_or ( & env , res , JNI_FALSE )
}
2020-09-11 00:33:25 -07:00
#[ no_mangle ]
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveExtendedSpendingKeys (
2019-11-17 20:45:05 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
seed : jbyteArray ,
accounts : jint ,
) -> jobjectArray {
let res = panic ::catch_unwind ( | | {
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 greater than zero " ) ) ;
} ;
let extsks : Vec < _ > = ( 0 .. accounts )
2019-12-18 20:30:19 -08:00
. map ( | account | spending_key ( & seed , COIN_TYPE , account ) )
2019-11-17 20:45:05 -08:00
. collect ( ) ;
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 ,
& extsk ,
) )
} ,
| env | env . new_string ( " " ) ,
) )
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
}
#[ no_mangle ]
2020-09-11 00:33:25 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveExtendedFullViewingKeys (
2019-11-17 20:45:05 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
seed : jbyteArray ,
accounts : jint ,
) -> jobjectArray {
let res = panic ::catch_unwind ( | | {
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 greater than zero " ) ) ;
} ;
let extfvks : Vec < _ > = ( 0 .. accounts )
2019-12-18 20:30:19 -08:00
. map ( | account | ExtendedFullViewingKey ::from ( & spending_key ( & seed , COIN_TYPE , account ) ) )
2019-11-17 20:45:05 -08:00
. collect ( ) ;
Ok ( utils ::rust_vec_to_java (
& env ,
extfvks ,
" java/lang/String " ,
| env , extfvk | {
env . new_string ( encode_extended_full_viewing_key (
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY ,
& extfvk ,
) )
} ,
| env | env . new_string ( " " ) ,
) )
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
}
2020-02-11 16:57:57 -08:00
#[ no_mangle ]
2020-09-11 00:33:25 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveShieldedAddressFromSeed (
2020-02-11 16:57:57 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
seed : jbyteArray ,
account_index : jint ,
) -> jstring {
let res = panic ::catch_unwind ( | | {
let seed = env . convert_byte_array ( seed ) . unwrap ( ) ;
let account_index = if account_index > = 0 {
account_index as u32
} else {
return Err ( format_err! ( " accountIndex argument must be positive " ) ) ;
} ;
2020-02-12 05:39:30 -08:00
let address = spending_key ( & seed , COIN_TYPE , account_index )
. default_address ( )
. unwrap ( )
. 1 ;
let address_str = encode_payment_address ( HRP_SAPLING_PAYMENT_ADDRESS , & address ) ;
let output = env
. new_string ( address_str )
. expect ( " Couldn't create Java string! " ) ;
2020-02-11 16:57:57 -08:00
Ok ( output . into_inner ( ) )
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
}
#[ no_mangle ]
2020-09-11 00:33:25 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveShieldedAddressFromViewingKey (
2020-02-11 16:57:57 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
extfvk_string : JString < '_ > ,
) -> jstring {
let res = panic ::catch_unwind ( | | {
let extfvk_string = utils ::java_string_to_rust ( & env , extfvk_string ) ;
let extfvk = match decode_extended_full_viewing_key (
2020-07-27 11:39:43 -07:00
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY ,
2020-02-11 16:57:57 -08:00
& extfvk_string ,
) {
Ok ( Some ( extfvk ) ) = > extfvk ,
Ok ( None ) = > {
2020-07-27 11:39:43 -07:00
return Err ( format_err! ( " Failed to parse viewing key string in order to derive the address. Deriving a viewing key from the string returned no results. Encoding was valid but type was incorrect. " ) ) ;
2020-02-11 16:57:57 -08:00
}
Err ( e ) = > {
return Err ( format_err! (
" Error while deriving viewing key from string input: {} " ,
e
) ) ;
}
} ;
2020-02-12 05:39:30 -08:00
let address = extfvk . default_address ( ) . unwrap ( ) . 1 ;
let address_str = encode_payment_address ( HRP_SAPLING_PAYMENT_ADDRESS , & address ) ;
let output = env
. new_string ( address_str )
. expect ( " Couldn't create Java string! " ) ;
2020-02-11 16:57:57 -08:00
Ok ( output . into_inner ( ) )
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
}
2019-11-17 20:45:05 -08:00
#[ no_mangle ]
2020-09-11 00:33:25 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveExtendedFullViewingKey (
2019-11-17 20:45:05 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
extsk_string : JString < '_ > ,
) -> jobjectArray {
let res = panic ::catch_unwind ( | | {
let extsk_string = utils ::java_string_to_rust ( & env , extsk_string ) ;
let extfvk = match decode_extended_spending_key (
HRP_SAPLING_EXTENDED_SPENDING_KEY ,
& extsk_string ,
) {
Ok ( Some ( extsk ) ) = > ExtendedFullViewingKey ::from ( & extsk ) ,
Ok ( None ) = > {
return Err ( format_err! ( " Deriving viewing key from spending key returned no results. Encoding was valid but type was incorrect. " ) ) ;
}
Err ( e ) = > {
return Err ( format_err! (
" Error while deriving viewing key from spending key: {} " ,
e
) ) ;
}
} ;
let output = env
. new_string ( encode_extended_full_viewing_key (
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY ,
& extfvk ,
) )
. expect ( " Couldn't create Java string! " ) ;
Ok ( output . into_inner ( ) )
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
}
2019-02-07 04:56:04 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initBlocksTable (
2019-02-11 07:11:09 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
2019-02-07 04:56:04 -08:00
height : jint ,
2019-05-02 15:40:58 -07:00
hash_string : JString < '_ > ,
2019-02-07 04:56:04 -08:00
time : jlong ,
2019-02-22 06:52:03 -08:00
sapling_tree_string : JString < '_ > ,
2019-02-07 04:56:04 -08:00
) -> jboolean {
2019-02-11 18:56:17 -08:00
let res = panic ::catch_unwind ( | | {
let db_data = utils ::java_string_to_rust ( & env , db_data ) ;
2019-05-02 15:40:58 -07:00
let hash = {
let mut hash = hex ::decode ( utils ::java_string_to_rust ( & env , hash_string ) ) . unwrap ( ) ;
hash . reverse ( ) ;
BlockHash ::from_slice ( & hash )
} ;
2019-02-11 18:56:17 -08:00
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 " ) ) ;
} ;
2019-02-22 06:52:03 -08:00
let sapling_tree =
hex ::decode ( utils ::java_string_to_rust ( & env , sapling_tree_string ) ) . unwrap ( ) ;
2019-02-11 18:56:17 -08:00
2019-11-17 20:45:05 -08:00
debug! ( " initializing blocks table with height {} " , height ) ;
2019-05-02 15:40:58 -07:00
match init_blocks_table ( & db_data , height , hash , time , & sapling_tree ) {
2019-02-11 18:56:17 -08:00
Ok ( ( ) ) = > Ok ( JNI_TRUE ) ,
Err ( e ) = > Err ( format_err! ( " Error while initializing blocks table: {} " , e ) ) ,
2019-01-14 16:31:26 -08:00
}
2019-02-11 18:56:17 -08:00
} ) ;
unwrap_exc_or ( & env , res , JNI_FALSE )
2019-02-07 04:56:04 -08:00
}
2019-01-14 16:31:26 -08:00
2019-02-07 04:56:04 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getAddress (
2019-02-11 07:11:09 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
2019-02-07 04:56:04 -08:00
account : jint ,
) -> jstring {
2019-02-11 18:56:17 -08:00
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_address ( & db_data , account ) {
Ok ( addr ) = > {
let output = env . new_string ( addr ) . expect ( " Couldn't create Java string! " ) ;
Ok ( output . into_inner ( ) )
2019-01-29 18:20:53 -08:00
}
2019-02-11 18:56:17 -08:00
Err ( e ) = > Err ( format_err! ( " Error while fetching address: {} " , e ) ) ,
2019-02-07 04:56:04 -08:00
}
2019-02-11 18:56:17 -08:00
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
2019-02-07 04:56:04 -08:00
}
2018-11-22 05:54:25 -08:00
2019-06-04 07:15:19 -07:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidShieldedAddress (
2019-06-04 07:15:19 -07:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
addr : JString < '_ > ,
) -> jboolean {
let res = panic ::catch_unwind ( | | {
let addr = utils ::java_string_to_rust ( & env , addr ) ;
match RecipientAddress ::from_str ( & addr ) {
2019-07-10 11:55:38 -07:00
Some ( addr ) = > match addr {
2019-06-04 07:15:19 -07:00
RecipientAddress ::Shielded ( _ ) = > Ok ( JNI_TRUE ) ,
RecipientAddress ::Transparent ( _ ) = > Ok ( JNI_FALSE ) ,
2019-08-29 10:04:53 -07:00
} ,
None = > Err ( format_err! ( " Address is for the wrong network " ) ) ,
2019-06-04 07:15:19 -07:00
}
} ) ;
unwrap_exc_or ( & env , res , JNI_FALSE )
}
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidTransparentAddress (
2019-06-04 07:15:19 -07:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
addr : JString < '_ > ,
) -> jboolean {
let res = panic ::catch_unwind ( | | {
let addr = utils ::java_string_to_rust ( & env , addr ) ;
match RecipientAddress ::from_str ( & addr ) {
2019-07-10 11:55:38 -07:00
Some ( addr ) = > match addr {
2019-06-04 07:15:19 -07:00
RecipientAddress ::Shielded ( _ ) = > Ok ( JNI_FALSE ) ,
RecipientAddress ::Transparent ( _ ) = > Ok ( JNI_TRUE ) ,
2019-08-29 10:04:53 -07:00
} ,
None = > Err ( format_err! ( " Address is for the wrong network " ) ) ,
2019-06-04 07:15:19 -07:00
}
} ) ;
unwrap_exc_or ( & env , res , JNI_FALSE )
}
2019-02-07 04:56:04 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getBalance (
2019-02-11 07:11:09 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
2019-02-07 04:56:04 -08:00
account : jint ,
) -> jlong {
2019-02-11 18:56:17 -08:00
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 ) {
2019-08-29 10:04:53 -07:00
Ok ( balance ) = > Ok ( balance . into ( ) ) ,
2019-02-11 18:56:17 -08:00
Err ( e ) = > Err ( format_err! ( " Error while fetching balance: {} " , e ) ) ,
2018-12-04 08:46:24 -08:00
}
2019-02-11 18:56:17 -08:00
} ) ;
unwrap_exc_or ( & env , res , - 1 )
2019-02-07 04:56:04 -08:00
}
2018-12-04 08:46:24 -08:00
2019-02-18 17:45:38 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getVerifiedBalance (
2019-02-18 17:45:38 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
account : jint ,
) -> jlong {
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_verified_balance ( & db_data , account ) {
2019-08-29 10:04:53 -07:00
Ok ( balance ) = > Ok ( balance . into ( ) ) ,
2019-02-18 17:45:38 -08:00
Err ( e ) = > Err ( format_err! ( " Error while fetching verified balance: {} " , e ) ) ,
}
} ) ;
unwrap_exc_or ( & env , res , - 1 )
}
2019-02-08 07:23:21 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getReceivedMemoAsUtf8 (
2019-02-11 07:11:09 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
2019-02-08 07:23:21 -08:00
id_note : jlong ,
) -> jstring {
2019-02-11 18:56:17 -08:00
let res = panic ::catch_unwind ( | | {
let db_data = utils ::java_string_to_rust ( & env , db_data ) ;
2019-03-08 19:47:49 -08:00
let memo = match get_received_memo_as_utf8 ( db_data , id_note ) {
2019-02-11 18:56:17 -08:00
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! " ) ;
Ok ( output . into_inner ( ) )
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
2019-02-08 07:23:21 -08:00
}
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getSentMemoAsUtf8 (
2019-02-11 07:11:09 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
2019-02-08 07:23:21 -08:00
id_note : jlong ,
) -> jstring {
2019-02-11 18:56:17 -08:00
let res = panic ::catch_unwind ( | | {
let db_data = utils ::java_string_to_rust ( & env , db_data ) ;
2019-03-08 19:47:49 -08:00
let memo = match get_sent_memo_as_utf8 ( db_data , id_note ) {
2019-02-11 18:56:17 -08:00
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! " ) ;
Ok ( output . into_inner ( ) )
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
2019-02-08 07:23:21 -08:00
}
2019-05-02 15:40:58 -07:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_validateCombinedChain (
2019-05-02 15:40:58 -07:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_cache : JString < '_ > ,
db_data : JString < '_ > ,
) -> jint {
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 ) ;
if let Err ( e ) = validate_combined_chain ( & db_cache , & db_data ) {
match e . kind ( ) {
ErrorKind ::InvalidChain ( upper_bound , _ ) = > Ok ( * upper_bound ) ,
_ = > Err ( format_err! ( " Error while validating chain: {} " , e ) ) ,
}
} else {
// All blocks are valid, so "highest invalid block height" is below genesis.
Ok ( - 1 )
}
} ) ;
unwrap_exc_or ( & env , res , 0 )
}
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_rewindToHeight (
2019-05-02 15:40:58 -07:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
height : jint ,
) -> jboolean {
let res = panic ::catch_unwind ( | | {
let db_data = utils ::java_string_to_rust ( & env , db_data ) ;
match rewind_to_height ( & db_data , height ) {
Ok ( ( ) ) = > Ok ( JNI_TRUE ) ,
Err ( e ) = > Err ( format_err! (
" Error while rewinding data DB to height {}: {} " ,
height ,
e
) ) ,
}
} ) ;
unwrap_exc_or ( & env , res , JNI_FALSE )
}
2019-02-07 04:56:04 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_scanBlocks (
2019-02-11 07:11:09 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_cache : JString < '_ > ,
db_data : JString < '_ > ,
2019-02-07 04:56:04 -08:00
) -> jboolean {
2019-02-11 18:56:17 -08:00
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 ) ;
2020-02-11 16:57:57 -08:00
match scan_cached_blocks ( & db_cache , & db_data , None ) {
2019-02-11 18:56:17 -08:00
Ok ( ( ) ) = > Ok ( JNI_TRUE ) ,
Err ( e ) = > Err ( format_err! ( " Error while scanning blocks: {} " , e ) ) ,
2018-11-22 05:54:25 -08:00
}
2019-02-11 18:56:17 -08:00
} ) ;
unwrap_exc_or ( & env , res , JNI_FALSE )
2019-02-07 04:56:04 -08:00
}
2018-12-04 08:36:47 -08:00
2020-01-14 09:56:03 -08:00
// ADDED BY ANDROID
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_scanBlockBatch (
2020-01-14 09:56:03 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_cache : JString < '_ > ,
db_data : JString < '_ > ,
2020-02-12 05:39:30 -08:00
limit : jint ,
2020-01-14 09:56:03 -08:00
) -> jboolean {
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 ) ;
2020-02-11 16:57:57 -08:00
match scan_cached_blocks ( & db_cache , & db_data , Some ( limit ) ) {
2020-01-14 09:56:03 -08:00
Ok ( ( ) ) = > Ok ( JNI_TRUE ) ,
Err ( e ) = > Err ( format_err! ( " Error while scanning blocks: {} " , e ) ) ,
}
} ) ;
unwrap_exc_or ( & env , res , JNI_FALSE )
}
2020-08-13 18:03:19 -07:00
// ////////////////////////////////////////////////////////////////////////////////////////////////
// PROOF-OF-CONCEPT FOR PROTOBUF COMMUNICATION WITH SDK
// ////////////////////////////////////////////////////////////////////////////////////////////////
#[ no_mangle ]
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_parseTransactionDataList (
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
tx_data_list : jbyteArray ,
) -> jbyteArray {
let mut err_val : Vec < u8 > = Vec ::new ( ) ;
let mut res_err = env . byte_array_from_slice ( & err_val ) . unwrap ( ) ;
let res = panic ::catch_unwind ( | | {
let tx_data_bytes = env . convert_byte_array ( tx_data_list ) ? ;
let input_tx_data = parse_from_bytes ::< TransactionDataList > ( & tx_data_bytes ) ? ;
let mut tx_list = TransparentTransactionList ::new ( ) ;
let mut txs = protobuf ::RepeatedField ::< TransparentTransaction > ::new ( ) ;
for data in input_tx_data . data . iter ( ) {
let mut tx = TransparentTransaction ::new ( ) ;
let parsed = Transaction ::read ( & data [ .. ] ) ? ;
tx . set_expiryHeight ( parsed . expiry_height ) ;
// Note: the wrong value is returned here (negative numbers)
tx . set_value ( i64 ::from ( parsed . value_balance ) ) ;
tx . set_hasShieldedSpends ( parsed . shielded_spends . len ( ) > 0 ) ;
tx . set_hasShieldedOutputs ( parsed . shielded_outputs . len ( ) > 0 ) ;
for ( n , vout ) in parsed . vout . iter ( ) . enumerate ( ) {
match vout . script_pubkey . address ( ) {
// NOTE : this logic below doesn't work. No address is parsed.
Some ( TransparentAddress ::PublicKey ( hash ) ) = > {
tx . set_toAddress ( hash . to_base58check ( & B58_PUBKEY_ADDRESS_PREFIX , & [ ] ) ) ;
2020-09-22 07:44:31 -07:00
}
2020-08-13 18:03:19 -07:00
_ = > { }
}
}
txs . push ( tx ) ;
}
tx_list . set_transactions ( txs ) ;
match env . byte_array_from_slice ( & tx_list . write_to_bytes ( ) ? ) {
Ok ( result ) = > Ok ( result ) ,
Err ( e ) = > Err ( format_err! ( " Error while parsing transaction: {} " , e ) ) ,
}
} ) ;
unwrap_exc_or ( & env , res , res_err )
}
#[ no_mangle ]
2020-09-11 00:33:25 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveTransparentAddressFromSeed (
2020-08-13 18:03:19 -07:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
seed : jbyteArray ,
) -> jstring {
let res = panic ::catch_unwind ( | | {
let seed = env . convert_byte_array ( seed ) . unwrap ( ) ;
// modified from: https://github.com/adityapk00/zecwallet-light-cli/blob/master/lib/src/lightwallet.rs
let ext_t_key = ExtendedPrivKey ::with_seed ( & seed ) . unwrap ( ) ;
let address_sk = ext_t_key
2020-09-22 07:44:31 -07:00
. derive_private_key ( KeyIndex ::hardened_from_normalize_index ( 44 ) . unwrap ( ) )
. unwrap ( )
. derive_private_key ( KeyIndex ::hardened_from_normalize_index ( COIN_TYPE ) . unwrap ( ) )
. unwrap ( )
. derive_private_key ( KeyIndex ::hardened_from_normalize_index ( 0 ) . unwrap ( ) )
. unwrap ( )
. derive_private_key ( KeyIndex ::Normal ( 0 ) )
. unwrap ( )
. derive_private_key ( KeyIndex ::Normal ( 0 ) )
. unwrap ( )
2020-08-13 18:03:19 -07:00
. private_key ;
let secp = Secp256k1 ::new ( ) ;
let pk = PublicKey ::from_secret_key ( & secp , & address_sk ) ;
let mut hash160 = ripemd160 ::Ripemd160 ::new ( ) ;
hash160 . input ( Sha256 ::digest ( & pk . serialize ( ) [ .. ] . to_vec ( ) ) ) ;
2020-09-22 07:44:31 -07:00
let address_string = hash160
. result ( )
. to_base58check ( & B58_PUBKEY_ADDRESS_PREFIX , & [ ] ) ;
2020-08-13 18:03:19 -07:00
2020-09-22 07:44:31 -07:00
let output = env
. new_string ( address_string )
. expect ( " Couldn't create Java string! " ) ;
2020-08-13 18:03:19 -07:00
Ok ( output . into_inner ( ) )
} ) ;
unwrap_exc_or ( & env , res , ptr ::null_mut ( ) )
}
2020-03-12 21:41:17 -07:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_decryptAndStoreTransaction (
2020-03-12 21:41:17 -07:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
tx : jbyteArray ,
) -> jboolean {
let res = panic ::catch_unwind ( | | {
let db_data = utils ::java_string_to_rust ( & env , db_data ) ;
let tx_bytes = env . convert_byte_array ( tx ) . unwrap ( ) ;
let tx = Transaction ::read ( & tx_bytes [ .. ] ) ? ;
match decrypt_and_store_transaction ( & db_data , & tx ) {
Ok ( ( ) ) = > Ok ( JNI_TRUE ) ,
Err ( e ) = > Err ( format_err! ( " Error while decrypting transaction: {} " , e ) ) ,
}
} ) ;
unwrap_exc_or ( & env , res , JNI_FALSE )
}
2019-02-07 04:56:04 -08:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_createToAddress (
2019-02-11 07:11:09 -08:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
db_data : JString < '_ > ,
2020-06-09 19:14:22 -07:00
consensus_branch_id : jlong ,
2019-02-07 04:56:04 -08:00
account : jint ,
2019-02-11 07:11:09 -08:00
extsk : JString < '_ > ,
to : JString < '_ > ,
2019-02-07 04:56:04 -08:00
value : jlong ,
2019-11-12 08:58:15 -08:00
memo : jbyteArray ,
2019-02-11 07:11:09 -08:00
spend_params : JString < '_ > ,
output_params : JString < '_ > ,
2019-02-07 04:56:04 -08:00
) -> jlong {
2019-02-11 18:56:17 -08:00
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 ) ;
2019-11-17 20:45:05 -08:00
let value =
Amount ::from_i64 ( value ) . map_err ( | ( ) | format_err! ( " Invalid amount, out of range " ) ) ? ;
2019-08-29 10:57:05 -07:00
if value . is_negative ( ) {
2019-08-30 10:05:02 -07:00
return Err ( format_err! ( " Amount is negative " ) ) ;
2019-08-29 10:57:05 -07:00
}
2019-11-12 08:58:15 -08:00
let memo_bytes = env . convert_byte_array ( memo ) . unwrap ( ) ;
2019-02-11 18:56:17 -08:00
let spend_params = utils ::java_string_to_rust ( & env , spend_params ) ;
let output_params = utils ::java_string_to_rust ( & env , output_params ) ;
2019-04-10 10:39:06 -07:00
let extsk = match decode_extended_spending_key ( HRP_SAPLING_EXTENDED_SPENDING_KEY , & extsk ) {
Ok ( Some ( extsk ) ) = > extsk ,
Ok ( None ) = > {
return Err ( format_err! ( " ExtendedSpendingKey is for the wrong network " ) ) ;
}
Err ( e ) = > {
return Err ( format_err! ( " Invalid ExtendedSpendingKey: {} " , e ) ) ;
}
} ;
2019-05-24 08:06:19 -07:00
let to = match RecipientAddress ::from_str ( & to ) {
2019-07-10 11:55:38 -07:00
Some ( to ) = > to ,
None = > {
2019-05-24 08:06:19 -07:00
return Err ( format_err! ( " Address is for the wrong network " ) ) ;
2019-03-08 19:47:49 -08:00
}
2019-02-11 18:56:17 -08:00
} ;
2019-11-12 08:58:15 -08:00
let memo = Memo ::from_bytes ( & memo_bytes ) ;
2019-02-11 18:56:17 -08:00
2019-04-10 10:39:06 -07:00
let prover = LocalTxProver ::new ( Path ::new ( & spend_params ) , Path ::new ( & output_params ) ) ;
2019-02-11 18:56:17 -08:00
2020-06-09 19:14:22 -07:00
let branch_id = match BranchId ::try_from ( consensus_branch_id as u32 ) {
Ok ( branch ) = > branch ,
Err ( e ) = > {
return Err ( format_err! ( " Invalid consensus branch id: {} " , e ) ) ;
}
} ;
// let branch = if
2019-08-29 10:04:53 -07:00
create_to_address (
2019-02-11 18:56:17 -08:00
& db_data ,
2020-06-09 19:14:22 -07:00
branch_id ,
2019-02-11 18:56:17 -08:00
prover ,
( account , & extsk ) ,
& to ,
value ,
memo ,
2020-07-28 21:34:36 -07:00
OvkPolicy ::Sender ,
2019-02-11 18:56:17 -08:00
)
2019-12-13 07:58:20 -08:00
. map_err ( | e | format_err! ( " Error while creating transaction: {} " , e ) )
2019-02-11 18:56:17 -08:00
} ) ;
unwrap_exc_or ( & env , res , - 1 )
2018-11-20 09:59:08 -08:00
}
2020-06-09 19:14:22 -07:00
#[ no_mangle ]
2020-06-10 00:08:19 -07:00
pub unsafe extern " C " fn Java_cash_z_ecc_android_sdk_jni_RustBackend_branchIdForHeight (
2020-06-09 19:14:22 -07:00
env : JNIEnv < '_ > ,
_ : JClass < '_ > ,
height : jint ,
) -> jint {
let res = panic ::catch_unwind ( | | {
let branch : BranchId = BranchId ::for_height ::< Network > ( height as u32 ) ;
2020-09-22 07:44:31 -07:00
let branch_id : u32 = u32 ::from ( branch ) ;
2020-06-09 19:14:22 -07:00
debug! ( " For height {} found consensus branch {:?} " , height , branch ) ;
Ok ( branch_id as i32 )
} ) ;
unwrap_exc_or ( & env , res , - 1 )
2020-06-09 20:28:21 -07:00
}
2020-08-13 18:03:19 -07:00
//
// Helper code from: https://github.com/adityapk00/zecwallet-light-cli/blob/master/lib/src/lightwallet.rs
//
/// A trait for converting a [u8] to base58 encoded string.
pub trait ToBase58Check {
/// Converts a value of `self` to a base58 value, returning the owned string.
/// The version is a coin-specific prefix that is added.
/// The suffix is any bytes that we want to add at the end (like the "iscompressed" flag for
/// Secret key encoding)
fn to_base58check ( & self , version : & [ u8 ] , suffix : & [ u8 ] ) -> String ;
}
impl ToBase58Check for [ u8 ] {
fn to_base58check ( & self , version : & [ u8 ] , suffix : & [ u8 ] ) -> String {
let mut payload : Vec < u8 > = Vec ::new ( ) ;
payload . extend_from_slice ( version ) ;
payload . extend_from_slice ( self ) ;
payload . extend_from_slice ( suffix ) ;
let mut checksum = double_sha256 ( & payload ) ;
payload . append ( & mut checksum [ .. 4 ] . to_vec ( ) ) ;
payload . to_base58 ( )
}
}
pub fn double_sha256 ( payload : & [ u8 ] ) -> Vec < u8 > {
let h1 = Sha256 ::digest ( & payload ) ;
let h2 = Sha256 ::digest ( & h1 ) ;
h2 . to_vec ( )
2020-09-22 07:44:31 -07:00
}