2018-11-28 09:19:43 -08:00
|
|
|
#[macro_use]
|
|
|
|
extern crate log;
|
|
|
|
|
2018-11-22 05:52:54 -08:00
|
|
|
extern crate protobuf;
|
2018-11-22 05:54:25 -08:00
|
|
|
extern crate rusqlite;
|
2018-11-21 08:40:18 -08:00
|
|
|
extern crate zcash_client_backend;
|
|
|
|
extern crate zip32;
|
2018-11-20 09:59:08 -08:00
|
|
|
|
2018-11-22 05:52:54 -08:00
|
|
|
mod protos;
|
|
|
|
|
2018-11-22 05:54:25 -08:00
|
|
|
use rusqlite::Connection;
|
2018-11-21 08:40:18 -08:00
|
|
|
use zcash_client_backend::{
|
|
|
|
address::encode_payment_address, constants::HRP_SAPLING_EXTENDED_SPENDING_KEY_TEST,
|
2018-11-22 05:54:25 -08:00
|
|
|
welding_rig::scan_block_from_bytes,
|
2018-11-21 08:40:18 -08:00
|
|
|
};
|
2018-11-21 09:29:30 -08:00
|
|
|
use zip32::{ChildIndex, ExtendedFullViewingKey, ExtendedSpendingKey};
|
2018-11-21 08:40:18 -08:00
|
|
|
|
2018-11-22 05:54:25 -08:00
|
|
|
use protos::ValueReceived::ValueReceived;
|
|
|
|
|
2018-11-21 09:29:30 -08:00
|
|
|
fn extfvk_from_seed(seed: &[u8]) -> ExtendedFullViewingKey {
|
2018-11-21 08:40:18 -08:00
|
|
|
let master = ExtendedSpendingKey::master(seed);
|
|
|
|
let extsk = ExtendedSpendingKey::from_path(
|
|
|
|
&master,
|
|
|
|
&[
|
|
|
|
ChildIndex::Hardened(32),
|
|
|
|
ChildIndex::Hardened(1),
|
|
|
|
ChildIndex::Hardened(0),
|
|
|
|
],
|
|
|
|
);
|
2018-11-21 09:29:30 -08:00
|
|
|
ExtendedFullViewingKey::from(&extsk)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String {
|
|
|
|
let addr = extfvk.default_address().unwrap().1;
|
2018-11-21 08:40:18 -08:00
|
|
|
encode_payment_address(HRP_SAPLING_EXTENDED_SPENDING_KEY_TEST, &addr)
|
2018-11-20 09:59:08 -08:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:54:25 -08:00
|
|
|
struct CompactBlockRow {
|
|
|
|
height: i32,
|
|
|
|
data: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Scans the given block range for any transactions received by the given
|
|
|
|
/// ExtendedFullViewingKeys. Returns a Vec of block height, txid and value.
|
|
|
|
fn scan_cached_blocks(
|
|
|
|
db: String,
|
|
|
|
start: i32,
|
|
|
|
end: i32,
|
|
|
|
extfvks: &[ExtendedFullViewingKey],
|
|
|
|
) -> Vec<ValueReceived> {
|
|
|
|
let conn = Connection::open(db).unwrap();
|
|
|
|
let mut stmt = conn
|
|
|
|
.prepare("SELECT height, data FROM compactblocks WHERE height >= ? AND height <= ?")
|
|
|
|
.unwrap();
|
|
|
|
let rows = stmt
|
|
|
|
.query_map(&[start, end], |row| CompactBlockRow {
|
|
|
|
height: row.get(0),
|
|
|
|
data: row.get(1),
|
|
|
|
}).unwrap();
|
|
|
|
|
|
|
|
let mut received = vec![];
|
|
|
|
for row in rows {
|
|
|
|
let row = row.unwrap();
|
|
|
|
for tx in scan_block_from_bytes(&row.data, &extfvks) {
|
|
|
|
for output in tx.shielded_outputs {
|
|
|
|
let mut vr = ValueReceived::new();
|
|
|
|
vr.set_blockHeight(row.height as u64);
|
|
|
|
vr.set_txHash(tx.txid.0.to_vec());
|
|
|
|
vr.set_value(output.value);
|
|
|
|
received.push(vr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
received
|
|
|
|
}
|
|
|
|
|
2018-11-20 09:59:08 -08:00
|
|
|
/// JNI interface
|
2018-11-21 05:51:37 -08:00
|
|
|
#[cfg(target_os = "android")]
|
2018-11-20 09:59:08 -08:00
|
|
|
#[allow(non_snake_case)]
|
|
|
|
pub mod android {
|
2018-11-28 09:19:43 -08:00
|
|
|
extern crate android_logger;
|
2018-11-20 09:59:08 -08:00
|
|
|
extern crate jni;
|
2018-11-28 09:19:43 -08:00
|
|
|
extern crate log_panics;
|
2018-11-20 09:59:08 -08:00
|
|
|
|
2018-11-28 09:19:43 -08:00
|
|
|
use log::Level;
|
2018-11-22 05:54:25 -08:00
|
|
|
use protobuf::Message;
|
|
|
|
|
2018-11-28 09:19:43 -08:00
|
|
|
use self::android_logger::Filter;
|
2018-11-22 05:54:25 -08:00
|
|
|
use self::jni::objects::{JClass, JString};
|
|
|
|
use self::jni::sys::{jbyteArray, jint, jobjectArray, jsize, jstring};
|
2018-11-21 05:51:37 -08:00
|
|
|
use self::jni::JNIEnv;
|
2018-11-20 09:59:08 -08:00
|
|
|
|
2018-11-22 05:54:25 -08:00
|
|
|
use super::{address_from_extfvk, extfvk_from_seed, scan_cached_blocks};
|
2018-11-20 09:59:08 -08:00
|
|
|
|
2018-11-28 09:19:43 -08:00
|
|
|
#[no_mangle]
|
|
|
|
pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_initLogs(
|
|
|
|
_env: JNIEnv,
|
|
|
|
_: JClass,
|
|
|
|
) {
|
|
|
|
android_logger::init_once(
|
|
|
|
Filter::default().with_min_level(Level::Trace),
|
|
|
|
Some("cash.z.rust.logs"),
|
|
|
|
);
|
|
|
|
|
|
|
|
log_panics::init();
|
|
|
|
|
|
|
|
debug!("logs have been initialized successfully");
|
|
|
|
}
|
|
|
|
|
2018-11-20 09:59:08 -08:00
|
|
|
#[no_mangle]
|
2018-11-21 08:40:18 -08:00
|
|
|
pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_getAddress(
|
2018-11-21 05:51:37 -08:00
|
|
|
env: JNIEnv,
|
|
|
|
_: JClass,
|
2018-11-21 08:40:18 -08:00
|
|
|
seed: jbyteArray,
|
|
|
|
) -> jstring {
|
|
|
|
let seed = env.convert_byte_array(seed).unwrap();
|
|
|
|
|
2018-11-21 09:29:30 -08:00
|
|
|
let addr = address_from_extfvk(&extfvk_from_seed(&seed));
|
2018-11-21 08:40:18 -08:00
|
|
|
|
|
|
|
let output = env.new_string(addr).expect("Couldn't create Java string!");
|
|
|
|
output.into_inner()
|
2018-11-20 09:59:08 -08:00
|
|
|
}
|
2018-11-22 05:54:25 -08:00
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub unsafe extern "C" fn Java_cash_z_wallet_sdk_jni_JniConverter_scanBlocks(
|
|
|
|
env: JNIEnv,
|
|
|
|
_: JClass,
|
|
|
|
db: JString,
|
|
|
|
start: jint,
|
|
|
|
end: jint,
|
|
|
|
seed: jbyteArray,
|
|
|
|
) -> jobjectArray {
|
|
|
|
let db = env
|
|
|
|
.get_string(db)
|
|
|
|
.expect("Couldn't get Java string!")
|
|
|
|
.into();
|
|
|
|
let seed = env.convert_byte_array(seed).unwrap();
|
|
|
|
|
|
|
|
let received = scan_cached_blocks(db, start, end, &[extfvk_from_seed(&seed)]);
|
|
|
|
|
|
|
|
let jreceived = env
|
|
|
|
.new_object_array(
|
|
|
|
received.len() as jsize,
|
|
|
|
"[B",
|
|
|
|
env.new_byte_array(0).unwrap().into(),
|
|
|
|
).unwrap();
|
|
|
|
for (i, vr) in received.into_iter().enumerate() {
|
|
|
|
let jvr = env
|
|
|
|
.byte_array_from_slice(&vr.write_to_bytes().unwrap())
|
|
|
|
.unwrap();
|
|
|
|
env.set_object_array_element(jreceived, i as jsize, jvr.into())
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
jreceived
|
|
|
|
}
|
2018-11-20 09:59:08 -08:00
|
|
|
}
|