This commit is contained in:
Hanh 2021-07-09 21:33:05 +08:00
parent 7d40ad28c6
commit cb7a2e04b9
9 changed files with 257 additions and 32 deletions

View File

@ -38,6 +38,10 @@ bls12_381 = "^0.4.0"
ff = "^0.9" ff = "^0.9"
group = "^0.9" group = "^0.9"
byteorder = "^1.4" byteorder = "^1.4"
secp256k1 = "0.20.2"
tiny-hderive = "0.3.0"
ripemd160 = "0.9.1"
sha2 = "0.9.5"
# librustzcash synced to 35023ed8ca2fb1061e78fd740b640d4eefcc5edd # librustzcash synced to 35023ed8ca2fb1061e78fd740b640d4eefcc5edd
@ -46,6 +50,7 @@ path = "/home/hanh/projects/librustzcash/zcash_client_backend"
[dependencies.zcash_primitives] [dependencies.zcash_primitives]
path = "/home/hanh/projects/librustzcash/zcash_primitives" path = "/home/hanh/projects/librustzcash/zcash_primitives"
features = [ "transparent-inputs" ]
[dependencies.zcash_proofs] [dependencies.zcash_proofs]
path = "/home/hanh/projects/librustzcash/zcash_proofs" path = "/home/hanh/projects/librustzcash/zcash_proofs"

View File

