zcash-sync/src/scan.rs

277 lines
8.0 KiB
Rust
Raw Normal View History

2022-11-05 05:42:55 -07:00
use crate::chain::get_latest_height;
2022-11-05 18:49:17 -07:00
use crate::db::AccountViewKey;
2022-07-21 18:08:29 -07:00
2022-11-06 04:50:51 -08:00
use crate::chain::{download_chain, DecryptNode};
2022-11-17 01:13:51 -08:00
use crate::transaction::get_transaction_details;
2022-11-06 04:50:51 -08:00
use crate::{
2022-11-15 02:21:47 -08:00
connect_lightwalletd, ChainError, CoinConfig, CompactBlock, CompactSaplingOutput, CompactTx,
2022-11-06 04:50:51 -08:00
DbAdapterBuilder,
};
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;
2022-11-06 04:50:51 -08:00
use orchard::note_encryption::OrchardDomain;
use std::collections::HashMap;
2021-06-24 05:08:20 -07:00
use std::sync::Arc;
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-11-05 05:42:55 -07:00
use zcash_primitives::consensus::{Network, Parameters};
2021-06-18 01:17:41 -07:00
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};
2022-11-06 04:50:51 -08:00
use zcash_primitives::sapling::note_encryption::SaplingDomain;
use zcash_primitives::sapling::Note;
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())
}
}
2023-03-09 17:10:28 -08:00
#[derive(Clone)]
2022-10-04 20:22:04 -07:00
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-11-06 04:50:51 -08:00
type SaplingSynchronizer = Synchronizer<
Network,
SaplingDomain<Network>,
SaplingViewKey,
DecryptedSaplingNote,
SaplingDecrypter<Network>,
SaplingHasher,
>;
type OrchardSynchronizer = Synchronizer<
Network,
OrchardDomain,
OrchardViewKey,
DecryptedOrchardNote,
OrchardDecrypter<Network>,
OrchardHasher,
>;
2022-10-28 06:02:34 -07:00
2022-10-27 03:10:51 -07:00
pub async fn sync_async<'a>(
2022-11-05 18:49:17 -07:00
coin: u8,
2022-11-15 02:21:47 -08:00
get_tx: bool,
target_height_offset: u32,
max_cost: u32,
progress_callback: AMProgressCallback, // TODO
cancel: &'static std::sync::Mutex<bool>,
) -> anyhow::Result<()> {
let result = sync_async_inner(
coin,
get_tx,
target_height_offset,
max_cost,
progress_callback,
cancel,
)
.await;
if let Err(ref e) = result {
if let Some(ChainError::Reorg) = e.downcast_ref::<ChainError>() {
log::info!("Drop latest checkpoint");
let c = CoinConfig::get(coin);
let mut db = c.db()?;
db.drop_last_checkpoint()?;
}
}
result
}
async fn sync_async_inner<'a>(
coin: u8,
get_tx: bool,
2022-10-27 03:10:51 -07:00
target_height_offset: u32,
max_cost: u32,
2022-11-12 19:27:52 -08:00
progress_callback: AMProgressCallback, // TODO
2022-10-27 03:10:51 -07:00
cancel: &'static std::sync::Mutex<bool>,
) -> anyhow::Result<()> {
2022-11-05 18:49:17 -07:00
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();
2022-10-27 03:10:51 -07:00
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-11-05 18:49:17 -07:00
let db = c.db.as_ref().unwrap();
let db = db.lock().unwrap();
2022-10-27 03:10:51 -07:00
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);
2022-11-15 02:21:47 -08:00
let downloader = tokio::spawn(async move {
2022-11-06 04:50:51 -08:00
download_chain(
&mut client,
start_height,
end_height,
prev_hash,
max_cost,
cancel,
blocks_tx,
)
.await?;
2022-10-27 03:10:51 -07:00
Ok::<_, anyhow::Error>(())
});
2022-11-06 04:50:51 -08:00
let db_builder = DbAdapterBuilder {
coin_type: c.coin_type,
db_path: db_path.clone(),
2023-03-05 17:15:11 -08:00
passwd: c.passwd.clone(),
2022-11-06 04:50:51 -08:00
};
2022-11-12 19:27:52 -08:00
let mut progress = Progress {
height: 0,
trial_decryptions: 0,
downloaded: 0,
};
2022-10-27 03:10:51 -07:00
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-11-12 19:27:52 -08:00
progress.downloaded += blocks.1;
progress.height = last_height;
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-11-12 19:27:52 -08:00
progress.trial_decryptions += synchronizer.process(&blocks.0)? as u64;
2022-10-28 06:02:34 -07:00
}
2022-11-13 01:00:21 -08:00
if c.chain.has_unified() {
// Orchard
log::info!("Orchard");
{
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(),
);
synchronizer.initialize(height)?;
2022-11-15 02:21:47 -08:00
log::info!("Process orchard start");
2022-11-13 01:00:21 -08:00
progress.trial_decryptions += synchronizer.process(&blocks.0)? as u64;
2022-11-15 02:21:47 -08:00
log::info!("Process orchard end");
2022-11-13 01:00:21 -08:00
}
2022-10-28 06:02:34 -07:00
}
2022-10-27 03:10:51 -07:00
2022-12-21 14:54:25 -08: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-11-12 19:27:52 -08:00
let cb = progress_callback.lock().await;
cb(progress.clone());
2022-10-27 03:10:51 -07:00
}
2022-11-15 02:21:47 -08:00
downloader.await??;
2022-11-05 18:49:17 -07:00
if get_tx {
get_transaction_details(coin).await?;
}
2022-12-21 14:54:25 -08:00
let mut db = db_builder.build()?;
db.purge_old_witnesses(height)?;
2022-11-15 23:44:51 -08:00
2022-10-27 03:10:51 -07:00
Ok(())
}
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
}