From f23cd2376f0aee14d846539e411ec5e3b0e31279 Mon Sep 17 00:00:00 2001 From: Hanh Date: Sun, 6 Nov 2022 10:55:53 +0800 Subject: [PATCH] Clean up some dead code --- src/api/payment.rs | 86 +++++++------- src/db.rs | 273 ++------------------------------------------- src/main/tests.rs | 116 ------------------- src/transaction.rs | 83 ++++++++++---- 4 files changed, 114 insertions(+), 444 deletions(-) delete mode 100644 src/main/tests.rs diff --git a/src/api/payment.rs b/src/api/payment.rs index 8181fc8..b11b2f9 100644 --- a/src/api/payment.rs +++ b/src/api/payment.rs @@ -29,51 +29,53 @@ async fn prepare_multi_payment( use_transparent: bool, anchor_offset: u32, ) -> anyhow::Result<(Tx, Vec)> { - let c = CoinConfig::get_active(); - let mut tx_builder = TxBuilder::new(c.coin_type, last_height); - - let AccountData { fvk, .. } = c.db()?.get_account_info(c.id_account)?; - let fvk = decode_extended_full_viewing_key( - c.chain.network().hrp_sapling_extended_full_viewing_key(), - &fvk, - ) - .unwrap(); - let utxos = if use_transparent { - let mut client = c.connect_lwd().await?; - let t_address = c.db()?.get_taddr(c.id_account)?; - if let Some(t_address) = t_address { - get_utxos(&mut client, &t_address, c.id_account).await? - } else { - vec![] - } - } else { - vec![] - }; - - let target_amount: u64 = recipients.iter().map(|r| r.amount).sum(); - let anchor_height = last_height.saturating_sub(anchor_offset); - let spendable_notes = c - .db()? - .get_spendable_notes(c.id_account, anchor_height, &fvk)?; - let note_ids = tx_builder.select_inputs(&fvk, &spendable_notes, &utxos, target_amount)?; - tx_builder.select_outputs(&fvk, recipients)?; - Ok((tx_builder.tx, note_ids)) + // let c = CoinConfig::get_active(); + // let mut tx_builder = TxBuilder::new(c.coin_type, last_height); + // + // let AccountData { fvk, .. } = c.db()?.get_account_info(c.id_account)?; + // let fvk = decode_extended_full_viewing_key( + // c.chain.network().hrp_sapling_extended_full_viewing_key(), + // &fvk, + // ) + // .unwrap(); + // let utxos = if use_transparent { + // let mut client = c.connect_lwd().await?; + // let t_address = c.db()?.get_taddr(c.id_account)?; + // if let Some(t_address) = t_address { + // get_utxos(&mut client, &t_address, c.id_account).await? + // } else { + // vec![] + // } + // } else { + // vec![] + // }; + // + // let target_amount: u64 = recipients.iter().map(|r| r.amount).sum(); + // let anchor_height = last_height.saturating_sub(anchor_offset); + // let spendable_notes = c + // .db()? + // .get_spendable_notes(c.id_account, anchor_height, &fvk)?; + // let note_ids = tx_builder.select_inputs(&fvk, &spendable_notes, &utxos, target_amount)?; + // tx_builder.select_outputs(&fvk, recipients)?; + // Ok((tx_builder.tx, note_ids)) + todo!() } fn sign(tx: &Tx, progress_callback: PaymentProgressCallback) -> anyhow::Result> { - let c = CoinConfig::get_active(); - let prover = get_prover(); - let db = c.db()?; - let AccountData { sk: zsk, .. } = db.get_account_info(c.id_account)?; - let zsk = zsk.ok_or(anyhow!("Cannot sign without secret key"))?; - let tsk = db - .get_tsk(c.id_account)? - .map(|tsk| SecretKey::from_str(&tsk).unwrap()); - let extsk = - decode_extended_spending_key(c.chain.network().hrp_sapling_extended_spending_key(), &zsk) - .unwrap(); - let raw_tx = tx.sign(tsk, &extsk, prover, progress_callback)?; - Ok(raw_tx) + // let c = CoinConfig::get_active(); + // let prover = get_prover(); + // let db = c.db()?; + // let AccountData { sk: zsk, .. } = db.get_account_info(c.id_account)?; + // let zsk = zsk.ok_or(anyhow!("Cannot sign without secret key"))?; + // let tsk = db + // .get_tsk(c.id_account)? + // .map(|tsk| SecretKey::from_str(&tsk).unwrap()); + // let extsk = + // decode_extended_spending_key(c.chain.network().hrp_sapling_extended_spending_key(), &zsk) + // .unwrap(); + // let raw_tx = tx.sign(tsk, &extsk, prover, progress_callback)?; + // Ok(raw_tx) + todo!() } /// Build a multi payment for offline signing diff --git a/src/db.rs b/src/db.rs index 6fbcd06..ad861bb 100644 --- a/src/db.rs +++ b/src/db.rs @@ -157,8 +157,7 @@ impl DbAdapter { .prepare("SELECT id_account FROM accounts WHERE ivk = ?1")?; let exists = statement.exists(params![ivk])?; self.connection.execute( - "INSERT INTO accounts(name, seed, aindex, sk, ivk, address) VALUES (?1, ?2, ?3, ?4, ?5, ?6) - ON CONFLICT DO NOTHING", + "INSERT INTO accounts(name, seed, aindex, sk, ivk, address) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", params![name, seed, index, sk, ivk, address], )?; let id_account: u32 = self @@ -246,24 +245,6 @@ impl DbAdapter { Ok(fvks) } - pub fn get_seeds(&self) -> anyhow::Result> { - let mut statement = self - .connection - .prepare("SELECT id_account, seed FROM accounts WHERE seed IS NOT NULL")?; - let rows = statement.query_map([], |row| { - let id_account: u32 = row.get(0)?; - let seed: String = row.get(1)?; - Ok(AccountSeed { - id_account, - seed}) - })?; - let mut accounts = vec![]; - for row in rows { - accounts.push(row?); - } - Ok(accounts) - } - pub fn trim_to_height(&mut self, height: u32) -> anyhow::Result { // snap height to an existing checkpoint let height = self.connection.query_row( @@ -307,22 +288,6 @@ impl DbAdapter { Ok(height) } - pub fn get_txhash(&self, id_tx: u32) -> anyhow::Result<(u32, u32, u32, Vec, String)> { - let (account, height, timestamp, tx_hash, ivk) = self.connection.query_row( - "SELECT account, height, timestamp, txid, ivk FROM transactions t, accounts a WHERE id_tx = ?1 AND t.account = a.id_account", - params![id_tx], - |row| { - let account: u32 = row.get(0)?; - let height: u32 = row.get(1)?; - let timestamp: u32 = row.get(2)?; - let tx_hash: Vec = row.get(3)?; - let ivk: String = row.get(4)?; - Ok((account, height, timestamp, tx_hash, ivk)) - }, - ).map_err(wrap_query_no_rows("get_txhash"))?; - Ok((account, height, timestamp, tx_hash, ivk)) - } - pub fn store_block( connection: &Connection, height: u32, @@ -341,34 +306,7 @@ impl DbAdapter { }); connection.execute( "INSERT INTO blocks(height, hash, timestamp, sapling_tree, orchard_tree) - VALUES (?1, ?2, ?3, ?4, ?5) - ON CONFLICT DO NOTHING", - params![height, hash, timestamp, &sapling_bb, orchard_bb], - )?; - log::debug!("-block"); - Ok(()) - } - - pub fn store_block2( - height: u32, - hash: &[u8], - timestamp: u32, - sapling_tree: &sync::CTree, - orchard_tree: Option<&sync::CTree>, - connection: &Connection, - ) -> anyhow::Result<()> { - log::debug!("+block"); - let mut sapling_bb: Vec = vec![]; - sapling_tree.write(&mut sapling_bb)?; - let orchard_bb = orchard_tree.map(|tree| { - let mut bb: Vec = vec![]; - tree.write(&mut bb).unwrap(); - bb - }); - connection.execute( - "INSERT INTO blocks(height, hash, timestamp, sapling_tree, orchard_tree) - VALUES (?1, ?2, ?3, ?4, ?5) - ON CONFLICT DO NOTHING", + VALUES (?1, ?2, ?3, ?4, ?5)", params![height, hash, timestamp, &sapling_bb, orchard_bb], )?; log::debug!("-block"); @@ -386,8 +324,7 @@ impl DbAdapter { log::debug!("+transaction"); db_tx.execute( "INSERT INTO transactions(account, txid, height, timestamp, tx_index, value) - VALUES (?1, ?2, ?3, ?4, ?5, 0) - ON CONFLICT DO NOTHING", + VALUES (?1, ?2, ?3, ?4, ?5, 0)", params![account, txid, height, timestamp, tx_index], )?; let id_tx: u32 = db_tx @@ -423,25 +360,6 @@ impl DbAdapter { Ok(id_note) } - // TODO: Depends on the type of witness - pub fn store_witnesses( - connection: &Connection, - witness: &Witness, - height: u32, - id_note: u32, - ) -> anyhow::Result<()> { - log::debug!("+witnesses"); - let mut bb: Vec = vec![]; - witness.write(&mut bb)?; - connection.execute( - "INSERT INTO sapling_witnesses(note, height, witness) VALUES (?1, ?2, ?3) - ON CONFLICT DO NOTHING", - params![id_note, height, bb], - )?; - log::debug!("-witnesses"); - Ok(()) - } - pub fn store_witness( witness: &sync::Witness, height: u32, @@ -453,8 +371,7 @@ impl DbAdapter { let mut bb: Vec = vec![]; witness.write(&mut bb)?; connection.execute( - &format!("INSERT INTO {}_witnesses(note, height, witness) VALUES (?1, ?2, ?3) - ON CONFLICT DO NOTHING", shielded_pool), + &format!("INSERT INTO {}_witnesses(note, height, witness) VALUES (?1, ?2, ?3)", shielded_pool), params![id_note, height, bb], )?; log::debug!("-store_witness"); @@ -473,8 +390,8 @@ impl DbAdapter { Ok(()) } - pub fn update_transaction_with_memo(&self, id_tx: u32, details: &TransactionDetails) -> anyhow::Result<()> { - self.connection.execute("UPDATE transactions SET address = ?1, memo = ?2 WHERE id_tx = ?3", params![details.address, details.memo, id_tx])?; + pub fn update_transaction_with_memo(&self, details: &TransactionDetails) -> anyhow::Result<()> { + self.connection.execute("UPDATE transactions SET address = ?1, memo = ?2 WHERE id_tx = ?3", params![details.address, details.memo, details.id_tx])?; Ok(()) } @@ -486,21 +403,7 @@ impl DbAdapter { Ok(()) } - pub fn get_received_note_value(nf: &Nf, db_tx: &Transaction) -> anyhow::Result<(u32, i64)> { - let (account, value) = db_tx - .query_row( - "SELECT account, value FROM received_notes WHERE nf = ?1", - params![nf.0.to_vec()], - |row| { - let account: u32 = row.get(0)?; - let value: i64 = row.get(1)?; - Ok((account, value)) - }, - ) - .map_err(wrap_query_no_rows("get_received_note_value"))?; - Ok((account, value)) - } - + #[allow(dead_code)] pub fn get_balance(&self, account: u32) -> anyhow::Result { let balance: Option = self.connection.query_row( "SELECT SUM(value) FROM received_notes WHERE (spent IS NULL OR spent = 0) AND account = ?1", @@ -575,28 +478,6 @@ impl DbAdapter { } } - pub fn get_nullifiers(&self) -> anyhow::Result> { - let mut statement = self.connection.prepare( - "SELECT id_note, account, nf FROM received_notes WHERE spent IS NULL OR spent = 0", - )?; - let nfs_res = statement.query_map([], |row| { - let id_note: u32 = row.get(0)?; - let account: u32 = row.get(1)?; - let nf_vec: Vec = row.get(2)?; - let mut nf = [0u8; 32]; - nf.clone_from_slice(&nf_vec); - let nf_ref = NfRef { id_note, account }; - Ok((nf_ref, nf)) - })?; - let mut nfs: HashMap = HashMap::new(); - for n in nfs_res { - let n = n?; - nfs.insert(Nf(n.1), n.0); - } - - Ok(nfs) - } - pub fn get_nullifier_amounts( &self, account: u32, @@ -648,69 +529,6 @@ impl DbAdapter { Ok(nfs) } - pub fn get_nullifiers_raw(&self) -> anyhow::Result)>> { - let mut statement = self - .connection - .prepare("SELECT account, value, nf FROM received_notes")?; - let res = statement.query_map([], |row| { - let account: u32 = row.get(0)?; - let amount: i64 = row.get(1)?; - let nf: Vec = row.get(2)?; - Ok((account, amount as u64, nf)) - })?; - let mut v: Vec<(u32, u64, Vec)> = vec![]; - for r in res { - v.push(r?); - } - Ok(v) - } - - // TODO: Depends on the type of witness - Should it returned any spendable note? sapling or orchard - pub fn get_spendable_notes( - &self, - account: u32, - anchor_height: u32, - fvk: &ExtendedFullViewingKey, - ) -> anyhow::Result> { - let mut statement = self.connection.prepare( - "SELECT id_note, diversifier, value, rcm, witness FROM received_notes r, sapling_witnesses w WHERE spent IS NULL AND account = ?2 - AND (r.excluded IS NULL OR NOT r.excluded) AND w.height = ( - SELECT MAX(height) FROM sapling_witnesses WHERE height <= ?1 - ) AND r.id_note = w.note")?; - let notes = statement.query_map(params![anchor_height, account], |row| { - let id_note: u32 = row.get(0)?; - - let diversifier: Vec = row.get(1)?; - let value: i64 = row.get(2)?; - let rcm: Vec = row.get(3)?; - let witness: Vec = row.get(4)?; - - let mut diversifer_bytes = [0u8; 11]; - diversifer_bytes.copy_from_slice(&diversifier); - let diversifier = Diversifier(diversifer_bytes); - let mut rcm_bytes = [0u8; 32]; - rcm_bytes.copy_from_slice(&rcm); - let rcm = jubjub::Fr::from_bytes(&rcm_bytes).unwrap(); - let rseed = Rseed::BeforeZip212(rcm); - let witness = IncrementalWitness::::read(&*witness).unwrap(); - - let pa = fvk.fvk.vk.to_payment_address(diversifier).unwrap(); - let note = pa.create_note(value as u64, rseed).unwrap(); - Ok(SpendableNote { - id: id_note, - note, - diversifier, - witness, - }) - })?; - let mut spendable_notes: Vec = vec![]; - for n in notes { - spendable_notes.push(n?); - } - - Ok(spendable_notes) - } - pub fn get_unspent_received_notes(&self, account: u32, anchor_height: u32) -> anyhow::Result> { let mut notes = vec![]; let mut statement = self.connection.prepare( @@ -855,7 +673,6 @@ impl DbAdapter { Ok(contacts) } - // TODO: Orchard diversifiers have a different space pub fn get_diversifier(&self, account: u32) -> anyhow::Result { let diversifier_index = self .connection @@ -874,7 +691,6 @@ impl DbAdapter { Ok(DiversifierIndex(diversifier_index)) } - // TODO: See get_diversifier pub fn store_diversifier( &self, account: u32, @@ -952,7 +768,7 @@ impl DbAdapter { let bip44_path = format!("m/44'/{}'/0'/0/{}", self.network().coin_type(), aindex); let (sk, address) = derive_tkeys(self.network(), &seed, &bip44_path)?; self.connection.execute( - "INSERT INTO taddrs(account, sk, address) VALUES (?1, ?2, ?3) ON CONFLICT DO NOTHING", + "INSERT INTO taddrs(account, sk, address) VALUES (?1, ?2, ?3)", params![account, &sk, &address], )?; } @@ -964,7 +780,7 @@ impl DbAdapter { if let Some(seed) = seed { let keys = derive_orchard_keys(self.network().coin_type(), &seed, aindex); self.connection.execute( - "INSERT INTO orchard_addrs(account, sk, fvk) VALUES (?1, ?2, ?3) ON CONFLICT DO NOTHING", + "INSERT INTO orchard_addrs(account, sk, fvk) VALUES (?1, ?2, ?3)", params![account, &keys.sk, &keys.fvk], )?; } @@ -985,7 +801,7 @@ impl DbAdapter { pub fn store_ua_settings(&self, account: u32, transparent: bool, sapling: bool, orchard: bool) -> anyhow::Result<()> { self.connection.execute( - "INSERT INTO ua_settings(account, transparent, sapling, orchard) VALUES (?1, ?2, ?3, ?4) ON CONFLICT DO NOTHING", + "INSERT INTO ua_settings(account, transparent, sapling, orchard) VALUES (?1, ?2, ?3, ?4)", params![account, transparent, sapling, orchard], )?; Ok(()) @@ -1035,37 +851,6 @@ impl DbAdapter { Ok(quote) } - pub fn store_share_secret( - &self, - account: u32, - secret: &str, - index: usize, - threshold: usize, - participants: usize, - ) -> anyhow::Result<()> { - self.connection.execute( - "INSERT INTO secret_shares(account, secret, idx, threshold, participants) VALUES (?1, ?2, ?3, ?4, ?5) \ - ON CONFLICT (account) DO UPDATE SET secret = excluded.secret, threshold = excluded.threshold, participants = excluded.participants", - params![account, &secret, index as u32, threshold as u32, participants as u32], - )?; - Ok(()) - } - - pub fn get_share_secret(&self, account: u32) -> anyhow::Result { - let secret = self - .connection - .query_row( - "SELECT secret FROM secret_shares WHERE account = ?1", - params![account], - |row| { - let secret: String = row.get(0)?; - Ok(secret) - }, - ) - .optional()?; - Ok(secret.unwrap_or("".to_string())) - } - pub fn truncate_data(&self) -> anyhow::Result<()> { self.truncate_sync_data()?; self.connection.execute("DELETE FROM diversifiers", [])?; @@ -1442,44 +1227,6 @@ mod tests { use zcash_params::coin::CoinType; use crate::sync::{CTree, Witness}; - #[test] - fn test_db() { - let mut db = DbAdapter::new(CoinType::Zcash, DEFAULT_DB_PATH).unwrap(); - db.init_db().unwrap(); - db.trim_to_height(0).unwrap(); - - let db_tx = db.begin_transaction().unwrap(); - DbAdapter::store_block(&db_tx, 1, &[0u8; 32], 0, &CTree::new(), None).unwrap(); - let id_tx = DbAdapter::store_transaction(&[0; 32], 1, 1, 0, 20, &db_tx).unwrap(); - DbAdapter::store_received_note( - &ReceivedNote { - account: 1, - height: 1, - output_index: 0, - diversifier: vec![], - value: 0, - rcm: vec![], - nf: vec![], - rho: None, - spent: None, - }, - id_tx, - 5, - &db_tx, - ) - .unwrap(); - let witness = Witness { - position: 10, - id_note: 0, - tree: CTree::new(), - filled: vec![], - cursor: CTree::new(), - cmx: [0u8; 32] - }; - DbAdapter::store_witnesses(&db_tx, &witness, 1000, 1).unwrap(); - db_tx.commit().unwrap(); - } - #[test] fn test_balance() { let db = DbAdapter::new(CoinType::Zcash, DEFAULT_DB_PATH).unwrap(); diff --git a/src/main/tests.rs b/src/main/tests.rs deleted file mode 100644 index 1bbe6f6..0000000 --- a/src/main/tests.rs +++ /dev/null @@ -1,116 +0,0 @@ -use std::collections::HashMap; -use std::fs::File; -use std::io::{BufReader, BufWriter, Read, Write}; -use byteorder::{LE, ReadBytesExt, WriteBytesExt}; -use tonic::Request; -use prost::Message; -use zcash_client_backend::encoding::{decode_extended_full_viewing_key, decode_extended_spending_key, encode_extended_full_viewing_key, encode_payment_address}; -use zcash_primitives::consensus::{Network, NetworkUpgrade, Parameters}; -use zcash_primitives::sapling::note_encryption::SaplingDomain; -use zcash_primitives::zip32::ExtendedFullViewingKey; -use warp_api_ffi::{BlockId, BlockRange, ChainSpec, COIN_CONFIG, CoinConfig, CompactBlock, connect_lightwalletd, DbAdapter, DbAdapterBuilder, derive_zip32, init_coin}; -use warp_api_ffi::sapling::{DecryptedSaplingNote, SaplingDecrypter, SaplingHasher, SaplingViewKey}; -use warp_api_ffi::sync::{WarpProcessor, Synchronizer, CTree}; - -type SaplingSynchronizer = Synchronizer, SaplingViewKey, DecryptedSaplingNote, - SaplingDecrypter, SaplingHasher>; - -#[allow(dead_code)] -async fn write_block_file() { - init_coin(1, "yec-new.db").unwrap(); - let coin = COIN_CONFIG[1].lock().unwrap(); - let mut client = connect_lightwalletd("https://lite.ycash.xyz:9067").await.unwrap(); - let network = coin.chain.network(); - let start = u32::from(network.activation_height(NetworkUpgrade::Sapling).unwrap()) + 1; - let end = client.get_latest_block(Request::new(ChainSpec {})).await.unwrap().into_inner(); - let end = end.height as u32; - - let mut blocks = client.get_block_range(Request::new(BlockRange { - start: Some(BlockId { height: start as u64, hash: vec![] }), - end: Some(BlockId { height: end as u64, hash: vec![] }), - spam_filter_threshold: 0 - })).await.unwrap().into_inner(); - - let file = File::create("ycash.bin").unwrap(); - let mut writer = BufWriter::new(file); - while let Some(block) = blocks.message().await.unwrap() { - println!("{}", block.height); - let mut buf = prost::bytes::BytesMut::new(); - block.encode(&mut buf).unwrap(); - writer.write_u32::(buf.len() as u32).unwrap(); - writer.write_all(&buf).unwrap(); - } -} - -fn read_block_file(coin: &CoinConfig, fvk: ExtendedFullViewingKey) { - let network = coin.chain.network(); - let file = File::open("/home/hanh/ycash.bin").unwrap(); - let mut reader = BufReader::new(file); - - let db_builder = DbAdapterBuilder { coin_type: coin.coin_type, db_path: coin.db_path.as_ref().unwrap().to_owned() }; - let mut synchronizer = SaplingSynchronizer { - decrypter: SaplingDecrypter::new(*network), - warper: WarpProcessor::new(SaplingHasher::default()), - vks: vec![SaplingViewKey { - account: 1, - fvk: fvk.clone(), - ivk: fvk.fvk.vk.ivk() - }], - tree: CTree::new(), - witnesses: vec![], - - db: db_builder.clone(), - shielded_pool: "sapling".to_string(), - - note_position: 0, - nullifiers: HashMap::new(), - _phantom: Default::default() - }; - - synchronizer.initialize().unwrap(); - - let mut blocks = vec![]; - let mut height = 0; - let mut hash = [0u8; 32]; - let mut time = 0; - while let Ok(len) = reader.read_u32::() { - let mut buf = vec![0u8; len as usize]; - reader.read_exact(&mut buf).unwrap(); - let cb: CompactBlock = CompactBlock::decode(&*buf).unwrap(); - height = cb.height; - hash.copy_from_slice(&cb.hash); - time = cb.time; - blocks.push(cb); - if height % 100_000 == 0 { - synchronizer.process(blocks).unwrap(); - blocks = vec![]; - } - } - synchronizer.process(blocks).unwrap(); - let db = db_builder.build().unwrap(); - DbAdapter::store_block2(height as u32, &hash, time, &synchronizer.tree, None, &db.connection).unwrap(); -} - -#[tokio::main] -async fn main() { - env_logger::init(); - init_coin(1, "yec-new.db").unwrap(); - let coin = COIN_CONFIG[1].lock().unwrap(); - let network = coin.chain.network(); - let _ = dotenv::dotenv(); - let seed_str = dotenv::var("SEED").unwrap(); - let kp = derive_zip32(&network, &seed_str, 0, 0, None).unwrap(); - let zk = kp.z_key.clone(); - let sk = decode_extended_spending_key(network.hrp_sapling_extended_spending_key(), &zk).unwrap().unwrap(); - - let fvk = ExtendedFullViewingKey::from(&sk); - let fvk_str = encode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), &fvk); - let (_, pa) = fvk.default_address(); - let address = encode_payment_address(network.hrp_sapling_payment_address(), &pa); - let db_builder = DbAdapterBuilder { coin_type: coin.coin_type, db_path: coin.db_path.as_ref().unwrap().to_owned() }; - let db = db_builder.build().unwrap(); - db.store_account("test", Some(&seed_str), 0, Some(&zk), &fvk_str, &address).unwrap(); - - // write_block_file().await; - read_block_file(&coin, fvk); -} diff --git a/src/transaction.rs b/src/transaction.rs index ee53b94..0c4fd42 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,8 +1,9 @@ +use std::collections::HashMap; use crate::contact::{Contact, ContactDecoder}; use crate::{AccountData, CoinConfig, CompactTxStreamerClient, DbAdapter, Hash, TxFilter}; use std::convert::TryFrom; use serde::Serialize; -use orchard::keys::{FullViewingKey, Scope}; +use orchard::keys::{FullViewingKey, IncomingViewingKey, OutgoingViewingKey, Scope}; use orchard::note_encryption::OrchardDomain; use orchard::value::ValueCommitment; use tonic::transport::Channel; @@ -16,6 +17,7 @@ use zcash_params::coin::get_branch; use zcash_primitives::consensus::{BlockHeight, Network, Parameters}; use zcash_primitives::memo::{Memo, MemoBytes}; use zcash_primitives::sapling::note_encryption::{PreparedIncomingViewingKey, try_sapling_note_decryption, try_sapling_output_recovery}; +use zcash_primitives::sapling::SaplingIvk; use zcash_primitives::transaction::Transaction; use crate::unified::orchard_as_unified; @@ -29,18 +31,37 @@ pub struct ContactRef { pub async fn get_transaction_details(coin: u8) -> anyhow::Result<()> { let c = CoinConfig::get(coin); let network = c.chain.network(); + let mut client = c.connect_lwd().await?; + let mut keys = HashMap::new(); + + let reqs = { + let db = c.db.as_ref().unwrap(); + let db = db.lock().unwrap(); + let reqs = db.get_txid_without_memo()?; + for req in reqs.iter() { + let decryption_keys = get_decryption_keys(network, req.account, &db)?; + keys.insert(req.account, decryption_keys); + } + reqs + // Make sure we don't hold a mutex across await + }; + + let mut details = vec![]; + for req in reqs.iter() { + let tx_details = retrieve_tx_info(network, &mut client, req.height, req.id_tx, &req.txid, &keys[&req.account]).await?; + log::info!("{:?}", tx_details); + details.push(tx_details); + } + let db = c.db.as_ref().unwrap(); let db = db.lock().unwrap(); - let mut client = c.connect_lwd().await?; - let reqs = db.get_txid_without_memo()?; - for req in reqs { - let tx_details = retrieve_tx_info(network, &mut client, &db, req.account, req.height, &req.txid).await?; - log::info!("{:?}", tx_details); - db.update_transaction_with_memo(req.id_tx, &tx_details)?; + for tx_details in details.iter() { + db.update_transaction_with_memo(tx_details)?; for c in tx_details.contacts.iter() { db.store_contact(c, false)?; } } + Ok(()) } @@ -59,23 +80,20 @@ async fn fetch_raw_transaction(network: &Network, client: &mut CompactTxStreamer Ok(tx) } +#[derive(Clone)] +pub struct DecryptionKeys { + sapling_keys: (SaplingIvk, zcash_primitives::keys::OutgoingViewingKey), + orchard_keys: Option<(IncomingViewingKey, OutgoingViewingKey)> +} + pub fn decode_transaction( network: &Network, - account: u32, height: u32, + id_tx: u32, tx: Transaction, - db: &DbAdapter, + decryption_keys: &DecryptionKeys, ) -> anyhow::Result { - let AccountData { fvk, .. } = db.get_account_info(account)?; - let fvk = decode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), &fvk).unwrap(); - let sapling_ivk = fvk.fvk.vk.ivk(); - let sapling_ovk = fvk.fvk.ovk; - - let okey = db.get_orchard(account)?; - let okey = okey.map(|okey| { - let fvk = FullViewingKey::from_bytes(&okey.fvk).unwrap(); - (fvk.to_ivk(Scope::External), fvk.to_ovk(Scope::External)) - }); + let (sapling_ivk, sapling_ovk) = decryption_keys.sapling_keys.clone(); let height = BlockHeight::from_u32(height); let mut taddress: Option = None; @@ -126,7 +144,7 @@ pub fn decode_transaction( } if let Some(orchard_bundle) = tx.orchard_bundle() { - if let Some((orchard_ivk, orchard_ovk)) = okey { + if let Some((orchard_ivk, orchard_ovk)) = decryption_keys.orchard_keys.clone() { for action in orchard_bundle.actions().iter() { let domain = OrchardDomain::for_action(action); if let Some((_note, pa, memo)) = try_note_decryption(&domain, &orchard_ivk, action) { @@ -159,6 +177,7 @@ pub fn decode_transaction( Memo::Arbitrary(_) => "Unrecognized".to_string(), }; let tx_details = TransactionDetails { + id_tx, address, memo, contacts, @@ -167,16 +186,33 @@ pub fn decode_transaction( Ok(tx_details) } +fn get_decryption_keys(network: &Network, account: u32, db: &DbAdapter) -> anyhow::Result { + let AccountData { fvk, .. } = db.get_account_info(account)?; + let fvk = decode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), &fvk).unwrap(); + let (sapling_ivk, sapling_ovk) = (fvk.fvk.vk.ivk(), fvk.fvk.ovk); + + let okey = db.get_orchard(account)?; + let okey = okey.map(|okey| { + let fvk = FullViewingKey::from_bytes(&okey.fvk).unwrap(); + (fvk.to_ivk(Scope::External), fvk.to_ovk(Scope::External)) + }); + let decryption_keys = DecryptionKeys { + sapling_keys: (sapling_ivk, sapling_ovk), + orchard_keys: okey, + }; + Ok(decryption_keys) +} + pub async fn retrieve_tx_info( network: &Network, client: &mut CompactTxStreamerClient, - db: &DbAdapter, - account: u32, height: u32, + id_tx: u32, txid: &Hash, + decryption_keys: &DecryptionKeys ) -> anyhow::Result { let transaction = fetch_raw_transaction(network, client, height, txid).await?; - let tx_details = decode_transaction(network, account, height, transaction, db)?; + let tx_details = decode_transaction(network, height, id_tx, transaction, &decryption_keys)?; Ok(tx_details) } @@ -190,6 +226,7 @@ pub struct GetTransactionDetailRequest { #[derive(Serialize, Debug)] pub struct TransactionDetails { + pub id_tx: u32, pub address: String, pub memo: String, pub contacts: Vec,