Documentation
This commit is contained in:
parent
49eeb7bd54
commit
267192c42c
|
@ -1,5 +1,5 @@
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
use sync::scan_all;
|
use warp_api_ffi::scan_all;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
|
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
|
||||||
use zcash_primitives::consensus::{Network, Parameters};
|
use zcash_primitives::consensus::{Network, Parameters};
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
[package]
|
||||||
|
name = "warpsync-rust-example"
|
||||||
|
version = "1.2.15"
|
||||||
|
authors = ["Hanh <hanh425@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "warp-example"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "^1.6", features = ["macros", "rt-multi-thread"] }
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
env_logger = "0.9"
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
|
[dependencies.zcash-warpsync]
|
||||||
|
path = "../.."
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
use warp_api_ffi::api::account::{get_backup, new_account};
|
||||||
|
use warp_api_ffi::api::sync::coin_sync;
|
||||||
|
use warp_api_ffi::{CoinConfig, init_coin, set_coin_lwd_url};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref CANCEL: Mutex<bool> = Mutex::new(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const FVK: &str = "zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz";
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
// Initialize the library for Zcash (coin = 0)
|
||||||
|
init_coin(0, "./zec.db").unwrap();
|
||||||
|
set_coin_lwd_url(0, "https://lwdv3.zecwallet.co:443"); // ZecWallet Lightwalletd URL
|
||||||
|
|
||||||
|
// Create a new account with the ZEC pages viewing key
|
||||||
|
let id_account = new_account(0, "test_account", Some(FVK.to_string()),
|
||||||
|
None).unwrap();
|
||||||
|
|
||||||
|
// Synchronize
|
||||||
|
coin_sync(0 /* zcash */,
|
||||||
|
true /* retrieve tx details */,
|
||||||
|
0 /* sync to tip */,
|
||||||
|
100 /* spam filter threshold */, |p| {
|
||||||
|
log::info!("Progress: {}", p.height);
|
||||||
|
}, &CANCEL).await.unwrap();
|
||||||
|
|
||||||
|
// Grab the database accessor
|
||||||
|
let cc = &CoinConfig::get(0 /* zcash */);
|
||||||
|
let db = cc.db.as_ref().unwrap().clone();
|
||||||
|
let db = db.lock().unwrap();
|
||||||
|
|
||||||
|
// Query the account balance
|
||||||
|
let balance = db.get_balance(id_account).unwrap();
|
||||||
|
|
||||||
|
println!("Balance = {}", balance)
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Account related API
|
||||||
|
|
||||||
// Account creation
|
// Account creation
|
||||||
|
|
||||||
use crate::coinconfig::CoinConfig;
|
use crate::coinconfig::CoinConfig;
|
||||||
|
@ -5,7 +7,7 @@ use crate::db::AccountData;
|
||||||
use crate::key2::decode_key;
|
use crate::key2::decode_key;
|
||||||
use crate::taddr::{derive_taddr, derive_tkeys};
|
use crate::taddr::{derive_taddr, derive_tkeys};
|
||||||
use crate::transaction::retrieve_tx_info;
|
use crate::transaction::retrieve_tx_info;
|
||||||
use crate::{connect_lightwalletd, derive_zip32, AccountInfo, KeyPack};
|
use crate::{connect_lightwalletd, AccountInfo, KeyPack};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use bip39::{Language, Mnemonic};
|
use bip39::{Language, Mnemonic};
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
@ -14,7 +16,21 @@ use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use zcash_client_backend::encoding::{decode_extended_full_viewing_key, encode_payment_address};
|
use zcash_client_backend::encoding::{decode_extended_full_viewing_key, encode_payment_address};
|
||||||
use zcash_primitives::consensus::Parameters;
|
use zcash_primitives::consensus::Parameters;
|
||||||
|
use crate::zip32::derive_zip32;
|
||||||
|
|
||||||
|
/// Create a new account
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `name`: account name
|
||||||
|
/// * `key`: `Some(key)` where key is either a passphrase,
|
||||||
|
/// a secret key or a viewing key for an existing account,
|
||||||
|
/// or `None` for a new randomly generated account
|
||||||
|
/// * `index`: `Some(x)` for account at index `x` or
|
||||||
|
/// `None` for main account (same as x = 0)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// `account id`
|
||||||
pub fn new_account(
|
pub fn new_account(
|
||||||
coin: u8,
|
coin: u8,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
@ -34,6 +50,17 @@ pub fn new_account(
|
||||||
Ok(id_account)
|
Ok(id_account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create one or many sub accounts of the current account
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// crate::api::account::new_sub_account("test", None, 5)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `name`: name of the sub accounts. Every sub account will have the same name
|
||||||
|
/// * `index`: Starting index. If `None`, use the index following the highest used index
|
||||||
|
/// * `count`: Number of subaccounts to create
|
||||||
pub fn new_sub_account(name: &str, index: Option<u32>, count: u32) -> anyhow::Result<()> {
|
pub fn new_sub_account(name: &str, index: Option<u32>, count: u32) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
|
@ -59,6 +86,14 @@ fn new_account_with_key(coin: u8, name: &str, key: &str, index: u32) -> anyhow::
|
||||||
Ok(account)
|
Ok(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the transparent secret key for the given account from a derivation path
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `id_account`: account id as returned from [new_account]
|
||||||
|
/// * `path`: derivation path
|
||||||
|
///
|
||||||
|
/// Account must have a seed phrase
|
||||||
pub fn import_transparent_key(coin: u8, id_account: u32, path: &str) -> anyhow::Result<()> {
|
pub fn import_transparent_key(coin: u8, id_account: u32, path: &str) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
|
@ -69,6 +104,12 @@ pub fn import_transparent_key(coin: u8, id_account: u32, path: &str) -> anyhow::
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the transparent secret key for the given account
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `id_account`: account id as returned from [new_account]
|
||||||
|
/// * `sk`: secret key
|
||||||
pub fn import_transparent_secret_key(coin: u8, id_account: u32, sk: &str) -> anyhow::Result<()> {
|
pub fn import_transparent_secret_key(coin: u8, id_account: u32, sk: &str) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
|
@ -77,6 +118,7 @@ pub fn import_transparent_secret_key(coin: u8, id_account: u32, sk: &str) -> any
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a new diversified address
|
||||||
pub fn new_diversified_address() -> anyhow::Result<String> {
|
pub fn new_diversified_address() -> anyhow::Result<String> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
|
@ -96,11 +138,16 @@ pub fn new_diversified_address() -> anyhow::Result<String> {
|
||||||
Ok(pa)
|
Ok(pa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve the transparent balance for the current account from the LWD server
|
||||||
pub async fn get_taddr_balance_default() -> anyhow::Result<u64> {
|
pub async fn get_taddr_balance_default() -> anyhow::Result<u64> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
get_taddr_balance(c.coin, c.id_account).await
|
get_taddr_balance(c.coin, c.id_account).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve the transparent balance from the LWD server
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `id_account`: account id as returned from [new_account]
|
||||||
pub async fn get_taddr_balance(coin: u8, id_account: u32) -> anyhow::Result<u64> {
|
pub async fn get_taddr_balance(coin: u8, id_account: u32) -> anyhow::Result<u64> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let mut client = c.connect_lwd().await?;
|
let mut client = c.connect_lwd().await?;
|
||||||
|
@ -112,6 +159,10 @@ pub async fn get_taddr_balance(coin: u8, id_account: u32) -> anyhow::Result<u64>
|
||||||
Ok(balance)
|
Ok(balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Look for accounts that have some transparent balance. Stop when the gap limit
|
||||||
|
/// is exceeded and no balance was found
|
||||||
|
/// # Arguments
|
||||||
|
/// * `gap_limit`: number of accounts with 0 balance before the scan stops
|
||||||
pub async fn scan_transparent_accounts(gap_limit: usize) -> anyhow::Result<()> {
|
pub async fn scan_transparent_accounts(gap_limit: usize) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let mut client = c.connect_lwd().await?;
|
let mut client = c.connect_lwd().await?;
|
||||||
|
@ -119,8 +170,12 @@ pub async fn scan_transparent_accounts(gap_limit: usize) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account backup
|
/// Get the backup string. It is either the passphrase, the secret key or the viewing key
|
||||||
|
/// depending on how the account was created
|
||||||
|
/// # Arguments
|
||||||
|
/// * `id_account`: account id as returned from [new_account]
|
||||||
|
///
|
||||||
|
/// Use the current active coin
|
||||||
pub fn get_backup(account: u32) -> anyhow::Result<String> {
|
pub fn get_backup(account: u32) -> anyhow::Result<String> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let AccountData { seed, sk, fvk, .. } = c.db()?.get_account_info(account)?;
|
let AccountData { seed, sk, fvk, .. } = c.db()?.get_account_info(account)?;
|
||||||
|
@ -133,30 +188,44 @@ pub fn get_backup(account: u32) -> anyhow::Result<String> {
|
||||||
Ok(fvk)
|
Ok(fvk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the secret key. Returns empty string if the account has no secret key
|
||||||
|
/// # Arguments
|
||||||
|
/// * `id_account`: account id as returned from [new_account]
|
||||||
|
///
|
||||||
|
/// Use the current active coin
|
||||||
pub fn get_sk(account: u32) -> anyhow::Result<String> {
|
pub fn get_sk(account: u32) -> anyhow::Result<String> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let AccountData { sk, .. } = c.db()?.get_account_info(account)?;
|
let AccountData { sk, .. } = c.db()?.get_account_info(account)?;
|
||||||
Ok(sk.unwrap_or(String::new()))
|
Ok(sk.unwrap_or(String::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset the database
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
pub fn reset_db(coin: u8) -> anyhow::Result<()> {
|
pub fn reset_db(coin: u8) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
db.reset_db()
|
db.reset_db()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Truncate all non account data for the current active coin
|
||||||
pub fn truncate_data() -> anyhow::Result<()> {
|
pub fn truncate_data() -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
db.truncate_data()
|
db.truncate_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Truncate all synchronization data for the current active coin
|
||||||
pub fn truncate_sync_data() -> anyhow::Result<()> {
|
pub fn truncate_sync_data() -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
db.truncate_sync_data()
|
db.truncate_sync_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete an account
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `id_account`: account id as returned from [new_account]
|
||||||
pub fn delete_account(coin: u8, account: u32) -> anyhow::Result<()> {
|
pub fn delete_account(coin: u8, account: u32) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
|
@ -164,9 +233,14 @@ pub fn delete_account(coin: u8, account: u32) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Import a ZWL data file
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `name`: prefix for the imported accounts
|
||||||
|
/// * `data`: data file
|
||||||
pub fn import_from_zwl(coin: u8, name: &str, data: &str) -> anyhow::Result<()> {
|
pub fn import_from_zwl(coin: u8, name: &str, data: &str) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let sks = crate::read_zwl(data)?;
|
let sks = crate::misc::read_zwl(data)?;
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
for (i, key) in sks.iter().enumerate() {
|
for (i, key) in sks.iter().enumerate() {
|
||||||
let name = format!("{}-{}", name, i + 1);
|
let name = format!("{}-{}", name, i + 1);
|
||||||
|
@ -176,6 +250,13 @@ pub fn import_from_zwl(coin: u8, name: &str, data: &str) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Derive keys using Zip-32
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `id_account`: account id as returned from [new_account]. Must have a passphrase
|
||||||
|
/// * `account`: derived account index
|
||||||
|
/// * `external`: external/internal
|
||||||
|
/// * `address`: address index
|
||||||
pub fn derive_keys(
|
pub fn derive_keys(
|
||||||
coin: u8,
|
coin: u8,
|
||||||
id_account: u32,
|
id_account: u32,
|
||||||
|
@ -190,6 +271,10 @@ pub fn derive_keys(
|
||||||
derive_zip32(c.chain.network(), &seed, account, external, address)
|
derive_zip32(c.chain.network(), &seed, account, external, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Import synchronization data obtained from external source
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `file`: file that contains the synchronization data
|
||||||
pub async fn import_sync_data(coin: u8, file: &str) -> anyhow::Result<()> {
|
pub async fn import_sync_data(coin: u8, file: &str) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let mut db = c.db()?;
|
let mut db = c.db()?;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Contact Address book
|
||||||
|
|
||||||
use crate::api::payment::{build_sign_send_multi_payment, RecipientMemo};
|
use crate::api::payment::{build_sign_send_multi_payment, RecipientMemo};
|
||||||
use crate::api::sync::get_latest_height;
|
use crate::api::sync::get_latest_height;
|
||||||
use crate::coinconfig::CoinConfig;
|
use crate::coinconfig::CoinConfig;
|
||||||
|
@ -5,6 +7,12 @@ use crate::contact::{serialize_contacts, Contact};
|
||||||
use crate::db::AccountData;
|
use crate::db::AccountData;
|
||||||
use zcash_primitives::memo::Memo;
|
use zcash_primitives::memo::Memo;
|
||||||
|
|
||||||
|
/// Store contact in the database
|
||||||
|
/// # Arguments
|
||||||
|
/// * `id`: contact id
|
||||||
|
/// * `name`: contact name
|
||||||
|
/// * `address`: contact address
|
||||||
|
/// * `dirty`: true if the database hasn't been saved to the blockchain yet
|
||||||
pub fn store_contact(id: u32, name: &str, address: &str, dirty: bool) -> anyhow::Result<()> {
|
pub fn store_contact(id: u32, name: &str, address: &str, dirty: bool) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let contact = Contact {
|
let contact = Contact {
|
||||||
|
@ -16,6 +24,9 @@ pub fn store_contact(id: u32, name: &str, address: &str, dirty: bool) -> anyhow:
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Save the new/modified contacts to the blockchain
|
||||||
|
/// # Arguments
|
||||||
|
/// * `anchor_offset`: minimum confirmations required for note selection
|
||||||
pub async fn commit_unsaved_contacts(anchor_offset: u32) -> anyhow::Result<String> {
|
pub async fn commit_unsaved_contacts(anchor_offset: u32) -> anyhow::Result<String> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let contacts = c.db()?.get_unsaved_contacts()?;
|
let contacts = c.db()?.get_unsaved_contacts()?;
|
||||||
|
@ -24,7 +35,7 @@ pub async fn commit_unsaved_contacts(anchor_offset: u32) -> anyhow::Result<Strin
|
||||||
Ok(tx_id)
|
Ok(tx_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save_contacts_tx(memos: &[Memo], anchor_offset: u32) -> anyhow::Result<String> {
|
async fn save_contacts_tx(memos: &[Memo], anchor_offset: u32) -> anyhow::Result<String> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let last_height = get_latest_height().await?;
|
let last_height = get_latest_height().await?;
|
||||||
let AccountData { address, .. } = c.db()?.get_account_info(c.id_account)?;
|
let AccountData { address, .. } = c.db()?.get_account_info(c.id_account)?;
|
||||||
|
|
|
@ -8,13 +8,11 @@ use log::Level;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
use zcash_primitives::transaction::builder::Progress;
|
use zcash_primitives::transaction::builder::Progress;
|
||||||
|
|
||||||
static mut POST_COBJ: Option<ffi::DartPostCObjectFnType> = None;
|
static mut POST_COBJ: Option<ffi::DartPostCObjectFnType> = None;
|
||||||
static IS_ERROR: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
const MAX_COINS: u8 = 3;
|
const MAX_COINS: u8 = 3;
|
||||||
|
|
||||||
|
@ -90,56 +88,12 @@ fn try_init_logger() {
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn log_result<T: Default>(result: anyhow::Result<T>) -> T {
|
|
||||||
// match result {
|
|
||||||
// Err(err) => {
|
|
||||||
// log::error!("ERROR: {}", err);
|
|
||||||
// let last_error = LAST_ERROR.lock().unwrap();
|
|
||||||
// last_error.replace(err.to_string());
|
|
||||||
// IS_ERROR.store(true, Ordering::Release);
|
|
||||||
// T::default()
|
|
||||||
// }
|
|
||||||
// Ok(v) => {
|
|
||||||
// IS_ERROR.store(false, Ordering::Release);
|
|
||||||
// v
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn log_string(result: anyhow::Result<String>) -> String {
|
|
||||||
// match result {
|
|
||||||
// Err(err) => {
|
|
||||||
// log::error!("{}", err);
|
|
||||||
// let last_error = LAST_ERROR.lock().unwrap();
|
|
||||||
// last_error.replace(err.to_string());
|
|
||||||
// IS_ERROR.store(true, Ordering::Release);
|
|
||||||
// format!("{}", err)
|
|
||||||
// }
|
|
||||||
// Ok(v) => {
|
|
||||||
// IS_ERROR.store(false, Ordering::Release);
|
|
||||||
// v
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct CResult<T> {
|
pub struct CResult<T> {
|
||||||
value: T,
|
value: T,
|
||||||
error: *mut c_char,
|
error: *mut c_char,
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[no_mangle]
|
|
||||||
// pub unsafe extern "C" fn get_error() -> bool {
|
|
||||||
// IS_ERROR.load(Ordering::Acquire)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[no_mangle]
|
|
||||||
// pub unsafe extern "C" fn get_error_msg() -> *mut c_char {
|
|
||||||
// let error = LAST_ERROR.lock().unwrap();
|
|
||||||
// let e = error.take();
|
|
||||||
// to_c_str(e)
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn init_wallet(db_path: *mut c_char) {
|
pub unsafe extern "C" fn init_wallet(db_path: *mut c_char) {
|
||||||
try_init_logger();
|
try_init_logger();
|
||||||
|
@ -254,14 +208,6 @@ pub async unsafe extern "C" fn warp(
|
||||||
get_tx,
|
get_tx,
|
||||||
anchor_offset,
|
anchor_offset,
|
||||||
max_cost,
|
max_cost,
|
||||||
move |downloaded| {
|
|
||||||
let mut downloaded = downloaded.into_dart();
|
|
||||||
if port != 0 {
|
|
||||||
if let Some(p) = POST_COBJ {
|
|
||||||
p(port, &mut downloaded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
move |progress| {
|
move |progress| {
|
||||||
let mut progress = serde_json::to_string(&progress).unwrap().into_dart();
|
let mut progress = serde_json::to_string(&progress).unwrap().into_dart();
|
||||||
if port != 0 {
|
if port != 0 {
|
||||||
|
@ -588,7 +534,7 @@ pub unsafe extern "C" fn parse_payment_uri(uri: *mut c_char) -> CResult<*mut c_c
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn generate_random_enc_key() -> CResult<*mut c_char> {
|
pub unsafe extern "C" fn generate_random_enc_key() -> CResult<*mut c_char> {
|
||||||
to_cresult_str(crate::key::generate_random_enc_key())
|
to_cresult_str(crate::api::fullbackup::generate_random_enc_key())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -624,7 +570,7 @@ pub unsafe extern "C" fn restore_full_backup(key: *mut c_char, backup: *mut c_ch
|
||||||
pub unsafe extern "C" fn split_data(id: u32, data: *mut c_char) -> CResult<*mut c_char> {
|
pub unsafe extern "C" fn split_data(id: u32, data: *mut c_char) -> CResult<*mut c_char> {
|
||||||
from_c_str!(data);
|
from_c_str!(data);
|
||||||
let res = || {
|
let res = || {
|
||||||
let res = crate::FountainCodes::encode_into_drops(id, &base64::decode(&*data)?)?;
|
let res = crate::fountain::FountainCodes::encode_into_drops(id, &base64::decode(&*data)?)?;
|
||||||
let output = serde_json::to_string(&res)?;
|
let output = serde_json::to_string(&res)?;
|
||||||
Ok(output)
|
Ok(output)
|
||||||
};
|
};
|
||||||
|
@ -632,11 +578,10 @@ pub unsafe extern "C" fn split_data(id: u32, data: *mut c_char) -> CResult<*mut
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
// TODO: who uses this?
|
|
||||||
pub unsafe extern "C" fn merge_data(drop: *mut c_char) -> CResult<*mut c_char> {
|
pub unsafe extern "C" fn merge_data(drop: *mut c_char) -> CResult<*mut c_char> {
|
||||||
from_c_str!(drop);
|
from_c_str!(drop);
|
||||||
let res = || {
|
let res = || {
|
||||||
let res = crate::put_drop(&*drop)?
|
let res = crate::fountain::put_drop(&*drop)?
|
||||||
.map(|d| base64::encode(&d))
|
.map(|d| base64::encode(&d))
|
||||||
.unwrap_or(String::new());
|
.unwrap_or(String::new());
|
||||||
Ok::<_, anyhow::Error>(res)
|
Ok::<_, anyhow::Error>(res)
|
||||||
|
@ -650,7 +595,7 @@ pub unsafe extern "C" fn get_tx_summary(tx: *mut c_char) -> CResult<*mut c_char>
|
||||||
from_c_str!(tx);
|
from_c_str!(tx);
|
||||||
let res = || {
|
let res = || {
|
||||||
let tx: Tx = serde_json::from_str(&tx)?;
|
let tx: Tx = serde_json::from_str(&tx)?;
|
||||||
let summary = crate::get_tx_summary(&tx)?;
|
let summary = crate::pay::get_tx_summary(&tx)?;
|
||||||
let summary = serde_json::to_string(&summary)?;
|
let summary = serde_json::to_string(&summary)?;
|
||||||
Ok::<_, anyhow::Error>(summary)
|
Ok::<_, anyhow::Error>(summary)
|
||||||
};
|
};
|
||||||
|
@ -710,22 +655,22 @@ pub unsafe extern "C" fn disable_wal(db_path: *mut c_char) {
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn has_cuda() -> bool {
|
pub unsafe extern "C" fn has_cuda() -> bool {
|
||||||
crate::has_cuda()
|
crate::gpu::has_cuda()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn has_metal() -> bool {
|
pub unsafe extern "C" fn has_metal() -> bool {
|
||||||
crate::has_metal()
|
crate::gpu::has_metal()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn has_gpu() -> bool {
|
pub unsafe extern "C" fn has_gpu() -> bool {
|
||||||
crate::has_gpu()
|
crate::gpu::has_gpu()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn use_gpu(v: bool) {
|
pub unsafe extern "C" fn use_gpu(v: bool) {
|
||||||
crate::use_gpu(v)
|
crate::gpu::use_gpu(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|
|
@ -1,23 +1,37 @@
|
||||||
|
//! Save/Load account data as JSON
|
||||||
use crate::coinconfig::CoinConfig;
|
use crate::coinconfig::CoinConfig;
|
||||||
use crate::db::AccountBackup;
|
use crate::db::AccountBackup;
|
||||||
use bech32::FromBase32;
|
use bech32::{FromBase32, ToBase32, Variant};
|
||||||
use chacha20poly1305::aead::{Aead, NewAead};
|
use chacha20poly1305::aead::{Aead, NewAead};
|
||||||
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
|
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
|
||||||
|
use rand::RngCore;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
const NONCE: &[u8; 12] = b"unique nonce";
|
const NONCE: &[u8; 12] = b"unique nonce";
|
||||||
|
|
||||||
|
/// Return backup data of every account for a given coin
|
||||||
|
/// # Argument
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
pub fn get_full_backup(coin: u8) -> anyhow::Result<Vec<AccountBackup>> {
|
pub fn get_full_backup(coin: u8) -> anyhow::Result<Vec<AccountBackup>> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
db.get_full_backup(coin)
|
db.get_full_backup(coin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Import backup data for a given coin
|
||||||
|
/// # Argument
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `accounts`: list of backups
|
||||||
pub fn restore_full_backup(coin: u8, accounts: &[AccountBackup]) -> anyhow::Result<()> {
|
pub fn restore_full_backup(coin: u8, accounts: &[AccountBackup]) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
db.restore_full_backup(accounts)
|
db.restore_full_backup(accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encrypt a list of account backups
|
||||||
|
/// # Argument
|
||||||
|
/// * `accounts`: list of backups
|
||||||
|
/// * `key`: encryption key
|
||||||
pub fn encrypt_backup(accounts: &[AccountBackup], key: &str) -> anyhow::Result<String> {
|
pub fn encrypt_backup(accounts: &[AccountBackup], key: &str) -> anyhow::Result<String> {
|
||||||
let accounts_bin = bincode::serialize(&accounts)?;
|
let accounts_bin = bincode::serialize(&accounts)?;
|
||||||
let backup = if !key.is_empty() {
|
let backup = if !key.is_empty() {
|
||||||
|
@ -40,6 +54,10 @@ pub fn encrypt_backup(accounts: &[AccountBackup], key: &str) -> anyhow::Result<S
|
||||||
Ok(backup)
|
Ok(backup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decrypt a list of account backups
|
||||||
|
/// # Argument
|
||||||
|
/// * `key`: encryption key
|
||||||
|
/// * `backup`: encrypted backup
|
||||||
pub fn decrypt_backup(key: &str, backup: &str) -> anyhow::Result<Vec<AccountBackup>> {
|
pub fn decrypt_backup(key: &str, backup: &str) -> anyhow::Result<Vec<AccountBackup>> {
|
||||||
let backup = if !key.is_empty() {
|
let backup = if !key.is_empty() {
|
||||||
let (hrp, key, _) = bech32::decode(key)?;
|
let (hrp, key, _) = bech32::decode(key)?;
|
||||||
|
@ -61,3 +79,11 @@ pub fn decrypt_backup(key: &str, backup: &str) -> anyhow::Result<Vec<AccountBack
|
||||||
let accounts: Vec<AccountBackup> = bincode::deserialize(&backup)?;
|
let accounts: Vec<AccountBackup> = bincode::deserialize(&backup)?;
|
||||||
Ok(accounts)
|
Ok(accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a random encryption key
|
||||||
|
pub fn generate_random_enc_key() -> anyhow::Result<String> {
|
||||||
|
let mut key = [0u8; 32];
|
||||||
|
OsRng.fill_bytes(&mut key);
|
||||||
|
let key = bech32::encode("zwk", key.to_base32(), Variant::Bech32)?;
|
||||||
|
Ok(key)
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
|
//! Retrieve Historical Prices from coingecko
|
||||||
|
|
||||||
use crate::coinconfig::CoinConfig;
|
use crate::coinconfig::CoinConfig;
|
||||||
|
|
||||||
|
/// Retrieve historical prices
|
||||||
|
/// # Arguments
|
||||||
|
/// * `now`: current timestamp
|
||||||
|
/// * `days`: how many days to fetch
|
||||||
|
/// * `currency`: base currency
|
||||||
pub async fn sync_historical_prices(now: i64, days: u32, currency: &str) -> anyhow::Result<u32> {
|
pub async fn sync_historical_prices(now: i64, days: u32, currency: &str) -> anyhow::Result<u32> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let mut db = c.db()?;
|
let mut db = c.db()?;
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
|
//! Access to server mempool
|
||||||
|
|
||||||
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
|
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
|
||||||
use zcash_primitives::consensus::Parameters;
|
use zcash_primitives::consensus::Parameters;
|
||||||
|
use crate::api::sync::get_latest_height;
|
||||||
|
|
||||||
use crate::coinconfig::CoinConfig;
|
use crate::coinconfig::CoinConfig;
|
||||||
use crate::db::AccountData;
|
use crate::db::AccountData;
|
||||||
use crate::get_latest_height;
|
|
||||||
|
|
||||||
|
/// Scan the mempool and return the unconfirmed balance
|
||||||
pub async fn scan() -> anyhow::Result<i64> {
|
pub async fn scan() -> anyhow::Result<i64> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let AccountData { fvk, .. } = c.db()?.get_account_info(c.id_account)?;
|
let AccountData { fvk, .. } = c.db()?.get_account_info(c.id_account)?;
|
||||||
let mut client = c.connect_lwd().await?;
|
let height = get_latest_height().await?;
|
||||||
let height = get_latest_height(&mut client).await?;
|
|
||||||
let mut mempool = c.mempool.lock().unwrap();
|
let mut mempool = c.mempool.lock().unwrap();
|
||||||
let current_height = c.height;
|
let current_height = c.height;
|
||||||
if height != current_height {
|
if height != current_height {
|
||||||
|
@ -21,6 +23,7 @@ pub async fn scan() -> anyhow::Result<i64> {
|
||||||
&fvk,
|
&fvk,
|
||||||
)?
|
)?
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let mut client = c.connect_lwd().await?;
|
||||||
mempool
|
mempool
|
||||||
.update(&mut client, height, &fvk.fvk.vk.ivk())
|
.update(&mut client, height, &fvk.fvk.vk.ivk())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
|
//! Mark messages read
|
||||||
|
|
||||||
use crate::coinconfig::CoinConfig;
|
use crate::coinconfig::CoinConfig;
|
||||||
|
|
||||||
|
/// Mark a given message as read or unread
|
||||||
|
/// # Arguments
|
||||||
|
/// * `message`: message id
|
||||||
|
/// * `read`: read or unread
|
||||||
pub fn mark_message_read(message: u32, read: bool) -> anyhow::Result<()> {
|
pub fn mark_message_read(message: u32, read: bool) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
c.db()?.mark_message_read(message, read)?;
|
c.db()?.mark_message_read(message, read)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark all messages as read or unread
|
||||||
|
/// # Arguments
|
||||||
|
/// * `read`: read or unread
|
||||||
pub fn mark_all_messages_read(read: bool) -> anyhow::Result<()> {
|
pub fn mark_all_messages_read(read: bool) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
c.db()?.mark_all_messages_read(c.id_account, read)?;
|
c.db()?.mark_all_messages_read(c.id_account, read)?;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Payments
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -77,6 +79,11 @@ fn sign(tx: &Tx, progress_callback: PaymentProgressCallback) -> anyhow::Result<V
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a multi payment for offline signing
|
/// Build a multi payment for offline signing
|
||||||
|
/// # Arguments
|
||||||
|
/// * `last_height`: current block height
|
||||||
|
/// * `recipients`: list of recipients
|
||||||
|
/// * `use_transparent`: include transparent balance
|
||||||
|
/// * `anchor_offset`: minimum number of confirmations for note selection
|
||||||
pub async fn build_only_multi_payment(
|
pub async fn build_only_multi_payment(
|
||||||
last_height: u32,
|
last_height: u32,
|
||||||
recipients: &[RecipientMemo],
|
recipients: &[RecipientMemo],
|
||||||
|
@ -89,6 +96,10 @@ pub async fn build_only_multi_payment(
|
||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sign a transaction
|
||||||
|
/// # Arguments
|
||||||
|
/// * `tx`: transaction to sign
|
||||||
|
/// * `progress_callback`: function callback during transaction building
|
||||||
pub async fn sign_only_multi_payment(
|
pub async fn sign_only_multi_payment(
|
||||||
tx: &Tx,
|
tx: &Tx,
|
||||||
progress_callback: PaymentProgressCallback,
|
progress_callback: PaymentProgressCallback,
|
||||||
|
@ -99,6 +110,12 @@ pub async fn sign_only_multi_payment(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build, sign and broadcast a multi payment
|
/// Build, sign and broadcast a multi payment
|
||||||
|
/// # Arguments
|
||||||
|
/// * `last_height`: current block height
|
||||||
|
/// * `recipients`: list of recipients
|
||||||
|
/// * `use_transparent`: include transparent balance
|
||||||
|
/// * `anchor_offset`: minimum number of confirmations for note selection
|
||||||
|
/// * `progress_callback`: function callback during transaction building
|
||||||
pub async fn build_sign_send_multi_payment(
|
pub async fn build_sign_send_multi_payment(
|
||||||
last_height: u32,
|
last_height: u32,
|
||||||
recipients: &[RecipientMemo],
|
recipients: &[RecipientMemo],
|
||||||
|
@ -118,12 +135,14 @@ pub async fn build_sign_send_multi_payment(
|
||||||
Ok(tx_id)
|
Ok(tx_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make a transaction that shields the transparent balance
|
||||||
pub async fn shield_taddr() -> anyhow::Result<String> {
|
pub async fn shield_taddr() -> anyhow::Result<String> {
|
||||||
let last_height = get_latest_height().await?;
|
let last_height = get_latest_height().await?;
|
||||||
let tx_id = build_sign_send_multi_payment(last_height, &[], true, 0, Box::new(|_| {})).await?;
|
let tx_id = build_sign_send_multi_payment(last_height, &[], true, 0, Box::new(|_| {})).await?;
|
||||||
Ok(tx_id)
|
Ok(tx_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a json document that contains a list of recipients
|
||||||
pub fn parse_recipients(recipients: &str) -> anyhow::Result<Vec<RecipientMemo>> {
|
pub fn parse_recipients(recipients: &str) -> anyhow::Result<Vec<RecipientMemo>> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let AccountData { address, .. } = c.db()?.get_account_info(c.id_account)?;
|
let AccountData { address, .. } = c.db()?.get_account_info(c.id_account)?;
|
||||||
|
@ -135,12 +154,14 @@ pub fn parse_recipients(recipients: &str) -> anyhow::Result<Vec<RecipientMemo>>
|
||||||
Ok(recipient_memos)
|
Ok(recipient_memos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encode a message into a memo
|
||||||
pub fn encode_memo(from: &str, include_from: bool, subject: &str, body: &str) -> String {
|
pub fn encode_memo(from: &str, include_from: bool, subject: &str, body: &str) -> String {
|
||||||
let from = if include_from { from } else { "" };
|
let from = if include_from { from } else { "" };
|
||||||
let msg = format!("\u{1F6E1}MSG\n{}\n{}\n{}", from, subject, body);
|
let msg = format!("\u{1F6E1}MSG\n{}\n{}\n{}", from, subject, body);
|
||||||
msg
|
msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decode a memo into a message
|
||||||
pub fn decode_memo(
|
pub fn decode_memo(
|
||||||
id_tx: u32,
|
id_tx: u32,
|
||||||
memo: &str,
|
memo: &str,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! encode and decode Payment URI
|
||||||
|
|
||||||
use crate::coinconfig::CoinConfig;
|
use crate::coinconfig::CoinConfig;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -7,6 +9,11 @@ use zcash_client_backend::zip321::{Payment, TransactionRequest};
|
||||||
use zcash_primitives::memo::Memo;
|
use zcash_primitives::memo::Memo;
|
||||||
use zcash_primitives::transaction::components::Amount;
|
use zcash_primitives::transaction::components::Amount;
|
||||||
|
|
||||||
|
/// Build a payment URI
|
||||||
|
/// # Arguments
|
||||||
|
/// * `address`: recipient address
|
||||||
|
/// * `amount`: amount in zats
|
||||||
|
/// * `memo`: memo text
|
||||||
pub fn make_payment_uri(address: &str, amount: u64, memo: &str) -> anyhow::Result<String> {
|
pub fn make_payment_uri(address: &str, amount: u64, memo: &str) -> anyhow::Result<String> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let addr = RecipientAddress::decode(c.chain.network(), address)
|
let addr = RecipientAddress::decode(c.chain.network(), address)
|
||||||
|
@ -29,6 +36,9 @@ pub fn make_payment_uri(address: &str, amount: u64, memo: &str) -> anyhow::Resul
|
||||||
Ok(uri)
|
Ok(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decode a payment uri
|
||||||
|
/// # Arguments
|
||||||
|
/// * `uri`: payment uri
|
||||||
pub fn parse_payment_uri(uri: &str) -> anyhow::Result<PaymentURI> {
|
pub fn parse_payment_uri(uri: &str) -> anyhow::Result<PaymentURI> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let scheme = c.chain.ticker();
|
let scheme = c.chain.ticker();
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
// Sync
|
//! Warp Synchronize
|
||||||
|
|
||||||
use crate::coinconfig::CoinConfig;
|
use crate::coinconfig::CoinConfig;
|
||||||
use crate::db::PlainNote;
|
use crate::db::PlainNote;
|
||||||
use crate::scan::{AMProgressCallback, Progress};
|
use crate::scan::{AMProgressCallback, Progress};
|
||||||
use crate::{AccountData, BlockId, CTree, CompactTxStreamerClient, DbAdapter};
|
use crate::{AccountData, BlockId, CompactTxStreamerClient, DbAdapter};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tonic::transport::Channel;
|
use tonic::transport::Channel;
|
||||||
use tonic::Request;
|
use tonic::Request;
|
||||||
use zcash_primitives::sapling::Note;
|
use zcash_primitives::sapling::Note;
|
||||||
|
use crate::commitment::CTree;
|
||||||
|
|
||||||
const DEFAULT_CHUNK_SIZE: u32 = 100_000;
|
const DEFAULT_CHUNK_SIZE: u32 = 100_000;
|
||||||
|
|
||||||
|
/// Asynchronously perform warp sync
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
|
/// * `get_tx`: true to retrieve transaction details
|
||||||
|
/// * `anchor_offset`: minimum number of confirmations for note selection
|
||||||
|
/// * `max_cost`: tx that have a higher spending cost are excluded
|
||||||
|
/// * `progress_callback`: function callback during synchronization
|
||||||
|
/// * `cancel`: cancellation mutex, set to true to abort
|
||||||
pub async fn coin_sync(
|
pub async fn coin_sync(
|
||||||
coin: u8,
|
coin: u8,
|
||||||
get_tx: bool,
|
get_tx: bool,
|
||||||
|
@ -69,6 +78,7 @@ async fn coin_sync_impl(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the latest block height
|
||||||
pub async fn get_latest_height() -> anyhow::Result<u32> {
|
pub async fn get_latest_height() -> anyhow::Result<u32> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let mut client = c.connect_lwd().await?;
|
let mut client = c.connect_lwd().await?;
|
||||||
|
@ -76,12 +86,17 @@ pub async fn get_latest_height() -> anyhow::Result<u32> {
|
||||||
Ok(last_height)
|
Ok(last_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the latest block height synchronized
|
||||||
pub fn get_synced_height() -> anyhow::Result<u32> {
|
pub fn get_synced_height() -> anyhow::Result<u32> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let db = c.db()?;
|
let db = c.db()?;
|
||||||
db.get_last_sync_height().map(|h| h.unwrap_or(0))
|
db.get_last_sync_height().map(|h| h.unwrap_or(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Skip block synchronization and directly mark the chain synchronized
|
||||||
|
/// Used for new accounts that have no transaction history
|
||||||
|
/// # Arguments
|
||||||
|
/// * `coin`: 0 for zcash, 1 for ycash
|
||||||
pub async fn skip_to_last_height(coin: u8) -> anyhow::Result<()> {
|
pub async fn skip_to_last_height(coin: u8) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get(coin);
|
let c = CoinConfig::get(coin);
|
||||||
let mut client = c.connect_lwd().await?;
|
let mut client = c.connect_lwd().await?;
|
||||||
|
@ -90,16 +105,17 @@ pub async fn skip_to_last_height(coin: u8) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// if exact = true, do not snap the height to a pre existing checkpoint
|
/// Rewind to a previous block height
|
||||||
// and get the tree_state from the server
|
///
|
||||||
// this option is used when we rescan from a given height
|
/// Height is snapped to a closest earlier checkpoint.
|
||||||
// We ignore any transaction that occurred before
|
/// The effective height is returned
|
||||||
pub async fn rewind_to(height: u32) -> anyhow::Result<u32> {
|
pub async fn rewind_to(height: u32) -> anyhow::Result<u32> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let height = c.db()?.trim_to_height(height)?;
|
let height = c.db()?.trim_to_height(height)?;
|
||||||
Ok(height)
|
Ok(height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Synchronize from a given height
|
||||||
pub async fn rescan_from(height: u32) -> anyhow::Result<()> {
|
pub async fn rescan_from(height: u32) -> anyhow::Result<()> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
c.db()?.truncate_sync_data()?;
|
c.db()?.truncate_sync_data()?;
|
||||||
|
@ -129,6 +145,7 @@ async fn fetch_and_store_tree_state(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the date of sapling activation
|
||||||
pub async fn get_activation_date() -> anyhow::Result<u32> {
|
pub async fn get_activation_date() -> anyhow::Result<u32> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let mut client = c.connect_lwd().await?;
|
let mut client = c.connect_lwd().await?;
|
||||||
|
@ -136,6 +153,9 @@ pub async fn get_activation_date() -> anyhow::Result<u32> {
|
||||||
Ok(date_time)
|
Ok(date_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the block height for a given timestamp
|
||||||
|
/// # Arguments
|
||||||
|
/// * `time`: seconds since epoch
|
||||||
pub async fn get_block_by_time(time: u32) -> anyhow::Result<u32> {
|
pub async fn get_block_by_time(time: u32) -> anyhow::Result<u32> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let mut client = c.connect_lwd().await?;
|
let mut client = c.connect_lwd().await?;
|
||||||
|
@ -143,7 +163,7 @@ pub async fn get_block_by_time(time: u32) -> anyhow::Result<u32> {
|
||||||
Ok(date_time)
|
Ok(date_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trial_decrypt(
|
fn trial_decrypt(
|
||||||
height: u32,
|
height: u32,
|
||||||
cmu: &[u8],
|
cmu: &[u8],
|
||||||
epk: &[u8],
|
epk: &[u8],
|
||||||
|
|
11
src/chain.rs
11
src/chain.rs
|
@ -3,10 +3,9 @@ use crate::db::AccountViewKey;
|
||||||
use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
||||||
use crate::lw_rpc::*;
|
use crate::lw_rpc::*;
|
||||||
use crate::scan::Blocks;
|
use crate::scan::Blocks;
|
||||||
use crate::{advance_tree, has_cuda};
|
use crate::builder::advance_tree;
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
use futures::{future, FutureExt};
|
use futures::{future, FutureExt};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use rand::prelude::SliceRandom;
|
use rand::prelude::SliceRandom;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
@ -470,7 +469,7 @@ impl DecryptNode {
|
||||||
if blocks.is_empty() {
|
if blocks.is_empty() {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
if has_cuda() {
|
if crate::gpu::has_cuda() {
|
||||||
let processor = CudaProcessor::setup_decrypt(network, blocks).unwrap();
|
let processor = CudaProcessor::setup_decrypt(network, blocks).unwrap();
|
||||||
return trial_decrypt(processor, self.vks.iter()).unwrap();
|
return trial_decrypt(processor, self.vks.iter()).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -546,7 +545,8 @@ fn calculate_tree_state_v1(
|
||||||
witnesses
|
witnesses
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_tree_state_v2(cbs: &[CompactBlock], blocks: &[DecryptedBlock]) -> Vec<Witness> {
|
#[allow(dead_code)]
|
||||||
|
fn calculate_tree_state_v2(cbs: &[CompactBlock], blocks: &[DecryptedBlock]) -> Vec<Witness> {
|
||||||
let mut p = 0usize;
|
let mut p = 0usize;
|
||||||
let mut nodes: Vec<Node> = vec![];
|
let mut nodes: Vec<Node> = vec![];
|
||||||
let mut positions: Vec<usize> = vec![];
|
let mut positions: Vec<usize> = vec![];
|
||||||
|
@ -593,6 +593,7 @@ pub fn calculate_tree_state_v2(cbs: &[CompactBlock], blocks: &[DecryptedBlock])
|
||||||
new_witnesses
|
new_witnesses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Connect to a lightwalletd server
|
||||||
pub async fn connect_lightwalletd(url: &str) -> anyhow::Result<CompactTxStreamerClient<Channel>> {
|
pub async fn connect_lightwalletd(url: &str) -> anyhow::Result<CompactTxStreamerClient<Channel>> {
|
||||||
log::info!("LWD URL: {}", url);
|
log::info!("LWD URL: {}", url);
|
||||||
let mut channel = tonic::transport::Channel::from_shared(url.to_owned())?;
|
let mut channel = tonic::transport::Channel::from_shared(url.to_owned())?;
|
||||||
|
@ -613,6 +614,8 @@ async fn get_height(server: String) -> Option<(String, u32)> {
|
||||||
Some((server, height))
|
Some((server, height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the URL of the best server given a list of servers
|
||||||
|
/// The best server is the one that has the highest height
|
||||||
pub async fn get_best_server(servers: &[String]) -> Option<String> {
|
pub async fn get_best_server(servers: &[String]) -> Option<String> {
|
||||||
let mut server_heights = vec![];
|
let mut server_heights = vec![];
|
||||||
for s in servers.iter() {
|
for s in servers.iter() {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter, FountainCodes, MemPool};
|
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter};
|
||||||
|
use crate::fountain::FountainCodes;
|
||||||
|
use crate::mempool::MemPool;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use lazycell::AtomicLazyCell;
|
use lazycell::AtomicLazyCell;
|
||||||
|
@ -21,10 +23,12 @@ lazy_static! {
|
||||||
|
|
||||||
pub static ACTIVE_COIN: AtomicU8 = AtomicU8::new(0);
|
pub static ACTIVE_COIN: AtomicU8 = AtomicU8::new(0);
|
||||||
|
|
||||||
|
/// Set the active coin
|
||||||
pub fn set_active(active: u8) {
|
pub fn set_active(active: u8) {
|
||||||
ACTIVE_COIN.store(active, Ordering::Release);
|
ACTIVE_COIN.store(active, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the active account for a given coin
|
||||||
pub fn set_active_account(coin: u8, id: u32) {
|
pub fn set_active_account(coin: u8, id: u32) {
|
||||||
let mempool = {
|
let mempool = {
|
||||||
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
||||||
|
@ -35,16 +39,19 @@ pub fn set_active_account(coin: u8, id: u32) {
|
||||||
let _ = mempool.clear();
|
let _ = mempool.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the lightwalletd url for a given coin
|
||||||
pub fn set_coin_lwd_url(coin: u8, lwd_url: &str) {
|
pub fn set_coin_lwd_url(coin: u8, lwd_url: &str) {
|
||||||
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
||||||
c.lwd_url = Some(lwd_url.to_string());
|
c.lwd_url = Some(lwd_url.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the URL of the lightwalletd server for a given coin
|
||||||
pub fn get_coin_lwd_url(coin: u8) -> String {
|
pub fn get_coin_lwd_url(coin: u8) -> String {
|
||||||
let c = COIN_CONFIG[coin as usize].lock().unwrap();
|
let c = COIN_CONFIG[coin as usize].lock().unwrap();
|
||||||
c.lwd_url.clone().unwrap_or_default()
|
c.lwd_url.clone().unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize a coin with a database path
|
||||||
pub fn init_coin(coin: u8, db_path: &str) -> anyhow::Result<()> {
|
pub fn init_coin(coin: u8, db_path: &str) -> anyhow::Result<()> {
|
||||||
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
||||||
c.set_db_path(db_path)?;
|
c.set_db_path(db_path)?;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::contact::Contact;
|
||||||
use crate::prices::Quote;
|
use crate::prices::Quote;
|
||||||
use crate::taddr::{derive_tkeys, TBalance};
|
use crate::taddr::{derive_tkeys, TBalance};
|
||||||
use crate::transaction::TransactionInfo;
|
use crate::transaction::TransactionInfo;
|
||||||
use crate::{CTree, Witness};
|
use crate::commitment::{CTree, Witness};
|
||||||
use rusqlite::Error::QueryReturnedNoRows;
|
use rusqlite::Error::QueryReturnedNoRows;
|
||||||
use rusqlite::{params, Connection, OptionalExtension, Transaction};
|
use rusqlite::{params, Connection, OptionalExtension, Transaction};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -1148,7 +1148,7 @@ pub struct AccountData {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::db::{DbAdapter, ReceivedNote, DEFAULT_DB_PATH};
|
use crate::db::{DbAdapter, ReceivedNote, DEFAULT_DB_PATH};
|
||||||
use crate::{CTree, Witness};
|
use crate::commitment::{CTree, Witness};
|
||||||
use zcash_params::coin::CoinType;
|
use zcash_params::coin::CoinType;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1157,8 +1157,8 @@ mod tests {
|
||||||
db.init_db().unwrap();
|
db.init_db().unwrap();
|
||||||
db.trim_to_height(0).unwrap();
|
db.trim_to_height(0).unwrap();
|
||||||
|
|
||||||
db.store_block(1, &[0u8; 32], 0, &CTree::new()).unwrap();
|
|
||||||
let db_tx = db.begin_transaction().unwrap();
|
let db_tx = db.begin_transaction().unwrap();
|
||||||
|
DbAdapter::store_block(&db_tx, 1, &[0u8; 32], 0, &CTree::new()).unwrap();
|
||||||
let id_tx = DbAdapter::store_transaction(&[0; 32], 1, 1, 0, 20, &db_tx).unwrap();
|
let id_tx = DbAdapter::store_transaction(&[0; 32], 1, 1, 0, 20, &db_tx).unwrap();
|
||||||
DbAdapter::store_received_note(
|
DbAdapter::store_received_note(
|
||||||
&ReceivedNote {
|
&ReceivedNote {
|
||||||
|
@ -1184,8 +1184,8 @@ mod tests {
|
||||||
filled: vec![],
|
filled: vec![],
|
||||||
cursor: CTree::new(),
|
cursor: CTree::new(),
|
||||||
};
|
};
|
||||||
|
DbAdapter::store_witnesses(&db_tx, &witness, 1000, 1).unwrap();
|
||||||
db_tx.commit().unwrap();
|
db_tx.commit().unwrap();
|
||||||
Db::store_witnesses(&witness, 1000, 1).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::chain::DecryptedBlock;
|
use crate::chain::DecryptedBlock;
|
||||||
use crate::gpu::{collect_nf, GPUProcessor};
|
use crate::gpu::{collect_nf, GPUProcessor};
|
||||||
use crate::lw_rpc::CompactBlock;
|
use crate::lw_rpc::CompactBlock;
|
||||||
use crate::{Hash, GENERATORS_EXP};
|
use crate::{Hash, hash::GENERATORS_EXP};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ff::BatchInverter;
|
use ff::BatchInverter;
|
||||||
use jubjub::Fq;
|
use jubjub::Fq;
|
||||||
|
|
|
@ -112,10 +112,3 @@ impl KeyHelpers {
|
||||||
recipient.is_some()
|
recipient.is_some()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_random_enc_key() -> anyhow::Result<String> {
|
|
||||||
let mut key = [0u8; 32];
|
|
||||||
OsRng.fill_bytes(&mut key);
|
|
||||||
let key = bech32::encode("zwk", key.to_base32(), Variant::Bech32)?;
|
|
||||||
Ok(key)
|
|
||||||
}
|
|
||||||
|
|
86
src/lib.rs
86
src/lib.rs
|
@ -1,12 +1,65 @@
|
||||||
// #![allow(dead_code)]
|
// #![allow(dead_code)]
|
||||||
// #![allow(unused_imports)]
|
// #![allow(unused_imports)]
|
||||||
|
// #![warn(missing_docs)]
|
||||||
|
|
||||||
|
//! A library for fast synchronization of y/zcash blockchain
|
||||||
|
//!
|
||||||
|
//! - Implements the warp sync algorithm for sapling
|
||||||
|
//! - Multi Account management
|
||||||
|
|
||||||
|
//! # Example
|
||||||
|
//! ```rust
|
||||||
|
//! use warp_api_ffi::api::account::{get_backup, new_account};
|
||||||
|
//! use warp_api_ffi::api::sync::coin_sync;
|
||||||
|
//! use warp_api_ffi::{CoinConfig, init_coin, set_coin_lwd_url};
|
||||||
|
//! use lazy_static::lazy_static;
|
||||||
|
//! use std::sync::Mutex;
|
||||||
|
//!
|
||||||
|
//! lazy_static! {
|
||||||
|
//! static ref CANCEL: Mutex<bool> = Mutex::new(false);
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! const FVK: &str = "zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz";
|
||||||
|
//!
|
||||||
|
//! #[tokio::main]
|
||||||
|
//! async fn main() {
|
||||||
|
//! env_logger::init();
|
||||||
|
//!
|
||||||
|
//! // Initialize the library for Zcash (coin = 0)
|
||||||
|
//! init_coin(0, "./zec.db").unwrap();
|
||||||
|
//! set_coin_lwd_url(0, "https://lwdv3.zecwallet.co:443"); // ZecWallet Lightwalletd URL
|
||||||
|
//!
|
||||||
|
//! // Create a new account with the ZEC pages viewing key
|
||||||
|
//! let id_account = new_account(0, "test_account", Some(FVK.to_string()),
|
||||||
|
//! None).unwrap();
|
||||||
|
//!
|
||||||
|
//! // Synchronize
|
||||||
|
//! coin_sync(0 /* zcash */,
|
||||||
|
//! true /* retrieve tx details */,
|
||||||
|
//! 0 /* sync to tip */,
|
||||||
|
//! 100 /* spam filter threshold */, |p| {
|
||||||
|
//! log::info!("Progress: {}", p.height);
|
||||||
|
//! }, &CANCEL).await.unwrap();
|
||||||
|
//!
|
||||||
|
//! // Grab the database accessor
|
||||||
|
//! let cc = &CoinConfig::get(0 /* zcash */);
|
||||||
|
//! let db = cc.db.as_ref().unwrap().clone();
|
||||||
|
//! let db = db.lock().unwrap();
|
||||||
|
//!
|
||||||
|
//! // Query the account balance
|
||||||
|
//! let balance = db.get_balance(id_account).unwrap();
|
||||||
|
//!
|
||||||
|
//! println!("Balance = {}", balance)
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
#[path = "generated/cash.z.wallet.sdk.rpc.rs"]
|
#[path = "generated/cash.z.wallet.sdk.rpc.rs"]
|
||||||
pub mod lw_rpc;
|
pub mod lw_rpc;
|
||||||
|
|
||||||
pub use zcash_params::coin::{get_branch, get_coin_type, CoinType};
|
use zcash_params::coin::{get_branch, get_coin_type, CoinType};
|
||||||
|
|
||||||
// Mainnet
|
// Mainnet
|
||||||
pub const LWD_URL: &str = "https://mainnet.lightwalletd.com:9067";
|
const LWD_URL: &str = "https://mainnet.lightwalletd.com:9067";
|
||||||
// pub const LWD_URL: &str = "https://lwdv3.zecwallet.co";
|
// pub const LWD_URL: &str = "https://lwdv3.zecwallet.co";
|
||||||
// pub const LWD_URL: &str = "http://lwd.hanh.me:9067";
|
// pub const LWD_URL: &str = "http://lwd.hanh.me:9067";
|
||||||
// pub const LWD_URL: &str = "http://127.0.0.1:9067";
|
// pub const LWD_URL: &str = "http://127.0.0.1:9067";
|
||||||
|
@ -40,6 +93,7 @@ mod transaction;
|
||||||
mod ua;
|
mod ua;
|
||||||
mod zip32;
|
mod zip32;
|
||||||
// mod wallet;
|
// mod wallet;
|
||||||
|
/// accounts, sync, payments, etc.
|
||||||
pub mod api;
|
pub mod api;
|
||||||
|
|
||||||
#[cfg(feature = "ledger")]
|
#[cfg(feature = "ledger")]
|
||||||
|
@ -56,34 +110,21 @@ mod ledger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hex_to_hash(hex: &str) -> anyhow::Result<[u8; 32]> {
|
|
||||||
let mut hash = [0u8; 32];
|
|
||||||
hex::decode_to_slice(hex, &mut hash)?;
|
|
||||||
Ok(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use crate::builder::advance_tree;
|
|
||||||
pub use crate::chain::{
|
pub use crate::chain::{
|
||||||
calculate_tree_state_v2, connect_lightwalletd, download_chain, get_best_server,
|
connect_lightwalletd, get_best_server,
|
||||||
get_latest_height, ChainError, DecryptNode,
|
ChainError,
|
||||||
};
|
};
|
||||||
pub use crate::coinconfig::{
|
pub use crate::coinconfig::{
|
||||||
init_coin, set_active, set_active_account, set_coin_lwd_url, CoinConfig,
|
init_coin, set_active, set_active_account, set_coin_lwd_url, CoinConfig,
|
||||||
};
|
};
|
||||||
pub use crate::commitment::{CTree, Witness};
|
|
||||||
pub use crate::db::{AccountData, AccountInfo, AccountRec, DbAdapter, TxRec};
|
pub use crate::db::{AccountData, AccountInfo, AccountRec, DbAdapter, TxRec};
|
||||||
pub use crate::fountain::{put_drop, FountainCodes, RaptorQDrops};
|
// pub use crate::fountain::FountainCodes;
|
||||||
pub use crate::hash::{pedersen_hash, Hash, GENERATORS_EXP};
|
pub use crate::hash::Hash;
|
||||||
pub use crate::key::{generate_random_enc_key, KeyHelpers};
|
pub use crate::key::KeyHelpers;
|
||||||
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
||||||
pub use crate::lw_rpc::*;
|
pub use crate::lw_rpc::*;
|
||||||
pub use crate::mempool::MemPool;
|
pub use crate::pay::{broadcast_tx, Tx, TxIn, TxOut};
|
||||||
pub use crate::misc::read_zwl;
|
pub use zip32::KeyPack;
|
||||||
pub use crate::pay::{broadcast_tx, get_tx_summary, Tx, TxIn, TxOut};
|
|
||||||
pub use crate::print::*;
|
|
||||||
pub use crate::scan::{latest_height, sync_async};
|
|
||||||
pub use crate::ua::{get_sapling, get_ua};
|
|
||||||
pub use zip32::{derive_zip32, KeyPack};
|
|
||||||
// pub use crate::wallet::{decrypt_backup, encrypt_backup, RecipientMemo, Wallet, WalletBalance};
|
// pub use crate::wallet::{decrypt_backup, encrypt_backup, RecipientMemo, Wallet, WalletBalance};
|
||||||
|
|
||||||
#[cfg(feature = "ledger_sapling")]
|
#[cfg(feature = "ledger_sapling")]
|
||||||
|
@ -97,4 +138,3 @@ pub mod nodejs;
|
||||||
|
|
||||||
mod gpu;
|
mod gpu;
|
||||||
|
|
||||||
pub use gpu::{has_cuda, has_gpu, has_metal, use_gpu};
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::db::SpendableNote;
|
||||||
// use crate::wallet::RecipientMemo;
|
// use crate::wallet::RecipientMemo;
|
||||||
use crate::api::payment::RecipientMemo;
|
use crate::api::payment::RecipientMemo;
|
||||||
use crate::coinconfig::CoinConfig;
|
use crate::coinconfig::CoinConfig;
|
||||||
use crate::{get_latest_height, hex_to_hash, GetAddressUtxosReply, RawTransaction};
|
use crate::{GetAddressUtxosReply, Hash, RawTransaction};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use jubjub::Fr;
|
use jubjub::Fr;
|
||||||
use rand::prelude::SliceRandom;
|
use rand::prelude::SliceRandom;
|
||||||
|
@ -28,6 +28,7 @@ use zcash_primitives::transaction::builder::{Builder, Progress};
|
||||||
use zcash_primitives::transaction::components::amount::{DEFAULT_FEE, MAX_MONEY};
|
use zcash_primitives::transaction::components::amount::{DEFAULT_FEE, MAX_MONEY};
|
||||||
use zcash_primitives::transaction::components::{Amount, OutPoint, TxOut as ZTxOut};
|
use zcash_primitives::transaction::components::{Amount, OutPoint, TxOut as ZTxOut};
|
||||||
use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey};
|
use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey};
|
||||||
|
use crate::chain::get_latest_height;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Tx {
|
pub struct Tx {
|
||||||
|
@ -318,7 +319,7 @@ impl Tx {
|
||||||
let mut builder = Builder::new(*chain.network(), last_height);
|
let mut builder = Builder::new(*chain.network(), last_height);
|
||||||
let efvk = ExtendedFullViewingKey::from(zsk);
|
let efvk = ExtendedFullViewingKey::from(zsk);
|
||||||
|
|
||||||
let ovk = hex_to_hash(&self.ovk)?;
|
let ovk: Hash = hex::decode(&self.ovk)?.try_into().unwrap();
|
||||||
builder.send_change_to(
|
builder.send_change_to(
|
||||||
OutgoingViewingKey(ovk),
|
OutgoingViewingKey(ovk),
|
||||||
decode_payment_address(chain.network().hrp_sapling_payment_address(), &self.change)
|
decode_payment_address(chain.network().hrp_sapling_payment_address(), &self.change)
|
||||||
|
@ -406,6 +407,7 @@ impl Tx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Broadcast a raw signed transaction to the network
|
||||||
pub async fn broadcast_tx(tx: &[u8]) -> anyhow::Result<String> {
|
pub async fn broadcast_tx(tx: &[u8]) -> anyhow::Result<String> {
|
||||||
let c = CoinConfig::get_active();
|
let c = CoinConfig::get_active();
|
||||||
let mut client = c.connect_lwd().await?;
|
let mut client = c.connect_lwd().await?;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{CTree, Witness};
|
use crate::commitment::{CTree, Witness};
|
||||||
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
|
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
|
||||||
use zcash_primitives::sapling::Node;
|
use zcash_primitives::sapling::Node;
|
||||||
|
|
||||||
|
|
17
src/scan.rs
17
src/scan.rs
|
@ -1,14 +1,16 @@
|
||||||
use crate::builder::BlockProcessor;
|
use crate::builder::BlockProcessor;
|
||||||
use crate::chain::{DecryptedBlock, Nf, NfRef};
|
use crate::chain::{DecryptedBlock, get_latest_height, Nf, NfRef};
|
||||||
use crate::db::{AccountViewKey, DbAdapter, PlainNote, ReceivedNote};
|
use crate::db::{AccountViewKey, DbAdapter, PlainNote, ReceivedNote};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use crate::transaction::retrieve_tx_info;
|
use crate::transaction::retrieve_tx_info;
|
||||||
use crate::{
|
use crate::{
|
||||||
connect_lightwalletd, download_chain, get_latest_height, CompactBlock, CompactSaplingOutput,
|
connect_lightwalletd, CompactBlock, CompactSaplingOutput,
|
||||||
CompactTx, DecryptNode, Witness,
|
CompactTx
|
||||||
};
|
};
|
||||||
|
use crate::chain::{DecryptNode, download_chain};
|
||||||
|
use crate::commitment::Witness;
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
@ -43,9 +45,9 @@ impl std::fmt::Debug for Blocks {
|
||||||
|
|
||||||
#[derive(Clone, Serialize)]
|
#[derive(Clone, Serialize)]
|
||||||
pub struct Progress {
|
pub struct Progress {
|
||||||
height: u32,
|
pub height: u32,
|
||||||
trial_decryptions: u64,
|
pub trial_decryptions: u64,
|
||||||
downloaded: usize,
|
pub downloaded: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ProgressCallback = dyn Fn(Progress) + Send;
|
pub type ProgressCallback = dyn Fn(Progress) + Send;
|
||||||
|
@ -326,7 +328,8 @@ pub async fn sync_async(
|
||||||
db_transaction.commit()?;
|
db_transaction.commit()?;
|
||||||
// db_transaction is dropped here
|
// db_transaction is dropped here
|
||||||
}
|
}
|
||||||
log::info!("progress: {}", dec_block.height);
|
progress.height = dec_block.height;
|
||||||
|
log::info!("progress: {}", progress.height);
|
||||||
let callback = proc_callback.lock().await;
|
let callback = proc_callback.lock().await;
|
||||||
callback(progress.clone());
|
callback(progress.clone());
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,7 +292,8 @@ mod tests {
|
||||||
nf_map.insert((nf.0, nf.2.clone()), nf.1);
|
nf_map.insert((nf.0, nf.2.clone()), nf.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let fvk = db.get_ivk(account).unwrap();
|
let account = db.get_account_info(account).unwrap();
|
||||||
|
let fvk = account.fvk.clone();
|
||||||
let fvk = decode_extended_full_viewing_key(
|
let fvk = decode_extended_full_viewing_key(
|
||||||
Network::MainNetwork.hrp_sapling_extended_full_viewing_key(),
|
Network::MainNetwork.hrp_sapling_extended_full_viewing_key(),
|
||||||
&fvk,
|
&fvk,
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl FromAddress for MyReceiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ua(_sapling_addr: &str, _transparent_addr: &str) -> anyhow::Result<ZcashAddress> {
|
fn get_ua(_sapling_addr: &str, _transparent_addr: &str) -> anyhow::Result<ZcashAddress> {
|
||||||
todo!()
|
todo!()
|
||||||
// let sapling_addr = ZcashAddress::try_from_encoded(sapling_addr)?;
|
// let sapling_addr = ZcashAddress::try_from_encoded(sapling_addr)?;
|
||||||
// let transparent_addr = ZcashAddress::try_from_encoded(transparent_addr)?;
|
// let transparent_addr = ZcashAddress::try_from_encoded(transparent_addr)?;
|
||||||
|
@ -53,7 +53,7 @@ pub fn get_ua(_sapling_addr: &str, _transparent_addr: &str) -> anyhow::Result<Zc
|
||||||
// Ok(ua_address)
|
// Ok(ua_address)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sapling(ua_addr: &str) -> anyhow::Result<ZcashAddress> {
|
fn get_sapling(ua_addr: &str) -> anyhow::Result<ZcashAddress> {
|
||||||
let ua_addr = ZcashAddress::try_from_encoded(ua_addr)?;
|
let ua_addr = ZcashAddress::try_from_encoded(ua_addr)?;
|
||||||
let r = ua_addr.convert::<MyReceiver>()?;
|
let r = ua_addr.convert::<MyReceiver>()?;
|
||||||
if let Receiver::Sapling(data) = r.receiver {
|
if let Receiver::Sapling(data) = r.receiver {
|
||||||
|
@ -64,7 +64,7 @@ pub fn get_sapling(ua_addr: &str) -> anyhow::Result<ZcashAddress> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::ua::{get_sapling, get_ua};
|
use super::{get_sapling, get_ua};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ua() -> anyhow::Result<()> {
|
fn test_ua() -> anyhow::Result<()> {
|
||||||
|
|
Loading…
Reference in New Issue