Get Transaction Details

This commit is contained in:
Hanh 2022-11-06 09:49:17 +08:00
parent f222187650
commit b61772b639
8 changed files with 190 additions and 294 deletions

View File

@ -290,9 +290,7 @@ pub async fn import_sync_data(coin: u8, file: &str) -> anyhow::Result<()> {
let file = File::open(file)?;
let file = BufReader::new(file);
let account_info: AccountInfo = serde_json::from_reader(file)?;
let ids = db.import_from_syncdata(&account_info)?;
let mut client = connect_lightwalletd(c.lwd_url.as_ref().unwrap()).await?;
retrieve_tx_info(c.coin_type, &mut client, c.db_path.as_ref().unwrap(), &ids).await?;
db.import_from_syncdata(&account_info)?;
Ok(())
}

View File

@ -61,17 +61,14 @@ async fn coin_sync_impl(
progress_callback: AMProgressCallback,
cancel: &'static std::sync::Mutex<bool>,
) -> anyhow::Result<()> {
let c = CoinConfig::get(coin);
crate::scan::sync_async(
c.coin_type,
coin,
chunk_size,
get_tx,
c.db_path.as_ref().unwrap(),
target_height_offset,
max_cost,
progress_callback,
cancel,
c.lwd_url.as_ref().unwrap(),
)
.await?;
Ok(())

View File

@ -2,7 +2,7 @@ use crate::chain::{Nf, NfRef};
use crate::contact::Contact;
use crate::prices::Quote;
use crate::taddr::{derive_tkeys, TBalance};
use crate::transaction::TransactionInfo;
use crate::transaction::{GetTransactionDetailRequest, TransactionDetails};
use crate::sync::tree::{CTree, TreeCheckpoint, Witness};
use rusqlite::Error::QueryReturnedNoRows;
use rusqlite::{params, Connection, OptionalExtension, Transaction};
@ -131,12 +131,7 @@ impl DbAdapter {
let tx = self.connection.transaction()?;
Ok(tx)
}
//
// pub fn commit(&self) -> anyhow::Result<()> {
// self.connection.execute("COMMIT", [])?;
// Ok(())
// }
//
pub fn init_db(&mut self) -> anyhow::Result<()> {
migration::init_db(&self.connection, self.network())?;
self.delete_incomplete_scan()?;
@ -478,11 +473,8 @@ impl DbAdapter {
Ok(())
}
pub fn store_tx_metadata(&self, id_tx: u32, tx_info: &TransactionInfo) -> anyhow::Result<()> {
self.connection.execute(
"UPDATE transactions SET address = ?1, memo = ?2 WHERE id_tx = ?3",
params![tx_info.address, &tx_info.memo, id_tx],
)?;
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])?;
Ok(())
}
@ -1273,6 +1265,27 @@ impl DbAdapter {
Ok(txs)
}
pub fn get_txid_without_memo(&self) -> anyhow::Result<Vec<GetTransactionDetailRequest>> {
let mut stmt = self.connection.prepare("SELECT account, id_tx, height, txid FROM transactions WHERE memo IS NULL")?;
let rows = stmt.query_map([], |row| {
let account: u32 = row.get(0)?;
let id_tx: u32 = row.get(1)?;
let height: u32 = row.get(2)?;
let txid: Vec<u8> = row.get(3)?;
Ok(GetTransactionDetailRequest {
account,
id_tx,
height,
txid: txid.try_into().unwrap(),
})
})?;
let mut reqs = vec![];
for r in rows {
reqs.push(r?);
}
Ok(reqs)
}
pub fn import_from_syncdata(&mut self, account_info: &AccountInfo) -> anyhow::Result<Vec<u32>> {
// get id_account from fvk
// truncate received_notes, sapling_witnesses for account

View File

@ -145,3 +145,8 @@ pub mod nodejs;
mod gpu;
pub fn init_test() {
let _ = env_logger::try_init();
init_coin(0, "./zec.db").unwrap();
set_coin_lwd_url(0, "http://127.0.0.1:9067");
}

View File

@ -1,6 +1,6 @@
use serde_json::Value;
use zcash_primitives::memo::Memo;
use crate::{CoinConfig, init_coin, set_coin_lwd_url};
use crate::{CoinConfig, init_test};
use crate::api::payment::RecipientMemo;
use crate::unified::UnifiedAddressType;
use super::{*, types::*};
@ -10,15 +10,9 @@ const CHANGE_ADDRESS: &str = "u1pncsxa8jt7aq37r8uvhjrgt7sv8a665hdw44rqa28cd9t6qq
const UA_TSO: &str = "uregtest1mxy5wq2n0xw57nuxa4lqpl358zw4vzyfgadsn5jungttmqcv6nx6cpx465dtpzjzw0vprjle4j4nqqzxtkuzm93regvgg4xce0un5ec6tedquc469zjhtdpkxz04kunqqyasv4rwvcweh3ue0ku0payn29stl2pwcrghyzscrrju9ar57rn36wgz74nmynwcyw27rjd8yk477l97ez8";
const UA_O: &str = "uregtest1mzt5lx5s5u8kczlfr82av97kjckmfjfuq8y9849h6cl9chhdekxsm6r9dklracflqwplrnfzm5rucp5txfdm04z5myrde8y3y5rayev8";
fn init() {
let _ = env_logger::try_init();
init_coin(0, "./zec.db").unwrap();
set_coin_lwd_url(0, "http://127.0.0.1:9067");
}
#[tokio::test]
async fn test_fetch_utxo() {
init();
init_test();
let utxos = fetch_utxos(0, 1, 235, true, 0).await.unwrap();
for utxo in utxos.iter() {
@ -30,7 +24,7 @@ async fn test_fetch_utxo() {
#[test]
fn test_ua() {
init();
init_test();
let c = CoinConfig::get(0);
let db = c.db().unwrap();
let address = crate::get_unified_address(c.chain.network(), &db, 1,
@ -40,7 +34,7 @@ fn test_ua() {
#[tokio::test]
async fn test_payment() {
init();
init_test();
let config = NoteSelectConfig::new(CHANGE_ADDRESS);
let recipients = vec![

View File

@ -1,9 +1,9 @@
use crate::chain::get_latest_height;
use crate::db::{AccountViewKey, DbAdapter};
use crate::db::AccountViewKey;
use serde::Serialize;
use crate::transaction::retrieve_tx_info;
use crate::{connect_lightwalletd, CompactBlock, CompactSaplingOutput, CompactTx, DbAdapterBuilder};
use crate::transaction::{get_transaction_details, GetTransactionDetailRequest, retrieve_tx_info};
use crate::{connect_lightwalletd, CompactBlock, CompactSaplingOutput, CompactTx, DbAdapterBuilder, CoinConfig};
use crate::chain::{DecryptNode, download_chain};
use anyhow::anyhow;
@ -15,7 +15,6 @@ use tokio::runtime::{Builder, Runtime};
use tokio::sync::mpsc;
use tokio::sync::Mutex;
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
use zcash_params::coin::{get_coin_chain, CoinType};
use zcash_primitives::consensus::{Network, Parameters};
use zcash_primitives::sapling::Note;
@ -63,26 +62,24 @@ type OrchardSynchronizer = Synchronizer<Network, OrchardDomain, OrchardViewKey,
OrchardDecrypter<Network>, OrchardHasher>;
pub async fn sync_async<'a>(
coin_type: CoinType,
coin: u8,
_chunk_size: u32,
_get_tx: bool, // TODO
db_path: &'a str,
get_tx: bool, // TODO
target_height_offset: u32,
max_cost: u32,
_progress_callback: AMProgressCallback, // TODO
cancel: &'static std::sync::Mutex<bool>,
ld_url: &'a str,
) -> anyhow::Result<()> {
let ld_url = ld_url.to_owned();
let db_path = db_path.to_owned();
let network = {
let chain = get_coin_chain(coin_type);
*chain.network()
};
let c = CoinConfig::get(coin);
let ld_url = c.lwd_url.as_ref().unwrap().clone();
let db_path = c.db_path.as_ref().unwrap().clone();
let network = *c.chain.network();
let mut client = connect_lightwalletd(&ld_url).await?;
let (start_height, prev_hash, sapling_vks, orchard_vks) = {
let db = DbAdapter::new(coin_type, &db_path)?;
let db = c.db.as_ref().unwrap();
let db = db.lock().unwrap();
let height = db.get_db_height()?;
let hash = db.get_db_hash(height)?;
let sapling_vks = db.get_sapling_fvks()?;
@ -102,7 +99,7 @@ pub async fn sync_async<'a>(
Ok::<_, anyhow::Error>(())
});
let db_builder = DbAdapterBuilder { coin_type, db_path: db_path.clone() };
let db_builder = DbAdapterBuilder { coin_type: c.coin_type, db_path: db_path.clone() };
while let Some(blocks) = blocks_rx.recv().await {
let first_block = blocks.0.first().unwrap(); // cannot be empty because blocks are not
log::info!("Height: {}", first_block.height);
@ -148,6 +145,9 @@ pub async fn sync_async<'a>(
height = last_height;
}
if get_tx {
get_transaction_details(coin).await?;
}
Ok(())
}

View File

@ -1,38 +1,23 @@
use crate::contact::{Contact, ContactDecoder};
// use crate::wallet::decode_memo;
use crate::api::payment::decode_memo;
use crate::{CompactTxStreamerClient, DbAdapter, TxFilter};
use anyhow::anyhow;
use futures::StreamExt;
use std::collections::HashMap;
use crate::{AccountData, CoinConfig, CompactTxStreamerClient, DbAdapter, Hash, TxFilter};
use std::convert::TryFrom;
use std::sync::mpsc;
use std::sync::mpsc::SyncSender;
use serde::Serialize;
use orchard::keys::{FullViewingKey, Scope};
use orchard::note_encryption::OrchardDomain;
use orchard::value::ValueCommitment;
use tonic::transport::Channel;
use tonic::Request;
use zcash_address::{ToAddress, ZcashAddress};
use zcash_client_backend::encoding::{
decode_extended_full_viewing_key, encode_payment_address, encode_transparent_address,
};
use zcash_params::coin::{get_branch, get_coin_chain, CoinType};
use zcash_note_encryption::{try_note_decryption, try_output_recovery_with_ovk};
use zcash_params::coin::get_branch;
use zcash_primitives::consensus::{BlockHeight, Network, Parameters};
use zcash_primitives::memo::Memo;
use zcash_primitives::memo::{Memo, MemoBytes};
use zcash_primitives::sapling::note_encryption::{PreparedIncomingViewingKey, try_sapling_note_decryption, try_sapling_output_recovery};
use zcash_primitives::transaction::Transaction;
use zcash_primitives::zip32::ExtendedFullViewingKey;
#[derive(Debug)]
pub struct TransactionInfo {
height: u32,
timestamp: u32,
index: u32, // index of tx in block
id_tx: u32, // id of tx in db
pub account: u32,
pub address: String,
pub memo: String,
pub amount: i64,
// pub fee: u64,
pub contacts: Vec<Contact>,
}
use crate::unified::orchard_as_unified;
#[derive(Debug)]
pub struct ContactRef {
@ -41,96 +26,131 @@ pub struct ContactRef {
pub contact: Contact,
}
pub async fn decode_transaction(
network: &Network,
client: &mut CompactTxStreamerClient<Channel>,
nfs: &HashMap<(u32, Vec<u8>), u64>,
id_tx: u32,
account: u32,
fvk: &ExtendedFullViewingKey,
tx_hash: &[u8],
height: u32,
timestamp: u32,
index: u32,
) -> anyhow::Result<TransactionInfo> {
let consensus_branch_id = get_branch(network, height);
let ivk = fvk.fvk.vk.ivk();
let ovk = fvk.fvk.ovk;
pub async fn get_transaction_details(coin: u8) -> anyhow::Result<()> {
let c = CoinConfig::get(coin);
let network = c.chain.network();
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 c in tx_details.contacts.iter() {
db.store_contact(c, false)?;
}
}
Ok(())
}
async fn fetch_raw_transaction(network: &Network, client: &mut CompactTxStreamerClient<Channel>, height: u32, txid: &Hash) -> anyhow::Result<Transaction> {
let consensus_branch_id = get_branch(network, height);
let tx_filter = TxFilter {
block: None,
index: 0,
hash: tx_hash.to_vec(), // only hash is supported
hash: txid.to_vec(), // only hash is supported
};
let raw_tx = client
.get_transaction(Request::new(tx_filter))
.await?
.into_inner();
let tx = Transaction::read(&*raw_tx.data, consensus_branch_id)?;
Ok(tx)
}
pub fn decode_transaction(
network: &Network,
account: u32,
height: u32,
tx: Transaction,
db: &DbAdapter,
) -> anyhow::Result<TransactionDetails> {
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 height = BlockHeight::from_u32(height);
let mut amount = 0i64;
let mut taddress = String::new();
let mut zaddress = String::new();
let mut taddress: Option<String> = None;
let mut zaddress: Option<String> = None;
let mut oaddress: Option<String> = None;
let tx = tx.into_data();
// log::info!("{:?}", tx);
let sapling_bundle = tx.sapling_bundle().ok_or(anyhow!("No sapling bundle"))?;
for spend in sapling_bundle.shielded_spends.iter() {
let nf = spend.nullifier.to_vec();
if let Some(&v) = nfs.get(&(account, nf)) {
amount -= v as i64;
}
}
let mut contact_decoder = ContactDecoder::new(sapling_bundle.shielded_outputs.len());
let mut tx_memo: Memo = Memo::Empty;
let mut contacts = vec![];
if let Some(transparent_bundle) = tx.transparent_bundle() {
for output in transparent_bundle.vout.iter() {
if let Some(taddr) = output.recipient_address() {
taddress = encode_transparent_address(
taddress = Some(encode_transparent_address(
&network.b58_pubkey_address_prefix(),
&network.b58_script_address_prefix(),
&taddr,
);
));
}
}
}
for output in sapling_bundle.shielded_outputs.iter() {
let pivk = PreparedIncomingViewingKey::new(&ivk);
if let Some((note, pa, memo)) = try_sapling_note_decryption(network, height, &pivk, output) {
amount += note.value as i64; // change or self transfer
let _ = contact_decoder.add_memo(&memo); // ignore memo that is not for contacts
let memo = Memo::try_from(memo)?;
if zaddress.is_empty() {
zaddress = encode_payment_address(network.hrp_sapling_payment_address(), &pa);
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);
if let Some((_note, pa, memo)) = try_sapling_note_decryption(network, height, &pivk, output) {
let memo = Memo::try_from(memo)?;
if zaddress.is_none() {
zaddress = Some(encode_payment_address(network.hrp_sapling_payment_address(), &pa));
}
if memo != Memo::Empty {
tx_memo = memo;
}
}
if memo != Memo::Empty {
tx_memo = memo;
if let Some((_note, pa, memo, ..)) = try_sapling_output_recovery(network, height, &sapling_ovk, output) {
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
zaddress = Some(encode_payment_address(network.hrp_sapling_payment_address(), &pa));
let memo = Memo::try_from(memo)?;
if memo != Memo::Empty {
tx_memo = memo;
}
}
} else if let Some((_note, pa, memo)) =
try_sapling_output_recovery(network, height, &ovk, output)
{
zaddress = encode_payment_address(network.hrp_sapling_payment_address(), &pa);
let memo = Memo::try_from(memo)?;
if memo != Memo::Empty {
tx_memo = memo;
}
contacts = contact_decoder.finalize()?;
}
if let Some(orchard_bundle) = tx.orchard_bundle() {
if let Some((orchard_ivk, orchard_ovk)) = okey {
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) {
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;
}
}
if let Some((_note, pa, memo, ..)) = try_output_recovery_with_ovk(&domain, &orchard_ovk, action,
action.cv_net(), &action.encrypted_note().out_ciphertext) {
let memo = Memo::try_from(MemoBytes::from_bytes(&memo)?)?;
oaddress = Some(orchard_as_unified(network, &pa).encode());
if memo != Memo::Empty {
tx_memo = memo;
}
}
}
}
}
// let fee =
// u64::from() +
// u64::from(tx.sapling_bundle().unwrap().value_balance);
// zaddress must be one of ours
// taddress is not always ours
let address =
// let's use the zaddr from ovk first, then the ivk then the taddr
if zaddress.is_empty() { taddress } else { zaddress };
let address = zaddress.or(oaddress).or(taddress).unwrap_or(String::new());
let memo = match tx_memo {
Memo::Empty => "".to_string(),
@ -138,178 +158,46 @@ pub async fn decode_transaction(
Memo::Future(_) => "Unrecognized".to_string(),
Memo::Arbitrary(_) => "Unrecognized".to_string(),
};
let contacts = contact_decoder.finalize()?;
let tx_info = TransactionInfo {
height: u32::from(height),
timestamp,
index,
id_tx,
account,
let tx_details = TransactionDetails {
address,
memo,
amount,
// fee,
contacts,
};
Ok(tx_info)
}
struct DecodeTxParams<'a> {
tx: SyncSender<TransactionInfo>,
client: CompactTxStreamerClient<Channel>,
nf_map: &'a HashMap<(u32, Vec<u8>), u64>,
index: u32,
id_tx: u32,
account: u32,
fvk: ExtendedFullViewingKey,
tx_hash: Vec<u8>,
height: u32,
timestamp: u32,
Ok(tx_details)
}
pub async fn retrieve_tx_info(
coin_type: CoinType,
network: &Network,
client: &mut CompactTxStreamerClient<Channel>,
db_path: &str,
tx_ids: &[u32],
) -> anyhow::Result<()> {
let network = {
let chain = get_coin_chain(coin_type);
*chain.network()
};
let db = DbAdapter::new(coin_type, db_path)?;
db: &DbAdapter,
account: u32,
height: u32,
txid: &Hash,
) -> anyhow::Result<TransactionDetails> {
let transaction = fetch_raw_transaction(network, client, height, txid).await?;
let tx_details = decode_transaction(network, account, height, transaction, db)?;
let nfs = db.get_nullifiers_raw()?;
let mut nf_map: HashMap<(u32, Vec<u8>), u64> = HashMap::new();
for nf in nfs.iter() {
nf_map.insert((nf.0, nf.2.clone()), nf.1);
}
let mut fvk_cache: HashMap<u32, ExtendedFullViewingKey> = HashMap::new();
let mut decode_tx_params: Vec<DecodeTxParams> = vec![];
let (tx, rx) = mpsc::sync_channel::<TransactionInfo>(4);
for (index, &id_tx) in tx_ids.iter().enumerate() {
let (account, height, timestamp, tx_hash, ivk) = db.get_txhash(id_tx)?;
let fvk: &ExtendedFullViewingKey = fvk_cache.entry(account).or_insert_with(|| {
decode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), &ivk)
.unwrap()
});
let params = DecodeTxParams {
tx: tx.clone(),
client: client.clone(),
nf_map: &nf_map,
index: index as u32,
id_tx,
account,
fvk: fvk.clone(),
tx_hash: tx_hash.clone(),
height,
timestamp,
};
decode_tx_params.push(params);
}
let res = tokio_stream::iter(decode_tx_params).for_each_concurrent(None, |mut p| async move {
if let Ok(tx_info) = decode_transaction(
&network,
&mut p.client,
p.nf_map,
p.id_tx,
p.account,
&p.fvk,
&p.tx_hash,
p.height,
p.timestamp,
p.index,
)
.await
{
p.tx.send(tx_info).unwrap();
drop(p.tx);
}
});
let f = tokio::spawn(async move {
let mut contacts: Vec<ContactRef> = vec![];
while let Ok(tx_info) = rx.recv() {
for c in tx_info.contacts.iter() {
contacts.push(ContactRef {
height: tx_info.height,
index: tx_info.index,
contact: c.clone(),
});
}
db.store_tx_metadata(tx_info.id_tx, &tx_info)?;
let z_msg = decode_memo(
tx_info.id_tx,
&tx_info.memo,
&tx_info.address,
tx_info.timestamp,
tx_info.height,
);
if !z_msg.is_empty() {
db.store_message(tx_info.account, &z_msg)?;
}
}
contacts.sort_by(|a, b| a.index.cmp(&b.index));
for cref in contacts.iter() {
db.store_contact(&cref.contact, false)?;
}
Ok::<_, anyhow::Error>(())
});
res.await;
drop(tx);
f.await??;
Ok(())
Ok(tx_details)
}
#[cfg(test)]
mod tests {
use crate::transaction::decode_transaction;
use crate::{AccountData, connect_lightwalletd, DbAdapter, LWD_URL};
use std::collections::HashMap;
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
use zcash_params::coin::CoinType;
use zcash_primitives::consensus::{Network, Parameters};
#[tokio::test]
async fn test_decode_transaction() {
let tx_hash =
hex::decode("b47da170329dc311b98892eac23e83025f8bb3ce10bb07535698c91fb37e1e54")
.unwrap();
let mut client = connect_lightwalletd(LWD_URL).await.unwrap();
let db = DbAdapter::new(CoinType::Zcash, "./zec.db").unwrap();
let account = 1;
let nfs = db.get_nullifiers_raw().unwrap();
let mut nf_map: HashMap<(u32, Vec<u8>), u64> = HashMap::new();
for nf in nfs.iter() {
if nf.0 == account {
nf_map.insert((nf.0, nf.2.clone()), nf.1);
}
}
let AccountData { fvk, .. } = db.get_account_info(account).unwrap();
let fvk = decode_extended_full_viewing_key(
Network::MainNetwork.hrp_sapling_extended_full_viewing_key(),
&fvk,
)
.unwrap();
let tx_info = decode_transaction(
&Network::MainNetwork,
&mut client,
&nf_map,
1,
account,
&fvk,
&tx_hash,
1313212,
1000,
1,
)
.await
.unwrap();
println!("{:?}", tx_info);
}
pub struct GetTransactionDetailRequest {
pub account: u32,
pub height: u32,
pub id_tx: u32,
pub txid: Hash,
}
#[derive(Serialize, Debug)]
pub struct TransactionDetails {
pub address: String,
pub memo: String,
pub contacts: Vec<Contact>,
}
#[tokio::test]
async fn test_get_transaction_details() {
crate::init_test();
get_transaction_details(0).await.unwrap();
}

View File

@ -29,7 +29,7 @@ impl std::fmt::Display for DecodedUA {
self.sapling.as_ref().map(|a| encode_payment_address(self.network.hrp_sapling_payment_address(), a)),
self.orchard.as_ref().map(|a| {
let ua = unified::Address(vec![Receiver::Orchard(a.to_raw_address_bytes())]);
ua.encode(&network2network(&self.network))
ua.encode(&self.network.address_network().unwrap())
})
)
}
@ -66,7 +66,7 @@ pub fn get_unified_address(network: &Network, db: &DbAdapter, account: u32, tpe:
}
let addresses = unified::Address(rcvs);
let unified_address = ZcashAddress::from_unified(network2network(network), addresses);
let unified_address = ZcashAddress::from_unified(network.address_network().unwrap(), addresses);
Ok(unified_address.encode())
}
@ -77,7 +77,7 @@ pub fn decode_unified_address(network: &Network, ua: &str) -> anyhow::Result<Dec
sapling: None,
orchard: None
};
let network = network2network(network);
let network = network.address_network().unwrap();
let (a_network, ua) = unified::Address::decode(ua)?;
if network != a_network {
anyhow::bail!("Invalid network")
@ -101,6 +101,7 @@ pub fn decode_unified_address(network: &Network, ua: &str) -> anyhow::Result<Dec
Ok(decoded_ua)
}
fn network2network(n: &Network) -> zcash_address::Network { n.address_network().unwrap() }
// u1pncsxa8jt7aq37r8uvhjrgt7sv8a665hdw44rqa28cd9t6qqmktzwktw772nlle6skkkxwmtzxaan3slntqev03g70tzpky3c58hfgvfjkcky255cwqgfuzdjcktfl7pjalt5sl33se75pmga09etn9dplr98eq2g8cgmvgvx6jx2a2xhy39x96c6rumvlyt35whml87r064qdzw30e
pub fn orchard_as_unified(network: &Network, address: &Address) -> ZcashAddress {
let unified_address = unified::Address(vec![Receiver::Orchard(address.to_raw_address_bytes())]);
ZcashAddress::from_unified(network.address_network().unwrap(), unified_address)
}