Implement remainder of FFI interface
This commit is contained in:
parent
bac9957f2b
commit
80ca214b25
|
@ -275,6 +275,15 @@ dependencies = [
|
||||||
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "directories"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "failure"
|
name = "failure"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
@ -426,9 +435,11 @@ dependencies = [
|
||||||
"cbindgen 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cbindgen 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ffi_helpers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ffi_helpers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"zcash_client_backend 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
"zcash_client_backend 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
||||||
"zcash_client_sqlite 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
"zcash_client_sqlite 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
||||||
"zcash_primitives 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
"zcash_primitives 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
||||||
|
"zcash_proofs 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -972,6 +983,22 @@ dependencies = [
|
||||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zcash_proofs"
|
||||||
|
version = "0.0.0"
|
||||||
|
source = "git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5#ae63116f639847e54716fbf47f19589d12038a16"
|
||||||
|
dependencies = [
|
||||||
|
"bellman 0.1.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
||||||
|
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
|
||||||
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ff 0.4.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
||||||
|
"pairing 0.14.2 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
||||||
|
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"sapling-crypto 0.0.1 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
||||||
|
"zcash_primitives 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)",
|
||||||
|
]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum aes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6fb1737cdc8da3db76e90ca817a194249a38fcb500c2e6ecec39b29448aa873"
|
"checksum aes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6fb1737cdc8da3db76e90ca817a194249a38fcb500c2e6ecec39b29448aa873"
|
||||||
"checksum aes-soft 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67cc03b0a090a05cb01e96998a01905d7ceedce1bc23b756c0bb7faa0682ccb1"
|
"checksum aes-soft 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67cc03b0a090a05cb01e96998a01905d7ceedce1bc23b756c0bb7faa0682ccb1"
|
||||||
|
@ -1007,6 +1034,7 @@ dependencies = [
|
||||||
"checksum crypto_api_chachapoly 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9ee35dbace0831b5fe7cb9b43eb029aa14a10f594a115025d4628a2baa63ab"
|
"checksum crypto_api_chachapoly 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9ee35dbace0831b5fe7cb9b43eb029aa14a10f594a115025d4628a2baa63ab"
|
||||||
"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
|
"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
|
||||||
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
|
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
|
||||||
|
"checksum directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f"
|
||||||
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
||||||
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
||||||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||||
|
@ -1088,3 +1116,4 @@ dependencies = [
|
||||||
"checksum zcash_client_backend 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)" = "<none>"
|
"checksum zcash_client_backend 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)" = "<none>"
|
||||||
"checksum zcash_client_sqlite 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)" = "<none>"
|
"checksum zcash_client_sqlite 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)" = "<none>"
|
||||||
"checksum zcash_primitives 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)" = "<none>"
|
"checksum zcash_primitives 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)" = "<none>"
|
||||||
|
"checksum zcash_proofs 0.0.0 (git+https://github.com/str4d/librustzcash.git?branch=note-spending-v5)" = "<none>"
|
||||||
|
|
|
@ -8,6 +8,7 @@ build = "rust/build.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
ffi_helpers = "0.1"
|
ffi_helpers = "0.1"
|
||||||
|
hex = "0.3"
|
||||||
|
|
||||||
[dependencies.zcash_client_backend]
|
[dependencies.zcash_client_backend]
|
||||||
git = "https://github.com/str4d/librustzcash.git"
|
git = "https://github.com/str4d/librustzcash.git"
|
||||||
|
@ -21,6 +22,10 @@ branch = "note-spending-v5"
|
||||||
git = "https://github.com/str4d/librustzcash.git"
|
git = "https://github.com/str4d/librustzcash.git"
|
||||||
branch = "note-spending-v5"
|
branch = "note-spending-v5"
|
||||||
|
|
||||||
|
[dependencies.zcash_proofs]
|
||||||
|
git = "https://github.com/str4d/librustzcash.git"
|
||||||
|
branch = "note-spending-v5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cbindgen = "0.8"
|
cbindgen = "0.8"
|
||||||
|
|
||||||
|
|
341
rust/src/lib.rs
341
rust/src/lib.rs
|
@ -1,16 +1,27 @@
|
||||||
use failure::format_err;
|
use failure::format_err;
|
||||||
use ffi_helpers::panic::catch_panic;
|
use ffi_helpers::panic::catch_panic;
|
||||||
use std::ffi::{CString, OsStr};
|
use std::ffi::{CStr, CString, OsStr};
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use zcash_client_backend::{
|
use zcash_client_backend::{
|
||||||
constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, encoding::encode_extended_spending_key,
|
constants::{testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, SAPLING_CONSENSUS_BRANCH_ID},
|
||||||
|
encoding::{decode_extended_spending_key, encode_extended_spending_key},
|
||||||
keys::spending_key,
|
keys::spending_key,
|
||||||
};
|
};
|
||||||
use zcash_client_sqlite::{get_address, init_accounts_table, init_data_database, ErrorKind};
|
use zcash_client_sqlite::{
|
||||||
use zcash_primitives::zip32::ExtendedFullViewingKey;
|
address::RecipientAddress,
|
||||||
|
chain::{rewind_to_height, validate_combined_chain},
|
||||||
|
get_address, get_balance, get_received_memo_as_utf8, get_sent_memo_as_utf8,
|
||||||
|
get_verified_balance, init_accounts_table, init_blocks_table, init_data_database,
|
||||||
|
scan_cached_blocks, send_to_address, ErrorKind,
|
||||||
|
};
|
||||||
|
use zcash_primitives::{
|
||||||
|
block::BlockHash, note_encryption::Memo, transaction::components::Amount,
|
||||||
|
zip32::ExtendedFullViewingKey,
|
||||||
|
};
|
||||||
|
use zcash_proofs::prover::LocalTxProver;
|
||||||
|
|
||||||
fn unwrap_exc_or<T>(exc: Result<T, ()>, def: T) -> T {
|
fn unwrap_exc_or<T>(exc: Result<T, ()>, def: T) -> T {
|
||||||
match exc {
|
match exc {
|
||||||
|
@ -119,6 +130,39 @@ pub extern "C" fn zcashlc_init_accounts_table(
|
||||||
unwrap_exc_or_null(res)
|
unwrap_exc_or_null(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialises the data database with the given block.
|
||||||
|
///
|
||||||
|
/// This enables a newly-created database to be immediately-usable, without needing to
|
||||||
|
/// synchronise historic blocks.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn zcashlc_init_blocks_table(
|
||||||
|
db_data: *const u8,
|
||||||
|
db_data_len: usize,
|
||||||
|
height: i32,
|
||||||
|
hash_hex: *const c_char,
|
||||||
|
time: u32,
|
||||||
|
sapling_tree_hex: *const c_char,
|
||||||
|
) -> i32 {
|
||||||
|
let res = catch_panic(|| {
|
||||||
|
let db_data = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_data, db_data_len)
|
||||||
|
}));
|
||||||
|
let hash = {
|
||||||
|
let mut hash = hex::decode(unsafe { CStr::from_ptr(hash_hex) }.to_str()?).unwrap();
|
||||||
|
hash.reverse();
|
||||||
|
BlockHash::from_slice(&hash)
|
||||||
|
};
|
||||||
|
let sapling_tree =
|
||||||
|
hex::decode(unsafe { CStr::from_ptr(sapling_tree_hex) }.to_str()?).unwrap();
|
||||||
|
|
||||||
|
match init_blocks_table(&db_data, height, hash, time, &sapling_tree) {
|
||||||
|
Ok(()) => Ok(1),
|
||||||
|
Err(e) => Err(format_err!("Error while initializing blocks table: {}", e)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
unwrap_exc_or_null(res)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the address for the account.
|
/// Returns the address for the account.
|
||||||
///
|
///
|
||||||
/// Call `zcashlc_string_free` on the returned pointer when you are finished with it.
|
/// Call `zcashlc_string_free` on the returned pointer when you are finished with it.
|
||||||
|
@ -149,6 +193,295 @@ pub extern "C" fn zcashlc_get_address(
|
||||||
unwrap_exc_or_null(res)
|
unwrap_exc_or_null(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the balance for the account, including all unspent notes that we know about.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn zcashlc_get_balance(db_data: *const u8, db_data_len: usize, account: i32) -> i64 {
|
||||||
|
let res = catch_panic(|| {
|
||||||
|
let db_data = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_data, db_data_len)
|
||||||
|
}));
|
||||||
|
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) => Ok(balance.0),
|
||||||
|
Err(e) => Err(format_err!("Error while fetching balance: {}", e)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
unwrap_exc_or(res, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the verified balance for the account, which ignores notes that have been
|
||||||
|
/// received too recently and are not yet deemed spendable.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn zcashlc_get_verified_balance(
|
||||||
|
db_data: *const u8,
|
||||||
|
db_data_len: usize,
|
||||||
|
account: i32,
|
||||||
|
) -> i64 {
|
||||||
|
let res = catch_panic(|| {
|
||||||
|
let db_data = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_data, db_data_len)
|
||||||
|
}));
|
||||||
|
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) {
|
||||||
|
Ok(balance) => Ok(balance.0),
|
||||||
|
Err(e) => Err(format_err!("Error while fetching verified balance: {}", e)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
unwrap_exc_or(res, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the memo for a received note, if it is known and a valid UTF-8 string.
|
||||||
|
///
|
||||||
|
/// The note is identified by its row index in the `received_notes` table within the data
|
||||||
|
/// database.
|
||||||
|
///
|
||||||
|
/// Call `zcashlc_string_free` on the returned pointer when you are finished with it.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn zcashlc_get_received_memo_as_utf8(
|
||||||
|
db_data: *const u8,
|
||||||
|
db_data_len: usize,
|
||||||
|
id_note: i64,
|
||||||
|
) -> *mut c_char {
|
||||||
|
let res = catch_panic(|| {
|
||||||
|
let db_data = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_data, db_data_len)
|
||||||
|
}));
|
||||||
|
|
||||||
|
let memo = match 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)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(CString::new(memo).unwrap().into_raw())
|
||||||
|
});
|
||||||
|
unwrap_exc_or_null(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the memo for a sent note, if it is known and a valid UTF-8 string.
|
||||||
|
///
|
||||||
|
/// The note is identified by its row index in the `sent_notes` table within the data
|
||||||
|
/// database.
|
||||||
|
///
|
||||||
|
/// Call `zcashlc_string_free` on the returned pointer when you are finished with it.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn zcashlc_get_sent_memo_as_utf8(
|
||||||
|
db_data: *const u8,
|
||||||
|
db_data_len: usize,
|
||||||
|
id_note: i64,
|
||||||
|
) -> *mut c_char {
|
||||||
|
let res = catch_panic(|| {
|
||||||
|
let db_data = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_data, db_data_len)
|
||||||
|
}));
|
||||||
|
|
||||||
|
let memo = match 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)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(CString::new(memo).unwrap().into_raw())
|
||||||
|
});
|
||||||
|
unwrap_exc_or_null(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks that the scanned blocks in the data database, when combined with the recent
|
||||||
|
/// `CompactBlock`s in the cache database, form a valid chain.
|
||||||
|
///
|
||||||
|
/// This function is built on the core assumption that the information provided in the
|
||||||
|
/// cache database is more likely to be accurate than the previously-scanned information.
|
||||||
|
/// This follows from the design (and trust) assumption that the `lightwalletd` server
|
||||||
|
/// provides accurate block information as of the time it was requested.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// - `-1` if the combined chain is valid.
|
||||||
|
/// - `upper_bound` if the combined chain is invalid.
|
||||||
|
/// `upper_bound` is the height of the highest invalid block (on the assumption that the
|
||||||
|
/// highest block in the cache database is correct).
|
||||||
|
/// - `0` if there was an error during validation unrelated to chain validity.
|
||||||
|
///
|
||||||
|
/// This function does not mutate either of the databases.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn zcashlc_validate_combined_chain(
|
||||||
|
db_cache: *const u8,
|
||||||
|
db_cache_len: usize,
|
||||||
|
db_data: *const u8,
|
||||||
|
db_data_len: usize,
|
||||||
|
) -> i32 {
|
||||||
|
let res = catch_panic(|| {
|
||||||
|
let db_cache = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_cache, db_cache_len)
|
||||||
|
}));
|
||||||
|
let db_data = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_data, db_data_len)
|
||||||
|
}));
|
||||||
|
|
||||||
|
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_null(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rewinds the data database to the given height.
|
||||||
|
///
|
||||||
|
/// If the requested height is greater than or equal to the height of the last scanned
|
||||||
|
/// block, this function does nothing.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn zcashlc_rewind_to_height(
|
||||||
|
db_data: *const u8,
|
||||||
|
db_data_len: usize,
|
||||||
|
height: i32,
|
||||||
|
) -> i32 {
|
||||||
|
let res = catch_panic(|| {
|
||||||
|
let db_data = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_data, db_data_len)
|
||||||
|
}));
|
||||||
|
|
||||||
|
match rewind_to_height(&db_data, height) {
|
||||||
|
Ok(()) => Ok(1),
|
||||||
|
Err(e) => Err(format_err!(
|
||||||
|
"Error while rewinding data DB to height {}: {}",
|
||||||
|
height,
|
||||||
|
e
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
unwrap_exc_or_null(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scans new blocks added to the cache for any transactions received by the tracked
|
||||||
|
/// accounts.
|
||||||
|
///
|
||||||
|
/// This function pays attention only to cached blocks with heights greater than the
|
||||||
|
/// highest scanned block in `db_data`. Cached blocks with lower heights are not verified
|
||||||
|
/// against previously-scanned blocks. In particular, this function **assumes** that the
|
||||||
|
/// caller is handling rollbacks.
|
||||||
|
///
|
||||||
|
/// For brand-new light client databases, this function starts scanning from the Sapling
|
||||||
|
/// activation height. This height can be fast-forwarded to a more recent block by calling
|
||||||
|
/// [`zcashlc_init_blocks_table`] before this function.
|
||||||
|
///
|
||||||
|
/// Scanned blocks are required to be height-sequential. If a block is missing from the
|
||||||
|
/// cache, an error will be signalled.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn zcashlc_scan_blocks(
|
||||||
|
db_cache: *const u8,
|
||||||
|
db_cache_len: usize,
|
||||||
|
db_data: *const u8,
|
||||||
|
db_data_len: usize,
|
||||||
|
) -> i32 {
|
||||||
|
let res = catch_panic(|| {
|
||||||
|
let db_cache = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_cache, db_cache_len)
|
||||||
|
}));
|
||||||
|
let db_data = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_data, db_data_len)
|
||||||
|
}));
|
||||||
|
|
||||||
|
match scan_cached_blocks(&db_cache, &db_data) {
|
||||||
|
Ok(()) => Ok(1),
|
||||||
|
Err(e) => Err(format_err!("Error while scanning blocks: {}", e)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
unwrap_exc_or_null(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a transaction paying the specified address from the given account.
|
||||||
|
///
|
||||||
|
/// Returns the row index of the newly-created transaction in the `transactions` table
|
||||||
|
/// within the data database. The caller can read the raw transaction bytes from the `raw`
|
||||||
|
/// column in order to broadcast the transaction to the network.
|
||||||
|
///
|
||||||
|
/// Do not call this multiple times in parallel, or you will generate transactions that
|
||||||
|
/// double-spend the same notes.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn zcashlc_send_to_address(
|
||||||
|
db_data: *const u8,
|
||||||
|
db_data_len: usize,
|
||||||
|
account: i32,
|
||||||
|
extsk: *const c_char,
|
||||||
|
to: *const c_char,
|
||||||
|
value: i64,
|
||||||
|
memo: *const c_char,
|
||||||
|
spend_params: *const u8,
|
||||||
|
spend_params_len: usize,
|
||||||
|
output_params: *const u8,
|
||||||
|
output_params_len: usize,
|
||||||
|
) -> i64 {
|
||||||
|
let res = catch_panic(|| {
|
||||||
|
let db_data = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(db_data, db_data_len)
|
||||||
|
}));
|
||||||
|
let account = if account >= 0 {
|
||||||
|
account as u32
|
||||||
|
} else {
|
||||||
|
return Err(format_err!("account argument must be positive"));
|
||||||
|
};
|
||||||
|
let extsk = unsafe { CStr::from_ptr(extsk) }.to_str()?;
|
||||||
|
let to = unsafe { CStr::from_ptr(to) }.to_str()?;
|
||||||
|
let value = Amount(value);
|
||||||
|
let memo = unsafe { CStr::from_ptr(memo) }.to_str()?;
|
||||||
|
let spend_params = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(spend_params, spend_params_len)
|
||||||
|
}));
|
||||||
|
let output_params = Path::new(OsStr::from_bytes(unsafe {
|
||||||
|
slice::from_raw_parts(output_params, output_params_len)
|
||||||
|
}));
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let to = match RecipientAddress::from_str(&to) {
|
||||||
|
Ok(Some(to)) => to,
|
||||||
|
Ok(None) => {
|
||||||
|
return Err(format_err!("PaymentAddress is for the wrong network"));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format_err!("Invalid address: {}", e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let memo = Memo::from_str(&memo);
|
||||||
|
|
||||||
|
let prover = LocalTxProver::new(spend_params, output_params);
|
||||||
|
|
||||||
|
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(res, -1)
|
||||||
|
}
|
||||||
|
|
||||||
/// Frees strings returned by other zcashlc functions.
|
/// Frees strings returned by other zcashlc functions.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn zcashlc_string_free(s: *mut c_char) {
|
pub extern "C" fn zcashlc_string_free(s: *mut c_char) {
|
||||||
|
|
Loading…
Reference in New Issue