zcash-sync/src/scan.rs

209 lines
6.7 KiB
Rust
Raw Normal View History

2022-11-05 05:42:55 -07:00
use crate::chain::get_latest_height;
use crate::db::{AccountViewKey, DbAdapter};
2022-10-04 20:22:04 -07:00
use serde::Serialize;
2022-07-21 18:08:29 -07:00
2021-07-16 01:42:29 -07:00
use crate::transaction::retrieve_tx_info;
2022-11-05 05:42:55 -07:00
use crate::{connect_lightwalletd, CompactBlock, CompactSaplingOutput, CompactTx, DbAdapterBuilder};
2022-10-19 23:24:36 -07:00
use crate::chain::{DecryptNode, download_chain};
2022-07-21 18:08:29 -07:00
2022-09-05 08:05:55 -07:00
use anyhow::anyhow;
2022-08-21 09:40:14 -07:00
use lazy_static::lazy_static;
use std::collections::HashMap;
2021-06-24 05:08:20 -07:00
use std::sync::Arc;
2022-10-28 06:02:34 -07:00
use orchard::note_encryption::OrchardDomain;
use tokio::runtime::{Builder, Runtime};
2021-06-26 02:52:03 -07:00
use tokio::sync::mpsc;
2021-06-24 05:08:20 -07:00
use tokio::sync::Mutex;
2022-09-04 04:19:49 -07:00
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
2022-06-07 09:58:24 -07:00
use zcash_params::coin::{get_coin_chain, CoinType};
2022-11-05 05:42:55 -07:00
use zcash_primitives::consensus::{Network, Parameters};
2021-06-18 01:17:41 -07:00
2022-11-05 05:42:55 -07:00
use zcash_primitives::sapling::Note;
2022-10-27 03:10:51 -07:00
use zcash_primitives::sapling::note_encryption::SaplingDomain;
2022-10-28 06:02:34 -07:00
use crate::orchard::{DecryptedOrchardNote, OrchardDecrypter, OrchardHasher, OrchardViewKey};
2022-10-27 03:10:51 -07:00
use crate::sapling::{DecryptedSaplingNote, SaplingDecrypter, SaplingHasher, SaplingViewKey};
2022-11-05 05:42:55 -07:00
use crate::sync::{Synchronizer, WarpProcessor};
2021-06-18 01:17:41 -07:00
2022-10-04 20:22:04 -07:00
pub struct Blocks(pub Vec<CompactBlock>, pub usize);
2021-06-21 17:33:13 -07:00
lazy_static! {
static ref DECRYPTER_RUNTIME: Runtime = Builder::new_multi_thread().build().unwrap();
}
2021-07-27 22:07:20 -07:00
#[derive(Debug)]
struct TxIdSet(Vec<u32>);
2021-06-21 17:33:13 -07:00
impl std::fmt::Debug for Blocks {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Blocks of len {}", self.0.len())
}
}
2022-10-04 20:22:04 -07:00
#[derive(Clone, Serialize)]
pub struct Progress {
2022-10-19 23:24:36 -07:00
pub height: u32,
pub trial_decryptions: u64,
pub downloaded: usize,
2022-10-04 20:22:04 -07:00
}
pub type ProgressCallback = dyn Fn(Progress) + Send;
2022-06-08 05:48:16 -07:00
pub type AMProgressCallback = Arc<Mutex<ProgressCallback>>;
2021-06-21 17:33:13 -07:00
2021-08-10 04:51:39 -07:00
#[derive(PartialEq, PartialOrd, Debug, Hash, Eq)]
pub struct TxIdHeight {
id_tx: u32,
2021-08-10 04:51:39 -07:00
height: u32,
index: u32,
}
2022-10-27 03:10:51 -07:00
type SaplingSynchronizer = Synchronizer<Network, SaplingDomain<Network>, SaplingViewKey, DecryptedSaplingNote,
SaplingDecrypter<Network>, SaplingHasher>;
2022-10-28 06:02:34 -07:00
type OrchardSynchronizer = Synchronizer<Network, OrchardDomain, OrchardViewKey, DecryptedOrchardNote,
OrchardDecrypter<Network>, OrchardHasher>;
2022-10-27 03:10:51 -07:00
pub async fn sync_async<'a>(
coin_type: CoinType,
_chunk_size: u32,
2022-11-05 05:42:55 -07:00
_get_tx: bool, // TODO
2022-10-27 03:10:51 -07:00
db_path: &'a str,
target_height_offset: u32,
max_cost: u32,
2022-11-05 05:42:55 -07:00
_progress_callback: AMProgressCallback, // TODO
2022-10-27 03:10:51 -07:00
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 mut client = connect_lightwalletd(&ld_url).await?;
2022-10-28 06:02:34 -07:00
let (start_height, prev_hash, sapling_vks, orchard_vks) = {
2022-10-27 03:10:51 -07:00
let db = DbAdapter::new(coin_type, &db_path)?;
let height = db.get_db_height()?;
let hash = db.get_db_hash(height)?;
let sapling_vks = db.get_sapling_fvks()?;
let orchard_vks = db.get_orchard_fvks()?;
2022-10-28 06:02:34 -07:00
(height, hash, sapling_vks, orchard_vks)
2022-10-27 03:10:51 -07:00
};
let end_height = get_latest_height(&mut client).await?;
let end_height = (end_height - target_height_offset).max(start_height);
if start_height >= end_height {
return Ok(());
}
2022-11-04 18:58:35 -07:00
let mut height = start_height;
2022-10-27 03:10:51 -07:00
let (blocks_tx, mut blocks_rx) = mpsc::channel::<Blocks>(1);
tokio::spawn(async move {
download_chain(&mut client, start_height, end_height, prev_hash, max_cost, cancel, blocks_tx).await?;
Ok::<_, anyhow::Error>(())
});
let db_builder = DbAdapterBuilder { 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);
let last_block = blocks.0.last().unwrap();
2022-10-28 06:02:34 -07:00
let last_hash: [u8; 32] = last_block.hash.clone().try_into().unwrap();
2022-10-27 03:10:51 -07:00
let last_height = last_block.height as u32;
let last_timestamp = last_block.time;
2022-10-28 06:02:34 -07:00
// Sapling
log::info!("Sapling");
2022-10-28 06:02:34 -07:00
{
let decrypter = SaplingDecrypter::new(network);
let warper = WarpProcessor::new(SaplingHasher::default());
let mut synchronizer = SaplingSynchronizer::new(
decrypter,
warper,
sapling_vks.clone(),
db_builder.clone(),
"sapling".to_string(),
);
2022-11-04 18:58:35 -07:00
synchronizer.initialize(height)?;
2022-10-28 06:02:34 -07:00
synchronizer.process(&blocks.0)?;
}
// Orchard
log::info!("Orchard");
2022-10-28 06:02:34 -07:00
{
let decrypter = OrchardDecrypter::new(network);
let warper = WarpProcessor::new(OrchardHasher::new());
let mut synchronizer = OrchardSynchronizer::new(
decrypter,
warper,
orchard_vks.clone(),
db_builder.clone(),
"orchard".to_string(),
);
2022-11-04 18:58:35 -07:00
synchronizer.initialize(height)?;
2022-10-28 06:02:34 -07:00
synchronizer.process(&blocks.0)?;
}
2022-10-27 03:10:51 -07:00
let db = db_builder.build()?;
2022-10-28 06:02:34 -07:00
db.store_block_timestamp(last_height, &last_hash, last_timestamp)?;
2022-11-04 18:58:35 -07:00
height = last_height;
2022-10-27 03:10:51 -07:00
}
Ok(())
}
2021-07-09 06:33:05 -07:00
pub async fn latest_height(ld_url: &str) -> anyhow::Result<u32> {
let mut client = connect_lightwalletd(ld_url).await?;
2021-06-24 05:08:20 -07:00
let height = get_latest_height(&mut client).await?;
Ok(height)
2021-06-21 17:33:13 -07:00
}
2022-09-04 04:19:49 -07:00
#[allow(dead_code)]
// test function
2022-09-05 08:05:55 -07:00
pub fn trial_decrypt_one(
network: &Network,
height: u32,
fvk: &str,
cmu: &[u8],
epk: &[u8],
ciphertext: &[u8],
) -> anyhow::Result<Option<Note>> {
2022-09-04 04:19:49 -07:00
let mut vks = HashMap::new();
2022-09-05 08:05:55 -07:00
let fvk =
decode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), &fvk)
2022-11-05 05:42:55 -07:00
.map_err(|_| anyhow!("Bech32 Decode Error"))?;
2022-09-04 04:19:49 -07:00
let ivk = fvk.fvk.vk.ivk();
2022-09-05 08:05:55 -07:00
vks.insert(
0,
AccountViewKey {
fvk,
ivk,
viewonly: false,
},
);
2022-09-04 04:19:49 -07:00
let dn = DecryptNode::new(vks);
let block = vec![CompactBlock {
proto_version: 0, // don't care about most of these fields
height: height as u64,
hash: vec![],
prev_hash: vec![],
time: 0,
header: vec![],
2022-09-05 08:05:55 -07:00
vtx: vec![CompactTx {
index: 0,
hash: vec![],
fee: 0,
spends: vec![],
actions: vec![],
outputs: vec![CompactSaplingOutput {
cmu: cmu.to_vec(),
epk: epk.to_vec(),
ciphertext: ciphertext.to_vec(),
}],
}],
2022-09-04 04:19:49 -07:00
}];
let decrypted_block = dn.decrypt_blocks(network, block);
let decrypted_block = decrypted_block.first().unwrap();
let note = decrypted_block.notes.first().map(|dn| dn.note.clone());
Ok(note)
2022-09-05 08:05:55 -07:00
}