From d9232c885db40bb85ef52c55fe79514288089e20 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 30 Jan 2019 01:41:50 +0000 Subject: [PATCH] JniConverter.initAccountsTable() --- .../cash/z/wallet/sdk/jni/JniConverter.kt | 5 + src/main/rust/lib.rs | 129 +++++++++++++++++- 2 files changed, 127 insertions(+), 7 deletions(-) diff --git a/src/main/java/cash/z/wallet/sdk/jni/JniConverter.kt b/src/main/java/cash/z/wallet/sdk/jni/JniConverter.kt index 73abd3fc..79f974e8 100644 --- a/src/main/java/cash/z/wallet/sdk/jni/JniConverter.kt +++ b/src/main/java/cash/z/wallet/sdk/jni/JniConverter.kt @@ -4,6 +4,11 @@ class JniConverter { external fun initDataDb(dbData: String): Boolean + external fun initAccountsTable( + dbData: String, + seed: ByteArray, + accounts: Int): Array + external fun initBlocksTable( dbData: String, height: Int, diff --git a/src/main/rust/lib.rs b/src/main/rust/lib.rs index 44d0d0b0..13822c78 100644 --- a/src/main/rust/lib.rs +++ b/src/main/rust/lib.rs @@ -22,9 +22,13 @@ use sapling_crypto::{ primitives::{Diversifier, Note, PaymentAddress}, }; use zcash_client_backend::{ - constants::HRP_SAPLING_PAYMENT_ADDRESS_TEST, encoding::encode_payment_address, - note_encryption::Memo, proto::compact_formats::CompactBlock, prover::TxProver, - transaction::Builder, welding_rig::scan_block, + constants::{HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY_TEST, HRP_SAPLING_PAYMENT_ADDRESS_TEST}, + encoding::{encode_extended_full_viewing_key, encode_payment_address}, + note_encryption::Memo, + proto::compact_formats::CompactBlock, + prover::TxProver, + transaction::Builder, + welding_rig::scan_block, }; use zcash_primitives::{ merkle_tree::{CommitmentTree, IncrementalWitness}, @@ -57,6 +61,14 @@ fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { fn init_data_database(db_data: &str) -> rusqlite::Result<()> { let data = Connection::open(db_data)?; + data.execute( + "CREATE TABLE IF NOT EXISTS accounts ( + account INTEGER PRIMARY KEY, + extfvk TEXT NOT NULL, + address TEXT NOT NULL + )", + NO_PARAMS, + )?; data.execute( "CREATE TABLE IF NOT EXISTS blocks ( height INTEGER PRIMARY KEY, @@ -90,6 +102,7 @@ fn init_data_database(db_data: &str) -> rusqlite::Result<()> { memo BLOB, spent INTEGER, FOREIGN KEY (tx) REFERENCES transactions(id_tx), + FOREIGN KEY (account) REFERENCES accounts(account), FOREIGN KEY (spent) REFERENCES transactions(id_tx), CONSTRAINT tx_output UNIQUE (tx, output_index) )", @@ -117,6 +130,7 @@ fn init_data_database(db_data: &str) -> rusqlite::Result<()> { value INTEGER NOT NULL, memo BLOB, FOREIGN KEY (tx) REFERENCES transactions(id_tx), + FOREIGN KEY (from_account) REFERENCES accounts(account), CONSTRAINT tx_output UNIQUE (tx, output_index) )", NO_PARAMS, @@ -124,6 +138,35 @@ fn init_data_database(db_data: &str) -> rusqlite::Result<()> { Ok(()) } +fn init_accounts_table(db_data: &str, extfvks: &[ExtendedFullViewingKey]) -> Result<(), Error> { + let data = Connection::open(db_data)?; + + let mut empty_check = data.prepare("SELECT * FROM accounts LIMIT 1")?; + if empty_check.exists(NO_PARAMS)? { + return Err(format_err!("accounts table is not empty")); + } + + // Insert accounts atomically + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + for (account, extfvk) in extfvks.iter().enumerate() { + let address = address_from_extfvk(extfvk); + let extfvk = + encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY_TEST, extfvk); + data.execute( + "INSERT INTO accounts (account, extfvk, address) + VALUES (?, ?, ?)", + &[ + (account as u32).to_sql()?, + extfvk.to_sql()?, + address.to_sql()?, + ], + )?; + } + data.execute("COMMIT", NO_PARAMS)?; + + Ok(()) +} + fn init_blocks_table( db_data: &str, height: i32, @@ -590,20 +633,25 @@ pub mod android { use std::path::Path; use zcash_client_backend::{ constants::{HRP_SAPLING_EXTENDED_SPENDING_KEY_TEST, HRP_SAPLING_PAYMENT_ADDRESS_TEST}, - encoding::{decode_extended_spending_key, decode_payment_address}, + encoding::{ + decode_extended_spending_key, decode_payment_address, encode_extended_spending_key, + }, note_encryption::Memo, prover::LocalTxProver, }; use zcash_primitives::transaction::components::Amount; + use zip32::{ChildIndex, ExtendedFullViewingKey, ExtendedSpendingKey}; use self::android_logger::Filter; use self::jni::objects::{JClass, JString}; - use self::jni::sys::{jboolean, jbyteArray, jint, jlong, jstring, JNI_FALSE, JNI_TRUE}; + use self::jni::sys::{ + jboolean, jbyteArray, jint, jlong, jobjectArray, jsize, jstring, JNI_FALSE, JNI_TRUE, + }; use self::jni::JNIEnv; use super::{ - address_from_extfvk, extfvk_from_seed, get_balance, init_blocks_table, init_data_database, - scan_cached_blocks, send_to_address, SAPLING_CONSENSUS_BRANCH_ID, + address_from_extfvk, extfvk_from_seed, get_balance, init_accounts_table, init_blocks_table, + init_data_database, scan_cached_blocks, send_to_address, SAPLING_CONSENSUS_BRANCH_ID, }; #[no_mangle] @@ -641,6 +689,73 @@ pub mod android { } } + #[no_mangle] + pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_initAccountsTable( + env: JNIEnv, + _: JClass, + db_data: JString, + seed: jbyteArray, + accounts: jint, + ) -> jobjectArray { + let db_data: String = env + .get_string(db_data) + .expect("Couldn't get Java string!") + .into(); + let seed = env.convert_byte_array(seed).unwrap(); + + let ret = if accounts >= 0 { + let master = ExtendedSpendingKey::master(&seed); + let extsks: Vec<_> = (0..accounts as u32) + .map(|account| { + ExtendedSpendingKey::from_path( + &master, + &[ + ChildIndex::Hardened(32), + ChildIndex::Hardened(1), + ChildIndex::Hardened(account), + ], + ) + }) + .collect(); + let extfvks: Vec<_> = extsks + .iter() + .map(|extsk| ExtendedFullViewingKey::from(extsk)) + .collect(); + + 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![] + } + } + } else { + error!("accounts argument must be positive"); + // Return an empty array to indicate an error + vec![] + }; + + let jempty = env.new_string("").expect("Couldn't create Java string!"); + let jret = env + .new_object_array(ret.len() as jsize, "java/lang/String", *jempty) + .expect("Couldn't create Java array!"); + for (i, extsk) in ret.into_iter().enumerate() { + let jextsk = env + .new_string(encode_extended_spending_key( + HRP_SAPLING_EXTENDED_SPENDING_KEY_TEST, + &extsk, + )) + .expect("Couldn't create Java string!"); + env.set_object_array_element(jret, i as jsize, *jextsk) + .expect("Couldn't set Java array element!"); + } + jret + } + #[no_mangle] pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_initBlocksTable( env: JNIEnv,