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;
|
2021-09-12 21:08:31 -07:00
|
|
|
use std::collections::HashMap;
|
2021-06-24 05:08:20 -07:00
|
|
|
use std::sync::Arc;
|
2022-08-17 18:23:23 -07:00
|
|
|
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
|
|
|
|
2022-08-17 18:23:23 -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 {
|
2021-09-12 21:08:31 -07:00
|
|
|
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)?;
|
2022-10-30 03:03:38 -07:00
|
|
|
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
|
2022-10-30 03:03:38 -07:00
|
|
|
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 =
|
2022-10-21 21:01:29 -07:00
|
|
|
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
|
|
|
}
|