zcash-sync/src/transaction.rs

278 lines
9.0 KiB
Rust
Raw Normal View History

2021-11-11 17:39:50 -08:00
use crate::contact::{Contact, ContactDecoder};
2022-11-06 04:50:51 -08:00
use crate::unified::orchard_as_unified;
2022-11-05 18:49:17 -07:00
use crate::{AccountData, CoinConfig, CompactTxStreamerClient, DbAdapter, Hash, TxFilter};
2022-11-05 19:55:53 -07:00
use orchard::keys::{FullViewingKey, IncomingViewingKey, OutgoingViewingKey, Scope};
2022-11-05 18:49:17 -07:00
use orchard::note_encryption::OrchardDomain;
use orchard::value::ValueCommitment;
2022-11-06 04:50:51 -08:00
use serde::Serialize;
use std::collections::HashMap;
use std::convert::TryFrom;
2021-07-09 22:44:13 -07:00
use tonic::transport::Channel;
use tonic::Request;
2022-11-05 18:49:17 -07:00
use zcash_address::{ToAddress, ZcashAddress};
2021-07-16 01:42:29 -07:00
use zcash_client_backend::encoding::{
decode_extended_full_viewing_key, encode_payment_address, encode_transparent_address,
};
2022-11-05 18:49:17 -07:00
use zcash_note_encryption::{try_note_decryption, try_output_recovery_with_ovk};
use zcash_params::coin::get_branch;
2022-03-07 06:47:06 -08:00
use zcash_primitives::consensus::{BlockHeight, Network, Parameters};
2022-11-05 18:49:17 -07:00
use zcash_primitives::memo::{Memo, MemoBytes};
2022-11-06 04:50:51 -08:00
use zcash_primitives::sapling::note_encryption::{
try_sapling_note_decryption, try_sapling_output_recovery, PreparedIncomingViewingKey,
};
2022-11-05 19:55:53 -07:00
use zcash_primitives::sapling::SaplingIvk;
2021-07-16 01:42:29 -07:00
use zcash_primitives::transaction::Transaction;
2021-07-09 22:44:13 -07:00
2021-07-18 08:57:43 -07:00
#[derive(Debug)]
2021-09-08 07:10:22 -07:00
pub struct ContactRef {
pub height: u32,
pub index: u32,
pub contact: Contact,
2021-07-18 08:57:43 -07:00
}
2022-11-05 18:49:17 -07:00
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?;
2022-11-05 19:55:53 -07:00
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() {
2022-11-06 04:50:51 -08:00
let tx_details = retrieve_tx_info(
network,
&mut client,
req.height,
req.id_tx,
&req.txid,
&keys[&req.account],
)
.await?;
2022-11-05 18:49:17 -07:00
log::info!("{:?}", tx_details);
2022-11-05 19:55:53 -07:00
details.push(tx_details);
}
let db = c.db.as_ref().unwrap();
let db = db.lock().unwrap();
for tx_details in details.iter() {
db.update_transaction_with_memo(tx_details)?;
2022-11-05 18:49:17 -07:00
for c in tx_details.contacts.iter() {
db.store_contact(c, false)?;
}
}
2022-11-05 19:55:53 -07:00
2022-11-05 18:49:17 -07:00
Ok(())
}
2021-07-09 22:44:13 -07:00
2022-11-06 04:50:51 -08:00
async fn fetch_raw_transaction(
network: &Network,
client: &mut CompactTxStreamerClient<Channel>,
height: u32,
txid: &Hash,
) -> anyhow::Result<Transaction> {
2022-11-05 18:49:17 -07:00
let consensus_branch_id = get_branch(network, height);
2021-07-09 22:44:13 -07:00
let tx_filter = TxFilter {
block: None,
index: 0,
2022-11-05 18:49:17 -07:00
hash: txid.to_vec(), // only hash is supported
2021-07-09 22:44:13 -07:00
};
2021-07-16 01:42:29 -07:00
let raw_tx = client
.get_transaction(Request::new(tx_filter))
.await?
.into_inner();
2022-03-14 19:40:08 -07:00
let tx = Transaction::read(&*raw_tx.data, consensus_branch_id)?;
2022-11-05 18:49:17 -07:00
Ok(tx)
}
2022-11-05 19:55:53 -07:00
#[derive(Clone)]
pub struct DecryptionKeys {
sapling_keys: (SaplingIvk, zcash_primitives::keys::OutgoingViewingKey),
2022-11-06 04:50:51 -08:00
orchard_keys: Option<(IncomingViewingKey, OutgoingViewingKey)>,
2022-11-05 19:55:53 -07:00
}
2022-11-05 18:49:17 -07:00
pub fn decode_transaction(
network: &Network,
height: u32,
2022-11-05 19:55:53 -07:00
id_tx: u32,
2022-11-05 18:49:17 -07:00
tx: Transaction,
2022-11-05 19:55:53 -07:00
decryption_keys: &DecryptionKeys,
2022-11-05 18:49:17 -07:00
) -> anyhow::Result<TransactionDetails> {
2022-11-05 19:55:53 -07:00
let (sapling_ivk, sapling_ovk) = decryption_keys.sapling_keys.clone();
2021-07-09 22:44:13 -07:00
let height = BlockHeight::from_u32(height);
2022-11-05 18:49:17 -07:00
let mut taddress: Option<String> = None;
let mut zaddress: Option<String> = None;
let mut oaddress: Option<String> = None;
2022-03-14 19:40:08 -07:00
let tx = tx.into_data();
2022-03-15 01:04:37 -07:00
// log::info!("{:?}", tx);
2021-09-08 07:10:22 -07:00
2021-09-11 06:55:32 -07:00
let mut tx_memo: Memo = Memo::Empty;
2022-11-05 18:49:17 -07:00
let mut contacts = vec![];
2021-07-16 01:42:29 -07:00
2022-03-15 01:04:37 -07:00
if let Some(transparent_bundle) = tx.transparent_bundle() {
for output in transparent_bundle.vout.iter() {
if let Some(taddr) = output.recipient_address() {
2022-11-05 18:49:17 -07:00
taddress = Some(encode_transparent_address(
2022-03-15 01:04:37 -07:00
&network.b58_pubkey_address_prefix(),
&network.b58_script_address_prefix(),
&taddr,
2022-11-05 18:49:17 -07:00
));
2022-03-15 01:04:37 -07:00
}
2021-09-25 17:00:04 -07:00
}
}
2022-11-05 18:49:17 -07:00
if let Some(sapling_bundle) = tx.sapling_bundle() {
let mut contact_decoder = ContactDecoder::new(sapling_bundle.shielded_outputs.len());
for output in sapling_bundle.shielded_outputs.iter() {
let pivk = PreparedIncomingViewingKey::new(&sapling_ivk);
2022-11-06 04:50:51 -08:00
if let Some((_note, pa, memo)) =
try_sapling_note_decryption(network, height, &pivk, output)
{
2022-11-05 18:49:17 -07:00
let memo = Memo::try_from(memo)?;
if zaddress.is_none() {
2022-11-06 04:50:51 -08:00
zaddress = Some(encode_payment_address(
network.hrp_sapling_payment_address(),
&pa,
));
2022-11-05 18:49:17 -07:00
}
if memo != Memo::Empty {
tx_memo = memo;
}
2021-07-09 22:44:13 -07:00
}
2022-11-06 04:50:51 -08:00
if let Some((_note, pa, memo, ..)) =
try_sapling_output_recovery(network, height, &sapling_ovk, output)
{
2022-11-05 18:49:17 -07:00
let _ = contact_decoder.add_memo(&memo); // ignore memo that is not for contacts, if we cannot decode it with ovk, we didn't make create this memo
2022-11-06 04:50:51 -08:00
zaddress = Some(encode_payment_address(
network.hrp_sapling_payment_address(),
&pa,
));
2022-11-05 18:49:17 -07:00
let memo = Memo::try_from(memo)?;
if memo != Memo::Empty {
tx_memo = memo;
}
2021-09-11 06:55:32 -07:00
}
2021-07-09 22:44:13 -07:00
}
2022-11-05 18:49:17 -07:00
contacts = contact_decoder.finalize()?;
2021-07-09 22:44:13 -07:00
}
2022-11-05 18:49:17 -07:00
if let Some(orchard_bundle) = tx.orchard_bundle() {
2022-11-05 19:55:53 -07:00
if let Some((orchard_ivk, orchard_ovk)) = decryption_keys.orchard_keys.clone() {
2022-11-05 18:49:17 -07:00
for action in orchard_bundle.actions().iter() {
let domain = OrchardDomain::for_action(action);
2022-11-06 04:50:51 -08:00
if let Some((_note, pa, memo)) = try_note_decryption(&domain, &orchard_ivk, action)
{
2022-11-05 18:49:17 -07:00
let memo = Memo::try_from(MemoBytes::from_bytes(&memo)?)?;
if oaddress.is_none() {
oaddress = Some(orchard_as_unified(network, &pa).encode());
}
if memo != Memo::Empty {
tx_memo = memo;
}
}
2022-11-06 04:50:51 -08:00
if let Some((_note, pa, memo, ..)) = try_output_recovery_with_ovk(
&domain,
&orchard_ovk,
action,
action.cv_net(),
&action.encrypted_note().out_ciphertext,
) {
2022-11-05 18:49:17 -07:00
let memo = Memo::try_from(MemoBytes::from_bytes(&memo)?)?;
oaddress = Some(orchard_as_unified(network, &pa).encode());
if memo != Memo::Empty {
tx_memo = memo;
}
}
}
}
}
2021-07-09 22:44:13 -07:00
2022-11-05 18:49:17 -07:00
let address = zaddress.or(oaddress).or(taddress).unwrap_or(String::new());
2022-03-07 21:12:34 -08:00
2021-11-11 17:39:50 -08:00
let memo = match tx_memo {
Memo::Empty => "".to_string(),
Memo::Text(text) => text.to_string(),
Memo::Future(_) => "Unrecognized".to_string(),
Memo::Arbitrary(_) => "Unrecognized".to_string(),
};
2022-11-05 18:49:17 -07:00
let tx_details = TransactionDetails {
2022-11-05 19:55:53 -07:00
id_tx,
2021-07-09 22:44:13 -07:00
address,
2021-07-18 08:57:43 -07:00
memo,
2021-09-08 07:10:22 -07:00
contacts,
2021-07-09 22:44:13 -07:00
};
2022-11-05 18:49:17 -07:00
Ok(tx_details)
2021-07-31 00:34:57 -07:00
}
2022-11-06 04:50:51 -08:00
fn get_decryption_keys(
network: &Network,
account: u32,
db: &DbAdapter,
) -> anyhow::Result<DecryptionKeys> {
2022-11-05 19:55:53 -07:00
let AccountData { fvk, .. } = db.get_account_info(account)?;
2022-11-06 04:50:51 -08:00
let fvk =
decode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), &fvk)
.unwrap();
2022-11-05 19:55:53 -07:00
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(
2022-11-05 18:49:17 -07:00
network: &Network,
client: &mut CompactTxStreamerClient<Channel>,
2022-11-05 18:49:17 -07:00
height: u32,
2022-11-05 19:55:53 -07:00
id_tx: u32,
2022-11-05 18:49:17 -07:00
txid: &Hash,
2022-11-06 04:50:51 -08:00
decryption_keys: &DecryptionKeys,
2022-11-05 18:49:17 -07:00
) -> anyhow::Result<TransactionDetails> {
let transaction = fetch_raw_transaction(network, client, height, txid).await?;
2022-11-05 19:55:53 -07:00
let tx_details = decode_transaction(network, height, id_tx, transaction, &decryption_keys)?;
2021-07-31 00:34:57 -07:00
2022-11-05 18:49:17 -07:00
Ok(tx_details)
}
2021-07-31 00:34:57 -07:00
2022-11-05 18:49:17 -07:00
pub struct GetTransactionDetailRequest {
pub account: u32,
pub height: u32,
pub id_tx: u32,
pub txid: Hash,
}
2021-07-09 22:44:13 -07:00
2022-11-05 18:49:17 -07:00
#[derive(Serialize, Debug)]
pub struct TransactionDetails {
2022-11-05 19:55:53 -07:00
pub id_tx: u32,
2022-11-05 18:49:17 -07:00
pub address: String,
pub memo: String,
pub contacts: Vec<Contact>,
2021-07-18 08:57:43 -07:00
}
2022-11-05 18:49:17 -07:00
#[tokio::test]
async fn test_get_transaction_details() {
crate::init_test();
2021-07-09 22:44:13 -07:00
2022-11-05 18:49:17 -07:00
get_transaction_details(0).await.unwrap();
2021-07-09 22:44:13 -07:00
}