From 39266e766e511a4f953ae357295325245b05dcf1 Mon Sep 17 00:00:00 2001 From: Hanh Date: Sun, 26 Feb 2023 09:53:50 +1000 Subject: [PATCH] diversifier address by time --- binding.h | 2 +- src/api/account.rs | 11 ++--- src/api/dart_ffi.rs | 4 +- src/db.rs | 42 ++++-------------- src/main/rpc.rs | 2 +- src/taddr.rs | 101 +++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 118 insertions(+), 44 deletions(-) diff --git a/binding.h b/binding.h index 165ac18..96a9ceb 100644 --- a/binding.h +++ b/binding.h @@ -190,7 +190,7 @@ int8_t is_valid_key(uint8_t coin, char *key); bool valid_address(uint8_t coin, char *address); -struct CResult_____c_char new_diversified_address(uint8_t ua_type); +struct CResult_____c_char get_diversified_address(uint8_t ua_type, uint32_t time); struct CResult_u32 get_latest_height(void); diff --git a/src/api/account.rs b/src/api/account.rs index 50bfc09..c87ba40 100644 --- a/src/api/account.rs +++ b/src/api/account.rs @@ -24,6 +24,7 @@ use zcash_address::{ToAddress, ZcashAddress}; use zcash_client_backend::encoding::{decode_extended_full_viewing_key, encode_payment_address}; use zcash_client_backend::keys::UnifiedFullViewingKey; use zcash_primitives::consensus::Parameters; +use zcash_primitives::zip32::DiversifierIndex; /// Create a new account /// # Arguments @@ -188,7 +189,7 @@ pub fn import_transparent_secret_key(coin: u8, id_account: u32, sk: &str) -> any } /// Generate a new diversified address -pub fn new_diversified_address(ua_type: u8) -> anyhow::Result { +pub fn get_diversified_address(ua_type: u8, time: u32) -> anyhow::Result { let ua_type = ua_type & 6; // don't include transparent component if ua_type == 0 { anyhow::bail!("Must include a shielded receiver"); @@ -201,12 +202,12 @@ pub fn new_diversified_address(ua_type: u8) -> anyhow::Result { &fvk, ) .map_err(|_| anyhow!("Bech32 Decode Error"))?; - let mut diversifier_index = db.get_diversifier(c.id_account)?; - diversifier_index.increment().unwrap(); - let (new_diversifier_index, pa) = fvk + let mut di = [0u8; 11]; + di[4..8].copy_from_slice(&time.to_le_bytes()); + let diversifier_index = DiversifierIndex(di); + let (_, pa) = fvk .find_address(diversifier_index) .ok_or_else(|| anyhow::anyhow!("Cannot generate new address"))?; - db.store_diversifier(c.id_account, &new_diversifier_index)?; let orchard_keys = db.get_orchard(c.id_account)?; if ua_type == 2 || orchard_keys.is_none() { diff --git a/src/api/dart_ffi.rs b/src/api/dart_ffi.rs index d9ac076..cbc5713 100644 --- a/src/api/dart_ffi.rs +++ b/src/api/dart_ffi.rs @@ -348,8 +348,8 @@ pub unsafe extern "C" fn valid_address(coin: u8, address: *mut c_char) -> bool { } #[no_mangle] -pub unsafe extern "C" fn new_diversified_address(ua_type: u8) -> CResult<*mut c_char> { - let res = || crate::api::account::new_diversified_address(ua_type); +pub unsafe extern "C" fn get_diversified_address(ua_type: u8, time: u32) -> CResult<*mut c_char> { + let res = || crate::api::account::get_diversified_address(ua_type, time); to_cresult_str(res()) } diff --git a/src/db.rs b/src/db.rs index ddace62..cfce5ab 100644 --- a/src/db.rs +++ b/src/db.rs @@ -5,7 +5,7 @@ use crate::orchard::{derive_orchard_keys, OrchardKeyBytes, OrchardViewKey}; use crate::prices::Quote; use crate::sapling::SaplingViewKey; use crate::sync::tree::{CTree, TreeCheckpoint}; -use crate::taddr::derive_tkeys; +use crate::taddr::{derive_tkeys, TransparentTxInfo}; use crate::transaction::{GetTransactionDetailRequest, TransactionDetails}; use crate::unified::UnifiedAddressType; use crate::{sync, BlockId, CoinConfig, CompactTxStreamerClient, Hash}; @@ -486,6 +486,14 @@ impl DbAdapter { Ok(()) } + pub fn store_transparent_tx( + _account: u32, + _tx: &TransparentTxInfo, + _db_tx: &Transaction, + ) -> anyhow::Result<()> { + todo!() + } + pub fn add_value(id_tx: u32, value: i64, db_tx: &Transaction) -> anyhow::Result<()> { db_tx.execute( "UPDATE transactions SET value = value + ?2 WHERE id_tx = ?1", @@ -825,38 +833,6 @@ impl DbAdapter { Ok(contacts) } - pub fn get_diversifier(&self, account: u32) -> anyhow::Result { - let diversifier_index = self - .connection - .query_row( - "SELECT diversifier_index FROM diversifiers WHERE account = ?1", - params![account], - |row| { - let d: Vec = row.get(0)?; - let mut div = [0u8; 11]; - div.copy_from_slice(&d); - Ok(div) - }, - ) - .optional()? - .unwrap_or([0u8; 11]); - Ok(DiversifierIndex(diversifier_index)) - } - - pub fn store_diversifier( - &self, - account: u32, - diversifier_index: &DiversifierIndex, - ) -> anyhow::Result<()> { - let diversifier_bytes = diversifier_index.0.to_vec(); - self.connection.execute( - "INSERT INTO diversifiers(account, diversifier_index) VALUES (?1, ?2) ON CONFLICT \ - (account) DO UPDATE SET diversifier_index = excluded.diversifier_index", - params![account, diversifier_bytes], - )?; - Ok(()) - } - pub fn get_account_info(&self, account: u32) -> anyhow::Result { assert_ne!(account, 0); log::info!("get_account_info {} {}", self.db_path, account); diff --git a/src/main/rpc.rs b/src/main/rpc.rs index 8c65697..a284b61 100644 --- a/src/main/rpc.rs +++ b/src/main/rpc.rs @@ -356,7 +356,7 @@ pub async fn build_from_plan(tx_plan: Json) -> Result Result { - let address = warp_api_ffi::api::account::new_diversified_address()?; + let address = warp_api_ffi::api::account::get_diversified_address()?; Ok(address) } diff --git a/src/taddr.rs b/src/taddr.rs index 4e13151..80ac884 100644 --- a/src/taddr.rs +++ b/src/taddr.rs @@ -6,8 +6,8 @@ use crate::db::AccountData; use crate::note_selection::{SecretKeys, Source, UTXO}; use crate::unified::orchard_as_unified; use crate::{ - broadcast_tx, build_tx, AddressList, CompactTxStreamerClient, GetAddressUtxosArg, - GetAddressUtxosReply, TransparentAddressBlockFilter, + broadcast_tx, build_tx, AddressList, BlockId, CompactTxStreamerClient, GetAddressUtxosArg, + GetAddressUtxosReply, Hash, TransparentAddressBlockFilter, TxFilter, }; use anyhow::anyhow; use base58check::FromBase58Check; @@ -18,13 +18,17 @@ use rand::rngs::OsRng; use ripemd::{Digest, Ripemd160}; use secp256k1::{All, PublicKey, Secp256k1, SecretKey}; use sha2::Sha256; +use std::collections::HashMap; use tiny_hderive::bip32::ExtendedPrivKey; use tonic::transport::Channel; use tonic::Request; use zcash_client_backend::encoding::encode_transparent_address; +use zcash_params::coin::get_branch; use zcash_primitives::consensus::{Network, Parameters}; use zcash_primitives::legacy::TransparentAddress; use zcash_primitives::memo::Memo; +use zcash_primitives::transaction::components::OutPoint; +use zcash_primitives::transaction::Transaction; pub async fn get_taddr_balance( client: &mut CompactTxStreamerClient, @@ -40,6 +44,99 @@ pub async fn get_taddr_balance( Ok(rep.value_zat as u64) } +pub struct TransparentTxInfo { + pub txid: Hash, + pub height: u32, + pub timestamp: u32, + pub inputs: Vec, + pub in_value: u64, + pub out_value: u64, +} + +/* With the current LWD API, this function performs poorly because +the server does not return tx timestamp, height and value + */ +#[allow(unused)] +pub async fn get_ttx_history( + network: &Network, + client: &mut CompactTxStreamerClient, + address: &str, +) -> anyhow::Result> { + let mut rep = client + .get_taddress_txids(Request::new(TransparentAddressBlockFilter { + address: address.to_string(), + range: None, + })) + .await? + .into_inner(); + let mut heights: HashMap = HashMap::new(); + let mut txs = vec![]; + while let Some(raw_tx) = rep.message().await? { + let height = raw_tx.height as u32; + heights.insert(height, 0); + let consensus_branch_id = get_branch(network, height); + let tx = Transaction::read(&*raw_tx.data, consensus_branch_id)?; + let txid = tx.txid(); + let tx_data = tx.into_data(); + let mut inputs = vec![]; + if let Some(transparent_bundle) = tx_data.transparent_bundle() { + for vin in transparent_bundle.vin.iter() { + inputs.push(vin.prevout.clone()); + } + let out_value = transparent_bundle + .vout + .iter() + .map(|vout| i64::from(vout.value)) + .sum::() as u64; + txs.push(TransparentTxInfo { + txid: txid.as_ref().clone(), + height, + timestamp: 0, + inputs, + in_value: 0, + out_value, + }); + } + } + + for (h, timestamp) in heights.iter_mut() { + let block = client + .get_block(Request::new(BlockId { + height: *h as u64, + hash: vec![], + })) + .await? + .into_inner(); + *timestamp = block.time; + } + + for tx in txs.iter_mut() { + let mut in_value = 0; + for input in tx.inputs.iter() { + let raw_tx = client + .get_transaction(Request::new(TxFilter { + block: None, + index: 0, + hash: input.hash().to_vec(), + })) + .await? + .into_inner(); + let consensus_branch_id = get_branch(network, raw_tx.height as u32); + let tx = Transaction::read(&*raw_tx.data, consensus_branch_id)?; + let tx_data = tx.into_data(); + let transparent_bundle = tx_data + .transparent_bundle() + .ok_or(anyhow!("No transparent bundle"))?; + let value = i64::from(transparent_bundle.vout[input.n() as usize].value); + in_value += value; + } + tx.timestamp = heights[&tx.height]; + tx.in_value = in_value as u64; + tx.inputs.clear(); + } + Ok(txs) +} + pub async fn get_taddr_tx_count( client: &mut CompactTxStreamerClient, address: &str,