@ -1,7 +1,7 @@
use crate::commitment::{CTree, Witness}; use crate::commitment::{CTree, Witness};
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::{advance_tree, NETWORK, LWD_URL}; use crate::{advance_tree, NETWORK};
use ff::PrimeField; use ff::PrimeField;
use group::GroupEncoding; use group::GroupEncoding;
use log::info; use log::info;
@ -316,9 +316,9 @@ pub fn calculate_tree_state_v2(cbs: &[CompactBlock], blocks: &[DecryptedBlock])
new_witnesses new_witnesses
} }
pub async fn connect_lightwalletd() -> anyhow::Result<CompactTxStreamerClient<Channel>> { pub async fn connect_lightwalletd(url: &str) -> anyhow::Result<CompactTxStreamerClient<Channel>> {
let mut channel = tonic::transport::Channel::from_shared(LWD_URL)?; let mut channel = tonic::transport::Channel::from_shared(url.to_owned())?;
if LWD_URL.starts_with("https") { if url.starts_with("https") {
let pem = include_bytes!("ca.pem"); let pem = include_bytes!("ca.pem");
let ca = Certificate::from_pem(pem); let ca = Certificate::from_pem(pem);
let tls = ClientTlsConfig::new().ca_certificate(ca); let tls = ClientTlsConfig::new().ca_certificate(ca);
@ -328,7 +328,7 @@ pub async fn connect_lightwalletd() -> anyhow::Result<CompactTxStreamerClient<Ch
Ok(client) Ok(client)
} }
pub async fn sync(fvks: &HashMap<u32, String>) -> anyhow::Result<()> { pub async fn sync(fvks: &HashMap<u32, String>, ld_url: &str) -> anyhow::Result<()> {
let fvks: HashMap<_, _> = fvks.iter().map(|(&account, fvk)| { let fvks: HashMap<_, _> = fvks.iter().map(|(&account, fvk)| {
let fvk = let fvk =
decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &fvk) decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &fvk)
@ -337,7 +337,7 @@ pub async fn sync(fvks: &HashMap<u32, String>) -> anyhow::Result<()> {
(account, fvk) (account, fvk)
}).collect(); }).collect();
let decrypter = DecryptNode::new(fvks); let decrypter = DecryptNode::new(fvks);
let mut client = connect_lightwalletd().await?; let mut client = connect_lightwalletd(ld_url).await?;
let start_height: u32 = crate::NETWORK let start_height: u32 = crate::NETWORK
.activation_height(NetworkUpgrade::Sapling) .activation_height(NetworkUpgrade::Sapling)
.unwrap() .unwrap()

View File

@ -6,6 +6,7 @@ use zcash_primitives::consensus::{NetworkUpgrade, Parameters};
use zcash_primitives::merkle_tree::IncrementalWitness; use zcash_primitives::merkle_tree::IncrementalWitness;
use zcash_primitives::sapling::{Diversifier, Node, Note, Rseed}; use zcash_primitives::sapling::{Diversifier, Node, Note, Rseed};
use zcash_primitives::zip32::{ExtendedFullViewingKey, DiversifierIndex}; use zcash_primitives::zip32::{ExtendedFullViewingKey, DiversifierIndex};
use crate::taddr::{derive_tkeys, BIP44_PATH};
#[allow(dead_code)] #[allow(dead_code)]
pub const DEFAULT_DB_PATH: &str = "zec.db"; pub const DEFAULT_DB_PATH: &str = "zec.db";
@ -105,6 +106,14 @@ impl DbAdapter {
NO_PARAMS, NO_PARAMS,
)?; )?;
self.connection.execute(
"CREATE TABLE IF NOT EXISTS taddrs (
account INTEGER PRIMARY KEY NOT NULL,
sk TEXT NOT NULL,
address TEXT NOT NULL)",
NO_PARAMS,
)?;
Ok(()) Ok(())
} }
@ -447,6 +456,20 @@ impl DbAdapter {
Ok((seed, sk, ivk)) Ok((seed, sk, ivk))
} }
pub fn get_seed(&self, account: u32) -> anyhow::Result<Option<String>> {
log::info!("+get_seed");
let seed = self.connection.query_row(
"SELECT seed FROM accounts WHERE id_account = ?1",
params![account],
|row| {
let sk: String = row.get(0)?;
Ok(sk)
},
).optional()?;
log::info!("-get_seed");
Ok(seed)
}
pub fn get_sk(&self, account: u32) -> anyhow::Result<String> { pub fn get_sk(&self, account: u32) -> anyhow::Result<String> {
log::info!("+get_sk"); log::info!("+get_sk");
let sk = self.connection.query_row( let sk = self.connection.query_row(
@ -475,6 +498,20 @@ impl DbAdapter {
Ok(ivk) Ok(ivk)
} }
pub fn get_address(&self, account: u32) -> anyhow::Result<String> {
log::debug!("+get_address");
let address = self.connection.query_row(
"SELECT address FROM accounts WHERE id_account = ?1",
params![account],
|row| {
let address: String = row.get(0)?;
Ok(address)
},
)?;
log::debug!("-get_address");
Ok(address)
}
pub fn get_diversifier(&self, account: u32) -> anyhow::Result<DiversifierIndex> { pub fn get_diversifier(&self, account: u32) -> anyhow::Result<DiversifierIndex> {
let diversifier_index = self.connection.query_row( let diversifier_index = self.connection.query_row(
"SELECT diversifier_index FROM diversifiers WHERE account = ?1", "SELECT diversifier_index FROM diversifiers WHERE account = ?1",
@ -484,7 +521,7 @@ impl DbAdapter {
let mut div = [0u8; 11]; let mut div = [0u8; 11];
div.copy_from_slice(&d); div.copy_from_slice(&d);
Ok(div) Ok(div)
} },
).optional()?.unwrap_or_else(|| [0u8; 11]); ).optional()?.unwrap_or_else(|| [0u8; 11]);
Ok(DiversifierIndex(diversifier_index)) Ok(DiversifierIndex(diversifier_index))
} }
@ -497,6 +534,38 @@ impl DbAdapter {
params![account, diversifier_bytes])?; params![account, diversifier_bytes])?;
Ok(()) Ok(())
} }
pub fn get_taddr(&self, account: u32) -> anyhow::Result<Option<String>> {
let address = self.connection.query_row(
"SELECT address FROM taddrs WHERE account = ?1",
params![account],
|row| {
let address: String = row.get(0)?;
Ok(address)
}).optional()?;
Ok(address)
}
pub fn get_tsk(&self, account: u32) -> anyhow::Result<String> {
let sk = self.connection.query_row(
"SELECT sk FROM taddrs WHERE account = ?1",
params![account],
|row| {
let address: String = row.get(0)?;
Ok(address)
})?;
Ok(sk)
}
pub fn create_taddr(&self, account: u32) -> anyhow::Result<()> {
let seed = self.get_seed(account)?;
if let Some(seed) = seed {
let (sk, address) = derive_tkeys(&seed, BIP44_PATH)?;
self.connection.execute("INSERT INTO taddrs(account, sk, address) VALUES (?1, ?2, ?3) \
ON CONFLICT DO NOTHING", params![account, &sk, &address])?;
}
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -22,6 +22,7 @@ mod key;
mod mempool; mod mempool;
mod print; mod print;
mod scan; mod scan;
mod taddr;
mod wallet; mod wallet;
pub use crate::builder::advance_tree; pub use crate::builder::advance_tree;

View File

@ -1,7 +1,7 @@
use bip39::{Language, Mnemonic}; use bip39::{Language, Mnemonic};
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::RngCore; use rand::RngCore;
use sync::{DbAdapter, Wallet, ChainError, Witness, print_witness2}; use sync::{DbAdapter, Wallet, ChainError, Witness, print_witness2, LWD_URL};
use rusqlite::NO_PARAMS; use rusqlite::NO_PARAMS;
const DB_NAME: &str = "zec.db"; const DB_NAME: &str = "zec.db";
@ -22,7 +22,7 @@ async fn test() -> anyhow::Result<()> {
let progress = |height| { let progress = |height| {
log::info!("Height = {}", height); log::info!("Height = {}", height);
}; };
let wallet = Wallet::new(DB_NAME); let wallet = Wallet::new(DB_NAME, LWD_URL);
wallet.new_account_with_key("test", &seed).unwrap(); wallet.new_account_with_key("test", &seed).unwrap();
let res = wallet.sync(progress).await; let res = wallet.sync(progress).await;
if let Err(err) = res { if let Err(err) = res {
@ -34,7 +34,7 @@ async fn test() -> anyhow::Result<()> {
} }
} }
let tx_id = wallet let tx_id = wallet
.send_payment(1, &address, 50000, u64::max_value(), move |progress| { println!("{}", progress.cur()); }) .send_payment(1, &address, 50000, u64::max_value(), 2, move |progress| { println!("{}", progress.cur()); })
.await .await
.unwrap(); .unwrap();
println!("TXID = {}", tx_id); println!("TXID = {}", tx_id);

View File

@ -27,10 +27,11 @@ pub struct MemPool {
transactions: HashMap<Vec<u8>, MemPoolTransacton>, transactions: HashMap<Vec<u8>, MemPoolTransacton>,
nfs: HashMap<Vec<u8>, u64>, nfs: HashMap<Vec<u8>, u64>,
balance: i64, balance: i64,
ld_url: String,
} }
impl MemPool { impl MemPool {
pub fn new(db_path: &str) -> MemPool { pub fn new(db_path: &str, ld_url: &str) -> MemPool {
MemPool { MemPool {
db_path: db_path.to_string(), db_path: db_path.to_string(),
account: 0, account: 0,
@ -39,6 +40,7 @@ impl MemPool {
transactions: HashMap::new(), transactions: HashMap::new(),
nfs: HashMap::new(), nfs: HashMap::new(),
balance: 0, balance: 0,
ld_url: ld_url.to_string(),
} }
} }
@ -63,7 +65,7 @@ impl MemPool {
pub async fn scan(&mut self) -> anyhow::Result<i64> { pub async fn scan(&mut self) -> anyhow::Result<i64> {
if self.ivk.is_some() { if self.ivk.is_some() {
let ivk = self.ivk.as_ref().unwrap().clone(); let ivk = self.ivk.as_ref().unwrap().clone();
let mut client = connect_lightwalletd().await?; let mut client = connect_lightwalletd(&self.ld_url).await?;
let height = BlockHeight::from(get_latest_height(&mut client).await?); let height = BlockHeight::from(get_latest_height(&mut client).await?);
if self.height != height { if self.height != height {
// New blocks invalidate the mempool // New blocks invalidate the mempool
@ -154,14 +156,14 @@ impl MemPool {
mod tests { mod tests {
use crate::db::DEFAULT_DB_PATH; use crate::db::DEFAULT_DB_PATH;
use crate::mempool::MemPool; use crate::mempool::MemPool;
use crate::DbAdapter; use crate::{DbAdapter, LWD_URL};
use std::time::Duration; use std::time::Duration;
#[tokio::test] #[tokio::test]
async fn test_mempool() { async fn test_mempool() {
let db = DbAdapter::new(DEFAULT_DB_PATH).unwrap(); let db = DbAdapter::new(DEFAULT_DB_PATH).unwrap();
let ivk = db.get_ivk(1).unwrap(); let ivk = db.get_ivk(1).unwrap();
let mut mempool = MemPool::new("zec.db"); let mut mempool = MemPool::new("zec.db", LWD_URL);
mempool.set_ivk(&ivk); mempool.set_ivk(&ivk);
loop { loop {
mempool.scan().await.unwrap(); mempool.scan().await.unwrap();

View File

@ -75,10 +75,12 @@ pub async fn sync_async(
db_path: &str, db_path: &str,
target_height_offset: u32, target_height_offset: u32,
progress_callback: ProgressCallback, progress_callback: ProgressCallback,
ld_url: &str
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let ld_url = ld_url.to_owned();
let db_path = db_path.to_string(); let db_path = db_path.to_string();
let mut client = connect_lightwalletd().await?; let mut client = connect_lightwalletd(&ld_url).await?;
let (start_height, mut prev_hash, fvks) = { let (start_height, mut prev_hash, fvks) = {
let db = DbAdapter::new(&db_path)?; let db = DbAdapter::new(&db_path)?;
let height = db.get_db_height()?; let height = db.get_db_height()?;
@ -102,7 +104,7 @@ pub async fn sync_async(
let (processor_tx, mut processor_rx) = mpsc::channel::<Blocks>(1); let (processor_tx, mut processor_rx) = mpsc::channel::<Blocks>(1);
let downloader = tokio::spawn(async move { let downloader = tokio::spawn(async move {
let mut client = connect_lightwalletd().await?; let mut client = connect_lightwalletd(&ld_url).await?;
while let Some(range) = download_rx.recv().await { while let Some(range) = download_rx.recv().await {
log::info!("+ {:?}", range); log::info!("+ {:?}", range);
let blocks = download_chain(&mut client, range.start, range.end, prev_hash).await?; let blocks = download_chain(&mut client, range.start, range.end, prev_hash).await?;
@ -302,8 +304,8 @@ pub async fn sync_async(
Ok(()) Ok(())
} }
pub async fn latest_height() -> anyhow::Result<u32> { pub async fn latest_height(ld_url: &str) -> anyhow::Result<u32> {
let mut client = connect_lightwalletd().await?; let mut client = connect_lightwalletd(ld_url).await?;
let height = get_latest_height(&mut client).await?; let height = get_latest_height(&mut client).await?;
Ok(height) Ok(height)
} }

120
src/taddr.rs Normal file
View File

@ -0,0 +1,120 @@
use zcash_primitives::transaction::builder::Builder;
use crate::{CompactTxStreamerClient, AddressList, DbAdapter, NETWORK, connect_lightwalletd, get_latest_height, GetAddressUtxosArg};
use tonic::transport::Channel;
use tonic::Request;
use zcash_primitives::consensus::{BlockHeight, Parameters, BranchId};
use zcash_primitives::transaction::components::amount::DEFAULT_FEE;
use zcash_primitives::transaction::components::{Amount, OutPoint, TxOut};
use std::str::FromStr;
use anyhow::Context;
use zcash_primitives::legacy::{Script, TransparentAddress};
use zcash_client_backend::encoding::{decode_extended_full_viewing_key, decode_payment_address, encode_transparent_address};
use crate::chain::send_transaction;
use zcash_proofs::prover::LocalTxProver;
use ripemd160::{Ripemd160, Digest};
use sha2::Sha256;
use secp256k1::{SecretKey, PublicKey, Secp256k1, All};
use tiny_hderive::bip32::ExtendedPrivKey;
use bip39::{Mnemonic, Language, Seed};
pub const BIP44_PATH: &str = "m/44'/133'/0'/0/0";
pub async fn get_taddr_balance(client: &mut CompactTxStreamerClient<Channel>, address: &str) -> anyhow::Result<u64> {
let req = AddressList {
addresses: vec![address.to_string()],
};
let rep = client.get_taddress_balance(Request::new(req)).await?.into_inner();
Ok(rep.value_zat as u64)
}
pub async fn shield_taddr(db: &DbAdapter, account: u32, prover: &LocalTxProver, ld_url: &str) -> anyhow::Result<String> {
let mut client = connect_lightwalletd(ld_url).await?;
let last_height = get_latest_height(&mut client).await?;
let ivk = db.get_ivk(account)?;
let fvk = decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &ivk)?.unwrap();
let z_address = db.get_address(account)?;
let pa = decode_payment_address(NETWORK.hrp_sapling_payment_address(), &z_address)?.unwrap();
let t_address = db.get_taddr(account)?;
if t_address.is_none() { anyhow::bail!("No transparent address"); }
let t_address = t_address.unwrap();
let mut builder = Builder::new(NETWORK, BlockHeight::from_u32(last_height));
let amount = Amount::from_u64(get_taddr_balance(&mut client, &t_address).await?).unwrap();
if amount <= DEFAULT_FEE { anyhow::bail!("Not enough balance"); }
let amount = amount - DEFAULT_FEE;
let sk = db.get_tsk(account)?;
let seckey =
secp256k1::SecretKey::from_str(&sk).context("Cannot parse secret key")?;
let req = GetAddressUtxosArg {
addresses: vec![t_address.to_string()],
start_height: 0,
max_entries: 0,
};
let utxo_rep = client.get_address_utxos(Request::new(req)).await?.into_inner();
for utxo in utxo_rep.address_utxos.iter() {
let mut tx_hash = [0u8; 32];
tx_hash.copy_from_slice(&utxo.txid);
let op = OutPoint::new(tx_hash, utxo.index as u32);
let script = Script(utxo.script.clone());
let txout = TxOut {
value: Amount::from_i64(utxo.value_zat).unwrap(),
script_pubkey: script,
};
builder.add_transparent_input(seckey, op, txout)?;
}
let ovk = fvk.fvk.ovk;
builder.add_sapling_output(Some(ovk), pa, amount, None)?;
let consensus_branch_id =
BranchId::for_height(&NETWORK, BlockHeight::from_u32(last_height));
let (tx, _) = builder.build(consensus_branch_id, prover)?;
let mut raw_tx: Vec<u8> = vec![];
tx.write(&mut raw_tx)?;
let tx_id = send_transaction(&mut client, &raw_tx, last_height).await?;
log::info!("Tx ID = {}", tx_id);
Ok(tx_id)
}
pub fn derive_tkeys(phrase: &str, path: &str) -> anyhow::Result<(String, String)> {
let mnemonic = Mnemonic::from_phrase(&phrase, Language::English)?;
let seed = Seed::new(&mnemonic, "");
let secp = Secp256k1::<All>::new();
let ext = ExtendedPrivKey::derive(&seed.as_bytes(), path).unwrap();
let secret_key = SecretKey::from_slice(&ext.secret()).unwrap();
let pub_key = PublicKey::from_secret_key(&secp, &secret_key);
let pub_key = pub_key.serialize();
let pub_key = Ripemd160::digest(&Sha256::digest(&pub_key));
let address = TransparentAddress::PublicKey(pub_key.into());
let address = encode_transparent_address(&NETWORK.b58_pubkey_address_prefix(), &NETWORK.b58_script_address_prefix(), &address);
let sk = secret_key.to_string();
Ok((sk, address))
}
#[cfg(test)]
mod tests {
use crate::{DbAdapter, LWD_URL};
use crate::db::DEFAULT_DB_PATH;
use crate::taddr::{shield_taddr, derive_tkeys};
use zcash_proofs::prover::LocalTxProver;
#[tokio::test]
async fn test_shield_addr() {
let prover = LocalTxProver::with_default_location().unwrap();
let db = DbAdapter::new(DEFAULT_DB_PATH).unwrap();
let txid = shield_taddr(&db, 1, &prover, LWD_URL).await.unwrap();
println!("{}", txid);
}
#[test]
fn test_derive() {
let seed = dotenv::var("SEED").unwrap();
for i in 0..10 {
let (_sk, addr) = derive_tkeys(&seed, &format!("m/44'/133'/0'/0/{}", i)).unwrap();
println!("{}", addr);
}
}
}

View File

@ -11,7 +11,6 @@ use std::sync::{mpsc, Arc};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tonic::Request; use tonic::Request;
use zcash_client_backend::address::RecipientAddress; use zcash_client_backend::address::RecipientAddress;
use zcash_client_backend::data_api::wallet::ANCHOR_OFFSET;
use zcash_client_backend::encoding::{decode_extended_spending_key, decode_extended_full_viewing_key, encode_payment_address}; use zcash_client_backend::encoding::{decode_extended_spending_key, decode_extended_full_viewing_key, encode_payment_address};
use zcash_params::{OUTPUT_PARAMS, SPEND_PARAMS}; use zcash_params::{OUTPUT_PARAMS, SPEND_PARAMS};
use zcash_primitives::consensus::{BlockHeight, BranchId, Parameters}; use zcash_primitives::consensus::{BlockHeight, BranchId, Parameters};
@ -20,6 +19,7 @@ use zcash_primitives::transaction::components::amount::{DEFAULT_FEE, MAX_MONEY};
use zcash_primitives::transaction::components::Amount; use zcash_primitives::transaction::components::Amount;
use zcash_primitives::zip32::ExtendedFullViewingKey; use zcash_primitives::zip32::ExtendedFullViewingKey;
use zcash_proofs::prover::LocalTxProver; use zcash_proofs::prover::LocalTxProver;
use crate::taddr::{get_taddr_balance, shield_taddr};
const DEFAULT_CHUNK_SIZE: u32 = 100_000; const DEFAULT_CHUNK_SIZE: u32 = 100_000;
@ -27,6 +27,7 @@ pub struct Wallet {
pub db_path: String, pub db_path: String,
db: DbAdapter, db: DbAdapter,
prover: LocalTxProver, prover: LocalTxProver,
pub ld_url: String,
} }
#[repr(C)] #[repr(C)]
@ -47,7 +48,7 @@ impl Default for WalletBalance {
} }
impl Wallet { impl Wallet {
pub fn new(db_path: &str) -> Wallet { pub fn new(db_path: &str, ld_url: &str) -> Wallet {
let prover = LocalTxProver::from_bytes(SPEND_PARAMS, OUTPUT_PARAMS); let prover = LocalTxProver::from_bytes(SPEND_PARAMS, OUTPUT_PARAMS);
let db = DbAdapter::new(db_path).unwrap(); let db = DbAdapter::new(db_path).unwrap();
db.init_db().unwrap(); db.init_db().unwrap();
@ -55,6 +56,7 @@ impl Wallet {
db_path: db_path.to_string(), db_path: db_path.to_string(),
db, db,
prover, prover,
ld_url: ld_url.to_string(),
} }
} }
@ -95,6 +97,7 @@ impl Wallet {
let account = self let account = self
.db .db
.store_account(name, seed.as_deref(), sk.as_deref(), &ivk, &pa)?; .store_account(name, seed.as_deref(), sk.as_deref(), &ivk, &pa)?;
self.db.create_taddr(account)?;
Ok(account) Ok(account)
} }
@ -103,12 +106,13 @@ impl Wallet {
chunk_size: u32, chunk_size: u32,
target_height_offset: u32, target_height_offset: u32,
progress_callback: ProgressCallback, progress_callback: ProgressCallback,
ld_url: &str
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
crate::scan::sync_async(chunk_size, db_path, target_height_offset, progress_callback).await crate::scan::sync_async(chunk_size, db_path, target_height_offset, progress_callback, ld_url).await
} }
pub async fn get_latest_height() -> anyhow::Result<u32> { pub async fn get_latest_height(&self) -> anyhow::Result<u32> {
let mut client = connect_lightwalletd().await?; let mut client = connect_lightwalletd(&self.ld_url).await?;
let last_height = get_latest_height(&mut client).await?; let last_height = get_latest_height(&mut client).await?;
Ok(last_height) Ok(last_height)
} }
@ -117,10 +121,11 @@ impl Wallet {
pub async fn sync_ex( pub async fn sync_ex(
db_path: &str, db_path: &str,
progress_callback: impl Fn(u32) + Send + 'static, progress_callback: impl Fn(u32) + Send + 'static,
ld_url: &str
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let cb = Arc::new(Mutex::new(progress_callback)); let cb = Arc::new(Mutex::new(progress_callback));
Self::scan_async(db_path, DEFAULT_CHUNK_SIZE, 10, cb.clone()).await?; Self::scan_async(db_path, DEFAULT_CHUNK_SIZE, 10, cb.clone(), ld_url).await?;
Self::scan_async(db_path, DEFAULT_CHUNK_SIZE, 0, cb.clone()).await?; Self::scan_async(db_path, DEFAULT_CHUNK_SIZE, 0, cb.clone(), ld_url).await?;
Ok(()) Ok(())
} }
@ -128,11 +133,11 @@ impl Wallet {
&self, &self,
progress_callback: impl Fn(u32) + Send + 'static, progress_callback: impl Fn(u32) + Send + 'static,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
Self::sync_ex(&self.db_path, progress_callback).await Self::sync_ex(&self.db_path, progress_callback, &self.ld_url).await
} }
pub async fn skip_to_last_height(&self) -> anyhow::Result<()> { pub async fn skip_to_last_height(&self) -> anyhow::Result<()> {
let mut client = connect_lightwalletd().await?; let mut client = connect_lightwalletd(&self.ld_url).await?;
let last_height = get_latest_height(&mut client).await?; let last_height = get_latest_height(&mut client).await?;
let block_id = BlockId { let block_id = BlockId {
height: last_height as u64, height: last_height as u64,
@ -160,6 +165,7 @@ impl Wallet {
to_address: &str, to_address: &str,
amount: u64, amount: u64,
max_amount_per_note: u64, max_amount_per_note: u64,
anchor_offset: u32,
progress_callback: impl Fn(Progress) + Send + 'static, progress_callback: impl Fn(Progress) + Send + 'static,
) -> anyhow::Result<String> { ) -> anyhow::Result<String> {
let secret_key = self.db.get_sk(account)?; let secret_key = self.db.get_sk(account)?;
@ -172,13 +178,13 @@ impl Wallet {
let extfvk = ExtendedFullViewingKey::from(&skey); let extfvk = ExtendedFullViewingKey::from(&skey);
let (_, change_address) = extfvk.default_address().unwrap(); let (_, change_address) = extfvk.default_address().unwrap();
let ovk = extfvk.fvk.ovk; let ovk = extfvk.fvk.ovk;
let last_height = Self::get_latest_height().await?; let last_height = self.get_latest_height().await?;
let mut builder = Builder::new(NETWORK, BlockHeight::from_u32(last_height)); let mut builder = Builder::new(NETWORK, BlockHeight::from_u32(last_height));
let anchor_height = self let anchor_height = self
.db .db
.get_last_sync_height()? .get_last_sync_height()?
.ok_or_else(|| anyhow::anyhow!("No spendable notes"))?; .ok_or_else(|| anyhow::anyhow!("No spendable notes"))?;
let anchor_height = anchor_height.min(last_height - ANCHOR_OFFSET); let anchor_height = anchor_height.min(last_height - anchor_offset);
log::info!("Anchor = {}", anchor_height); log::info!("Anchor = {}", anchor_height);
let mut notes = self let mut notes = self
.db .db
@ -252,7 +258,7 @@ impl Wallet {
let mut raw_tx: Vec<u8> = vec![]; let mut raw_tx: Vec<u8> = vec![];
tx.write(&mut raw_tx)?; tx.write(&mut raw_tx)?;
let mut client = connect_lightwalletd().await?; let mut client = connect_lightwalletd(&self.ld_url).await?;
let tx_id = send_transaction(&mut client, &raw_tx, last_height).await?; let tx_id = send_transaction(&mut client, &raw_tx, last_height).await?;
log::info!("Tx ID = {}", tx_id); log::info!("Tx ID = {}", tx_id);
@ -276,6 +282,25 @@ impl Wallet {
let pa = encode_payment_address(NETWORK.hrp_sapling_payment_address(), &pa); let pa = encode_payment_address(NETWORK.hrp_sapling_payment_address(), &pa);
Ok(pa) Ok(pa)
} }
pub async fn get_taddr_balance(&self, account: u32) -> anyhow::Result<u64> {
let mut client = connect_lightwalletd(&self.ld_url).await?;
let address = self.db.get_taddr(account)?;
let balance = match address {
None => 0u64,
Some(address) => get_taddr_balance(&mut client, &address).await?,
};
Ok(balance)
}
pub async fn shield_taddr(&self, account: u32) -> anyhow::Result<String> {
shield_taddr(&self.db, account, &self.prover, &self.ld_url).await
}
pub fn set_lwd_url(&mut self, ld_url: &str) -> anyhow::Result<()> {
self.ld_url = ld_url.to_string();
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]
@ -283,6 +308,7 @@ mod tests {
use crate::key::derive_secret_key; use crate::key::derive_secret_key;
use crate::wallet::Wallet; use crate::wallet::Wallet;
use bip39::{Language, Mnemonic}; use bip39::{Language, Mnemonic};
use crate::LWD_URL;
#[tokio::test] #[tokio::test]
async fn test_wallet_seed() { async fn test_wallet_seed() {
@ -290,7 +316,7 @@ mod tests {
env_logger::init(); env_logger::init();
let seed = dotenv::var("SEED").unwrap(); let seed = dotenv::var("SEED").unwrap();
let wallet = Wallet::new("zec.db"); let wallet = Wallet::new("zec.db", LWD_URL);
wallet.new_account_with_key("test", &seed).unwrap(); wallet.new_account_with_key("test", &seed).unwrap();
} }
@ -311,7 +337,7 @@ mod tests {
#[test] #[test]
pub fn test_diversified_address() { pub fn test_diversified_address() {
let wallet = Wallet::new("zec.db"); let wallet = Wallet::new("zec.db", LWD_URL);
let address = wallet.new_diversified_address(1).unwrap(); let address = wallet.new_diversified_address(1).unwrap();
println!("{}", address); println!("{}", address);
} }