2023-03-09 17:10:28 -08:00
|
|
|
use crate::db::data_generated::fb::Servers as fbServers;
|
2021-07-30 01:11:44 -07:00
|
|
|
use crate::db::AccountViewKey;
|
2021-06-17 09:56:20 -07:00
|
|
|
use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
|
|
|
use crate::lw_rpc::*;
|
2022-08-03 08:30:34 -07:00
|
|
|
use crate::scan::Blocks;
|
2023-02-21 00:36:29 -08:00
|
|
|
use crate::DbAdapter;
|
2022-07-13 20:22:12 -07:00
|
|
|
use futures::{future, FutureExt};
|
2022-08-21 23:36:29 -07:00
|
|
|
use rand::prelude::SliceRandom;
|
|
|
|
use rand::rngs::OsRng;
|
2021-06-18 01:17:41 -07:00
|
|
|
use rayon::prelude::*;
|
2021-07-16 01:42:29 -07:00
|
|
|
use std::collections::HashMap;
|
2022-08-03 08:30:34 -07:00
|
|
|
use std::convert::TryInto;
|
2022-03-14 22:57:10 -07:00
|
|
|
use std::marker::PhantomData;
|
2022-08-30 07:00:15 -07:00
|
|
|
use std::sync::Mutex;
|
2022-07-13 20:22:12 -07:00
|
|
|
use std::time::Duration;
|
2021-06-26 02:52:03 -07:00
|
|
|
use std::time::Instant;
|
2022-08-03 08:30:34 -07:00
|
|
|
use sysinfo::{System, SystemExt};
|
2021-07-16 01:42:29 -07:00
|
|
|
use thiserror::Error;
|
2022-07-09 09:12:34 -07:00
|
|
|
use tokio::sync::mpsc::Sender;
|
2022-07-13 20:22:12 -07:00
|
|
|
use tokio::time::timeout;
|
2021-06-26 02:52:03 -07:00
|
|
|
use tonic::transport::{Certificate, Channel, ClientTlsConfig};
|
2021-06-17 09:56:20 -07:00
|
|
|
use tonic::Request;
|
2022-03-14 22:57:10 -07:00
|
|
|
use zcash_note_encryption::batch::try_compact_note_decryption;
|
2022-06-07 09:58:24 -07:00
|
|
|
use zcash_note_encryption::{Domain, EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE};
|
2022-03-07 06:47:06 -08:00
|
|
|
use zcash_primitives::consensus::{BlockHeight, Network, NetworkUpgrade, Parameters};
|
2021-06-18 01:17:41 -07:00
|
|
|
use zcash_primitives::merkle_tree::{CommitmentTree, IncrementalWitness};
|
2023-03-10 22:43:10 -08:00
|
|
|
use zcash_primitives::sapling::note::ExtractedNoteCommitment;
|
2022-10-21 21:01:29 -07:00
|
|
|
use zcash_primitives::sapling::note_encryption::{PreparedIncomingViewingKey, SaplingDomain};
|
2021-06-21 17:33:13 -07:00
|
|
|
use zcash_primitives::sapling::{Node, Note, PaymentAddress};
|
2021-06-17 09:56:20 -07:00
|
|
|
use zcash_primitives::transaction::components::sapling::CompactOutputDescription;
|
2021-06-21 17:33:13 -07:00
|
|
|
use zcash_primitives::zip32::ExtendedFullViewingKey;
|
2021-06-17 09:56:20 -07:00
|
|
|
|
2022-08-17 04:05:53 -07:00
|
|
|
#[cfg(feature = "cuda")]
|
2022-08-21 09:40:14 -07:00
|
|
|
use crate::gpu::cuda::{CudaProcessor, CUDA_CONTEXT};
|
2022-08-17 20:42:06 -07:00
|
|
|
#[cfg(feature = "apple_metal")]
|
|
|
|
use crate::gpu::metal::MetalProcessor;
|
2022-11-05 05:42:55 -07:00
|
|
|
use crate::gpu::USE_GPU;
|
2022-08-17 04:05:53 -07:00
|
|
|
|
2021-06-17 09:56:20 -07:00
|
|
|
pub async fn get_latest_height(
|
|
|
|
client: &mut CompactTxStreamerClient<Channel>,
|
|
|
|
) -> anyhow::Result<u32> {
|
|
|
|
let chainspec = ChainSpec {};
|
|
|
|
let rep = client.get_latest_block(Request::new(chainspec)).await?;
|
|
|
|
let block_id = rep.into_inner();
|
|
|
|
Ok(block_id.height as u32)
|
|
|
|
}
|
|
|
|
|
2022-06-07 09:58:24 -07:00
|
|
|
pub async fn get_activation_date(
|
|
|
|
network: &Network,
|
|
|
|
client: &mut CompactTxStreamerClient<Channel>,
|
|
|
|
) -> anyhow::Result<u32> {
|
2022-03-31 22:20:53 -07:00
|
|
|
let height = network.activation_height(NetworkUpgrade::Sapling).unwrap();
|
|
|
|
let time = get_block_date(client, u32::from(height)).await?;
|
|
|
|
Ok(time)
|
|
|
|
}
|
|
|
|
|
2022-06-07 09:58:24 -07:00
|
|
|
pub async fn get_block_date(
|
|
|
|
client: &mut CompactTxStreamerClient<Channel>,
|
|
|
|
height: u32,
|
|
|
|
) -> anyhow::Result<u32> {
|
|
|
|
let block = client
|
|
|
|
.get_block(Request::new(BlockId {
|
|
|
|
height: height as u64,
|
|
|
|
hash: vec![],
|
|
|
|
}))
|
|
|
|
.await?
|
|
|
|
.into_inner();
|
2022-03-31 22:20:53 -07:00
|
|
|
Ok(block.time)
|
|
|
|
}
|
|
|
|
|
2022-06-07 09:58:24 -07:00
|
|
|
pub async fn get_block_by_time(
|
|
|
|
network: &Network,
|
|
|
|
client: &mut CompactTxStreamerClient<Channel>,
|
|
|
|
time: u32,
|
|
|
|
) -> anyhow::Result<u32> {
|
2022-03-31 22:20:53 -07:00
|
|
|
let mut start = u32::from(network.activation_height(NetworkUpgrade::Sapling).unwrap());
|
|
|
|
let mut end = get_latest_height(client).await?;
|
2022-06-07 09:58:24 -07:00
|
|
|
if time <= get_block_date(client, start).await? {
|
|
|
|
return Ok(0);
|
|
|
|
}
|
|
|
|
if time >= get_block_date(client, end).await? {
|
|
|
|
return Ok(end);
|
|
|
|
}
|
2022-03-31 22:20:53 -07:00
|
|
|
let mut block_mid;
|
|
|
|
while end - start >= 1000 {
|
|
|
|
block_mid = (start + end) / 2;
|
|
|
|
let mid = get_block_date(client, block_mid).await?;
|
|
|
|
if time < mid {
|
|
|
|
end = block_mid - 1;
|
2022-06-07 09:58:24 -07:00
|
|
|
} else if time > mid {
|
2022-03-31 22:20:53 -07:00
|
|
|
start = block_mid + 1;
|
2022-06-07 09:58:24 -07:00
|
|
|
} else {
|
2022-03-31 22:20:53 -07:00
|
|
|
return Ok(block_mid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(start)
|
|
|
|
}
|
|
|
|
|
2021-06-26 02:52:03 -07:00
|
|
|
#[derive(Error, Debug)]
|
|
|
|
pub enum ChainError {
|
|
|
|
#[error("Blockchain reorganization")]
|
|
|
|
Reorg,
|
|
|
|
#[error("Synchronizer busy")]
|
|
|
|
Busy,
|
|
|
|
}
|
|
|
|
|
2022-08-03 08:30:34 -07:00
|
|
|
fn get_mem_per_output() -> usize {
|
|
|
|
if cfg!(feature = "cuda") {
|
2022-08-13 19:56:42 -07:00
|
|
|
250
|
2022-08-03 08:30:34 -07:00
|
|
|
} else {
|
|
|
|
5
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "cuda")]
|
|
|
|
fn get_available_memory() -> anyhow::Result<usize> {
|
2022-08-17 04:05:53 -07:00
|
|
|
let cuda = CUDA_CONTEXT.lock().unwrap();
|
2022-08-03 08:30:34 -07:00
|
|
|
if let Some(cuda) = cuda.as_ref() {
|
|
|
|
cuda.total_memory()
|
|
|
|
} else {
|
|
|
|
get_system_available_memory()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "cuda"))]
|
|
|
|
fn get_available_memory() -> anyhow::Result<usize> {
|
|
|
|
get_system_available_memory()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_system_available_memory() -> anyhow::Result<usize> {
|
|
|
|
let mut sys = System::new();
|
|
|
|
sys.refresh_memory();
|
|
|
|
let mem_available = sys.available_memory() as usize;
|
|
|
|
Ok(mem_available)
|
|
|
|
}
|
|
|
|
|
|
|
|
const MAX_OUTPUTS_PER_CHUNKS: usize = 200_000;
|
|
|
|
|
2021-06-17 09:56:20 -07:00
|
|
|
/* download [start_height+1, end_height] inclusive */
|
2022-08-03 08:30:34 -07:00
|
|
|
#[allow(unused_variables)]
|
2021-06-17 09:56:20 -07:00
|
|
|
pub async fn download_chain(
|
|
|
|
client: &mut CompactTxStreamerClient<Channel>,
|
|
|
|
start_height: u32,
|
2021-06-18 01:17:41 -07:00
|
|
|
end_height: u32,
|
2021-06-26 02:52:03 -07:00
|
|
|
mut prev_hash: Option<[u8; 32]>,
|
2022-08-31 08:35:21 -07:00
|
|
|
max_cost: u32,
|
2022-08-30 07:00:15 -07:00
|
|
|
cancel: &'static Mutex<bool>,
|
2022-10-27 03:10:51 -07:00
|
|
|
handler: Sender<Blocks>,
|
2022-07-09 09:12:34 -07:00
|
|
|
) -> anyhow::Result<()> {
|
2022-08-03 08:30:34 -07:00
|
|
|
let outputs_per_chunk = get_available_memory()? / get_mem_per_output();
|
|
|
|
let outputs_per_chunk = outputs_per_chunk.min(MAX_OUTPUTS_PER_CHUNKS);
|
|
|
|
log::info!("Outputs per chunk = {}", outputs_per_chunk);
|
2022-08-31 09:08:53 -07:00
|
|
|
log::info!("max_cost = {}", max_cost);
|
2022-08-03 08:30:34 -07:00
|
|
|
|
2022-07-09 09:12:34 -07:00
|
|
|
let mut output_count = 0;
|
2021-06-17 09:56:20 -07:00
|
|
|
let mut cbs: Vec<CompactBlock> = Vec::new();
|
2022-07-12 00:28:58 -07:00
|
|
|
let range = BlockRange {
|
|
|
|
start: Some(BlockId {
|
|
|
|
height: (start_height + 1) as u64,
|
|
|
|
hash: vec![],
|
|
|
|
}),
|
|
|
|
end: Some(BlockId {
|
|
|
|
height: end_height as u64,
|
|
|
|
hash: vec![],
|
|
|
|
}),
|
2022-08-31 08:35:21 -07:00
|
|
|
spam_filter_threshold: max_cost as u64,
|
2022-07-12 00:28:58 -07:00
|
|
|
};
|
2022-10-04 20:22:04 -07:00
|
|
|
let mut total_block_size = 0;
|
2022-07-12 00:28:58 -07:00
|
|
|
let mut block_stream = client
|
|
|
|
.get_block_range(Request::new(range))
|
|
|
|
.await?
|
|
|
|
.into_inner();
|
|
|
|
while let Some(mut block) = block_stream.message().await? {
|
2022-08-16 07:47:48 -07:00
|
|
|
let block_size = get_block_size(&block);
|
2022-10-04 20:22:04 -07:00
|
|
|
total_block_size += block_size;
|
2022-08-30 07:00:15 -07:00
|
|
|
let c = *cancel.lock().unwrap();
|
|
|
|
if c {
|
2022-07-21 18:08:29 -07:00
|
|
|
log::info!("Canceling download");
|
|
|
|
break;
|
|
|
|
}
|
2022-07-12 00:28:58 -07:00
|
|
|
if prev_hash.is_some() && block.prev_hash.as_slice() != prev_hash.unwrap() {
|
2022-07-13 21:21:20 -07:00
|
|
|
log::warn!(
|
|
|
|
"Reorg: {} != {}",
|
|
|
|
hex::encode(block.prev_hash.as_slice()),
|
|
|
|
hex::encode(prev_hash.unwrap())
|
|
|
|
);
|
2022-07-12 00:28:58 -07:00
|
|
|
anyhow::bail!(ChainError::Reorg);
|
|
|
|
}
|
|
|
|
let mut ph = [0u8; 32];
|
|
|
|
ph.copy_from_slice(&block.hash);
|
|
|
|
prev_hash = Some(ph);
|
2022-09-01 07:30:42 -07:00
|
|
|
for tx in block.vtx.iter_mut() {
|
2022-08-31 08:35:21 -07:00
|
|
|
let mut skipped = false;
|
2022-11-12 17:39:12 -08:00
|
|
|
if tx.outputs.len() + tx.actions.len() > max_cost as usize {
|
2022-09-01 07:30:42 -07:00
|
|
|
for co in tx.outputs.iter_mut() {
|
|
|
|
co.epk.clear();
|
|
|
|
co.ciphertext.clear();
|
2022-08-30 07:00:15 -07:00
|
|
|
}
|
2022-11-12 17:39:12 -08:00
|
|
|
for a in tx.actions.iter_mut() {
|
|
|
|
a.ephemeral_key.clear();
|
|
|
|
a.ciphertext.clear();
|
|
|
|
}
|
|
|
|
skipped = true;
|
2022-08-30 07:00:15 -07:00
|
|
|
}
|
2022-08-31 08:35:21 -07:00
|
|
|
if skipped {
|
2022-09-01 07:30:42 -07:00
|
|
|
log::debug!("Output skipped {}", tx.outputs.len());
|
2022-08-31 08:35:21 -07:00
|
|
|
}
|
2022-07-12 00:28:58 -07:00
|
|
|
}
|
2022-07-09 09:12:34 -07:00
|
|
|
|
2022-11-12 17:39:12 -08:00
|
|
|
let block_output_count: usize = block
|
|
|
|
.vtx
|
|
|
|
.iter()
|
|
|
|
.map(|tx| tx.outputs.len() + tx.actions.len())
|
|
|
|
.sum();
|
2022-08-03 08:30:34 -07:00
|
|
|
if output_count + block_output_count > outputs_per_chunk {
|
2022-07-12 00:28:58 -07:00
|
|
|
// output
|
|
|
|
let out = cbs;
|
|
|
|
cbs = Vec::new();
|
2022-10-27 03:10:51 -07:00
|
|
|
let blocks = Blocks(out, total_block_size);
|
|
|
|
if !blocks.0.is_empty() {
|
|
|
|
let _ = handler.send(blocks).await;
|
|
|
|
}
|
2022-07-12 00:28:58 -07:00
|
|
|
output_count = 0;
|
2022-10-04 20:22:04 -07:00
|
|
|
total_block_size = 0;
|
2021-06-17 09:56:20 -07:00
|
|
|
}
|
2022-07-12 00:28:58 -07:00
|
|
|
|
|
|
|
cbs.push(block);
|
|
|
|
output_count += block_output_count;
|
2021-06-17 09:56:20 -07:00
|
|
|
}
|
2022-10-27 03:10:51 -07:00
|
|
|
let blocks = Blocks(cbs, total_block_size);
|
|
|
|
if !blocks.0.is_empty() {
|
|
|
|
let _ = handler.send(blocks).await;
|
|
|
|
}
|
2022-07-09 09:12:34 -07:00
|
|
|
Ok(())
|
2021-06-17 09:56:20 -07:00
|
|
|
}
|
|
|
|
|
2022-08-16 07:47:48 -07:00
|
|
|
fn get_block_size(block: &CompactBlock) -> usize {
|
|
|
|
block
|
|
|
|
.vtx
|
|
|
|
.iter()
|
|
|
|
.map(|tx| {
|
|
|
|
tx.spends.len() * 32
|
|
|
|
+ tx.outputs.len() * (32 * 2 + 52)
|
|
|
|
+ tx.actions.len() * (32 * 3 + 52)
|
|
|
|
+ 8
|
|
|
|
+ 32
|
|
|
|
+ 4
|
|
|
|
})
|
|
|
|
.sum::<usize>()
|
|
|
|
+ 16
|
|
|
|
+ 32 * 2
|
|
|
|
}
|
|
|
|
|
2021-06-18 01:17:41 -07:00
|
|
|
pub struct DecryptNode {
|
2021-07-27 22:07:20 -07:00
|
|
|
vks: HashMap<u32, AccountViewKey>,
|
2021-06-17 09:56:20 -07:00
|
|
|
}
|
|
|
|
|
2021-06-26 02:52:03 -07:00
|
|
|
#[derive(Eq, Hash, PartialEq, Copy, Clone)]
|
2021-06-24 05:08:20 -07:00
|
|
|
pub struct Nf(pub [u8; 32]);
|
|
|
|
|
2021-06-29 00:04:12 -07:00
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct NfRef {
|
|
|
|
pub id_note: u32,
|
2021-07-16 01:42:29 -07:00
|
|
|
pub account: u32,
|
2021-06-29 00:04:12 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 18:23:23 -07:00
|
|
|
pub struct DecryptedBlock {
|
2021-06-18 01:17:41 -07:00
|
|
|
pub height: u32,
|
|
|
|
pub notes: Vec<DecryptedNote>,
|
|
|
|
pub count_outputs: u32,
|
2021-06-24 05:08:20 -07:00
|
|
|
pub spends: Vec<Nf>,
|
2022-08-17 18:23:23 -07:00
|
|
|
pub compact_block: CompactBlock,
|
2022-03-14 22:57:10 -07:00
|
|
|
pub elapsed: usize,
|
2021-06-18 01:17:41 -07:00
|
|
|
}
|
|
|
|
|
2021-06-21 17:33:13 -07:00
|
|
|
#[derive(Clone)]
|
2021-06-18 01:17:41 -07:00
|
|
|
pub struct DecryptedNote {
|
2021-06-29 00:04:12 -07:00
|
|
|
pub account: u32,
|
2021-06-21 17:33:13 -07:00
|
|
|
pub ivk: ExtendedFullViewingKey,
|
2021-06-18 01:17:41 -07:00
|
|
|
pub note: Note,
|
2021-06-21 17:33:13 -07:00
|
|
|
pub pa: PaymentAddress,
|
2021-06-24 05:08:20 -07:00
|
|
|
pub position_in_block: usize,
|
2021-07-27 22:07:20 -07:00
|
|
|
pub viewonly: bool,
|
2021-06-21 17:33:13 -07:00
|
|
|
|
|
|
|
pub height: u32,
|
|
|
|
pub txid: Vec<u8>,
|
|
|
|
pub tx_index: usize,
|
|
|
|
pub output_index: usize,
|
2021-06-18 01:17:41 -07:00
|
|
|
}
|
|
|
|
|
2022-11-17 17:02:03 -08:00
|
|
|
#[allow(dead_code)]
|
2022-06-11 05:29:14 -07:00
|
|
|
pub fn to_output_description(co: &CompactSaplingOutput) -> CompactOutputDescription {
|
2022-08-03 08:30:34 -07:00
|
|
|
let cmu: [u8; 32] = co.cmu.clone().try_into().unwrap();
|
|
|
|
let epk: [u8; 32] = co.epk.clone().try_into().unwrap();
|
|
|
|
let enc_ciphertext: [u8; 52] = co.ciphertext.clone().try_into().unwrap();
|
2022-06-08 05:48:16 -07:00
|
|
|
|
|
|
|
CompactOutputDescription {
|
2022-03-14 19:40:08 -07:00
|
|
|
ephemeral_key: EphemeralKeyBytes::from(epk),
|
2023-03-10 22:43:10 -08:00
|
|
|
cmu: ExtractedNoteCommitment::from_bytes(&cmu).unwrap(),
|
2022-03-14 19:40:08 -07:00
|
|
|
enc_ciphertext,
|
2022-06-08 05:48:16 -07:00
|
|
|
}
|
2021-06-26 02:52:03 -07:00
|
|
|
}
|
|
|
|
|
2022-03-14 22:57:10 -07:00
|
|
|
struct AccountOutput<'a, N: Parameters> {
|
|
|
|
epk: EphemeralKeyBytes,
|
|
|
|
cmu: <SaplingDomain<N> as Domain>::ExtractedCommitmentBytes,
|
|
|
|
ciphertext: [u8; COMPACT_NOTE_SIZE],
|
2022-03-15 01:04:37 -07:00
|
|
|
tx_index: usize,
|
2022-03-14 22:57:10 -07:00
|
|
|
output_index: usize,
|
|
|
|
block_output_index: usize,
|
|
|
|
vtx: &'a CompactTx,
|
|
|
|
_phantom: PhantomData<N>,
|
|
|
|
}
|
|
|
|
|
2022-06-07 09:58:24 -07:00
|
|
|
impl<'a, N: Parameters> AccountOutput<'a, N> {
|
|
|
|
fn new(
|
|
|
|
tx_index: usize,
|
|
|
|
output_index: usize,
|
|
|
|
block_output_index: usize,
|
|
|
|
vtx: &'a CompactTx,
|
2022-06-11 05:29:14 -07:00
|
|
|
co: &CompactSaplingOutput,
|
2022-06-07 09:58:24 -07:00
|
|
|
) -> Self {
|
2022-03-14 22:57:10 -07:00
|
|
|
let mut epk_bytes = [0u8; 32];
|
|
|
|
epk_bytes.copy_from_slice(&co.epk);
|
|
|
|
let epk = EphemeralKeyBytes::from(epk_bytes);
|
|
|
|
let mut cmu_bytes = [0u8; 32];
|
|
|
|
cmu_bytes.copy_from_slice(&co.cmu);
|
2022-06-08 05:48:16 -07:00
|
|
|
let cmu = cmu_bytes;
|
2022-03-14 22:57:10 -07:00
|
|
|
let mut ciphertext_bytes = [0u8; COMPACT_NOTE_SIZE];
|
|
|
|
ciphertext_bytes.copy_from_slice(&co.ciphertext);
|
|
|
|
|
|
|
|
AccountOutput {
|
2022-03-15 01:04:37 -07:00
|
|
|
tx_index,
|
2022-03-14 22:57:10 -07:00
|
|
|
output_index,
|
|
|
|
block_output_index,
|
|
|
|
vtx,
|
|
|
|
epk,
|
|
|
|
cmu,
|
|
|
|
ciphertext: ciphertext_bytes,
|
|
|
|
_phantom: PhantomData::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-07 09:58:24 -07:00
|
|
|
impl<'a, N: Parameters> ShieldedOutput<SaplingDomain<N>, COMPACT_NOTE_SIZE>
|
|
|
|
for AccountOutput<'a, N>
|
|
|
|
{
|
2022-03-14 22:57:10 -07:00
|
|
|
fn ephemeral_key(&self) -> EphemeralKeyBytes {
|
|
|
|
self.epk.clone()
|
|
|
|
}
|
|
|
|
fn cmstar_bytes(&self) -> <SaplingDomain<N> as Domain>::ExtractedCommitmentBytes {
|
|
|
|
self.cmu
|
|
|
|
}
|
|
|
|
fn enc_ciphertext(&self) -> &[u8; COMPACT_NOTE_SIZE] {
|
|
|
|
&self.ciphertext
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-07 06:47:06 -08:00
|
|
|
fn decrypt_notes<'a, N: Parameters>(
|
|
|
|
network: &N,
|
2022-08-17 18:23:23 -07:00
|
|
|
block: CompactBlock,
|
2022-03-14 22:57:10 -07:00
|
|
|
vks: &[(&u32, &AccountViewKey)],
|
2022-08-17 18:23:23 -07:00
|
|
|
) -> DecryptedBlock {
|
2021-06-17 09:56:20 -07:00
|
|
|
let height = BlockHeight::from_u32(block.height as u32);
|
2021-06-18 01:17:41 -07:00
|
|
|
let mut count_outputs = 0u32;
|
2021-06-24 05:08:20 -07:00
|
|
|
let mut spends: Vec<Nf> = vec![];
|
2021-06-18 01:17:41 -07:00
|
|
|
let mut notes: Vec<DecryptedNote> = vec![];
|
2022-11-06 04:50:51 -08:00
|
|
|
let vvks: Vec<_> = vks
|
|
|
|
.iter()
|
|
|
|
.map(|vk| PreparedIncomingViewingKey::new(&vk.1.ivk))
|
|
|
|
.collect();
|
2022-03-14 22:57:10 -07:00
|
|
|
let mut outputs: Vec<(SaplingDomain<N>, AccountOutput<N>)> = vec![];
|
2021-06-21 17:33:13 -07:00
|
|
|
for (tx_index, vtx) in block.vtx.iter().enumerate() {
|
2021-06-24 05:08:20 -07:00
|
|
|
for cs in vtx.spends.iter() {
|
|
|
|
let mut nf = [0u8; 32];
|
|
|
|
nf.copy_from_slice(&cs.nf);
|
|
|
|
spends.push(Nf(nf));
|
|
|
|
}
|
|
|
|
|
2022-09-01 07:30:42 -07:00
|
|
|
if let Some(fco) = vtx.outputs.first() {
|
|
|
|
if !fco.epk.is_empty() {
|
|
|
|
for (output_index, co) in vtx.outputs.iter().enumerate() {
|
|
|
|
let domain = SaplingDomain::<N>::for_height(network.clone(), height);
|
|
|
|
let output = AccountOutput::<N>::new(
|
|
|
|
tx_index,
|
|
|
|
output_index,
|
|
|
|
count_outputs as usize,
|
|
|
|
vtx,
|
|
|
|
co,
|
|
|
|
);
|
|
|
|
outputs.push((domain, output));
|
|
|
|
|
|
|
|
count_outputs += 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// we filter by transaction, therefore if one epk is empty, every epk is empty
|
|
|
|
// log::info!("Spam Filter tx {}", hex::encode(&vtx.hash));
|
|
|
|
count_outputs += vtx.outputs.len() as u32;
|
2022-07-30 16:11:58 -07:00
|
|
|
}
|
2021-06-17 09:56:20 -07:00
|
|
|
}
|
|
|
|
}
|
2022-03-14 22:57:10 -07:00
|
|
|
|
|
|
|
let start = Instant::now();
|
2022-06-07 09:58:24 -07:00
|
|
|
let notes_decrypted =
|
|
|
|
try_compact_note_decryption::<SaplingDomain<N>, AccountOutput<N>>(&vvks, &outputs);
|
2022-03-14 22:57:10 -07:00
|
|
|
let elapsed = start.elapsed().as_millis() as usize;
|
|
|
|
|
|
|
|
for (pos, opt_note) in notes_decrypted.iter().enumerate() {
|
2022-10-21 21:01:29 -07:00
|
|
|
if let Some(((note, pa), _)) = opt_note {
|
2022-03-14 22:57:10 -07:00
|
|
|
let vk = &vks[pos / outputs.len()];
|
|
|
|
let output = &outputs[pos % outputs.len()];
|
|
|
|
notes.push(DecryptedNote {
|
|
|
|
account: *vk.0,
|
|
|
|
ivk: vk.1.fvk.clone(),
|
|
|
|
note: note.clone(),
|
|
|
|
pa: pa.clone(),
|
|
|
|
viewonly: vk.1.viewonly,
|
|
|
|
position_in_block: output.1.block_output_index,
|
|
|
|
height: block.height as u32,
|
2022-03-15 01:04:37 -07:00
|
|
|
tx_index: output.1.tx_index,
|
2022-03-14 22:57:10 -07:00
|
|
|
txid: output.1.vtx.hash.clone(),
|
|
|
|
output_index: output.1.output_index,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-18 01:17:41 -07:00
|
|
|
DecryptedBlock {
|
|
|
|
height: block.height as u32,
|
2021-06-24 05:08:20 -07:00
|
|
|
spends,
|
2021-06-18 01:17:41 -07:00
|
|
|
notes,
|
|
|
|
count_outputs,
|
2021-06-26 02:52:03 -07:00
|
|
|
compact_block: block,
|
2022-03-14 22:57:10 -07:00
|
|
|
elapsed,
|
2021-06-18 01:17:41 -07:00
|
|
|
}
|
2021-06-17 09:56:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DecryptNode {
|
2022-09-01 07:30:42 -07:00
|
|
|
pub fn new(vks: HashMap<u32, AccountViewKey>) -> DecryptNode {
|
|
|
|
DecryptNode { vks }
|
2021-06-18 01:17:41 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 18:23:23 -07:00
|
|
|
pub fn decrypt_blocks(
|
2022-06-07 09:58:24 -07:00
|
|
|
&self,
|
|
|
|
network: &Network,
|
2022-08-17 18:23:23 -07:00
|
|
|
blocks: Vec<CompactBlock>,
|
|
|
|
) -> Vec<DecryptedBlock> {
|
2022-08-30 07:00:15 -07:00
|
|
|
let use_gpu = { *USE_GPU.lock().unwrap() };
|
2022-08-21 10:48:34 -07:00
|
|
|
if use_gpu {
|
|
|
|
#[cfg(feature = "cuda")]
|
|
|
|
return self.cuda_decrypt_blocks(network, blocks);
|
2022-08-02 07:34:15 -07:00
|
|
|
|
2022-08-21 10:48:34 -07:00
|
|
|
#[cfg(feature = "apple_metal")]
|
|
|
|
return self.metal_decrypt_blocks(network, blocks);
|
2022-08-17 20:42:06 -07:00
|
|
|
|
2022-08-21 10:48:34 -07:00
|
|
|
#[allow(unreachable_code)]
|
|
|
|
self.decrypt_blocks_soft(network, blocks)
|
|
|
|
} else {
|
|
|
|
self.decrypt_blocks_soft(network, blocks)
|
|
|
|
}
|
2022-08-02 07:34:15 -07:00
|
|
|
}
|
|
|
|
|
2022-08-17 18:23:23 -07:00
|
|
|
pub fn decrypt_blocks_soft(
|
2022-08-02 07:34:15 -07:00
|
|
|
&self,
|
|
|
|
network: &Network,
|
2022-08-17 18:23:23 -07:00
|
|
|
blocks: Vec<CompactBlock>,
|
|
|
|
) -> Vec<DecryptedBlock> {
|
2022-03-14 22:57:10 -07:00
|
|
|
let vks: Vec<_> = self.vks.iter().collect();
|
2021-06-18 01:17:41 -07:00
|
|
|
let mut decrypted_blocks: Vec<DecryptedBlock> = blocks
|
2022-08-17 18:23:23 -07:00
|
|
|
.into_par_iter()
|
2022-09-01 07:30:42 -07:00
|
|
|
.map(|b| decrypt_notes(network, b, &vks))
|
2021-06-18 01:17:41 -07:00
|
|
|
.collect();
|
|
|
|
decrypted_blocks.sort_by(|a, b| a.height.cmp(&b.height));
|
|
|
|
decrypted_blocks
|
|
|
|
}
|
2022-08-02 07:34:15 -07:00
|
|
|
|
|
|
|
#[cfg(feature = "cuda")]
|
2022-08-17 18:23:23 -07:00
|
|
|
pub fn cuda_decrypt_blocks(
|
2022-08-02 07:34:15 -07:00
|
|
|
&self,
|
|
|
|
network: &Network,
|
2022-08-17 18:23:23 -07:00
|
|
|
blocks: Vec<CompactBlock>,
|
|
|
|
) -> Vec<DecryptedBlock> {
|
2022-08-16 00:57:42 -07:00
|
|
|
if blocks.is_empty() {
|
|
|
|
return vec![];
|
|
|
|
}
|
2022-10-19 23:24:36 -07:00
|
|
|
if crate::gpu::has_cuda() {
|
2022-08-17 04:05:53 -07:00
|
|
|
let processor = CudaProcessor::setup_decrypt(network, blocks).unwrap();
|
2022-08-21 09:40:14 -07:00
|
|
|
return trial_decrypt(processor, self.vks.iter()).unwrap();
|
2022-08-02 07:34:15 -07:00
|
|
|
}
|
|
|
|
self.decrypt_blocks_soft(network, blocks)
|
|
|
|
}
|
2022-08-17 18:23:23 -07:00
|
|
|
|
2022-08-17 20:42:06 -07:00
|
|
|
#[cfg(feature = "apple_metal")]
|
|
|
|
pub fn metal_decrypt_blocks(
|
|
|
|
&self,
|
|
|
|
network: &Network,
|
|
|
|
blocks: Vec<CompactBlock>,
|
|
|
|
) -> Vec<DecryptedBlock> {
|
|
|
|
if blocks.is_empty() {
|
|
|
|
return vec![];
|
|
|
|
}
|
|
|
|
let processor = MetalProcessor::setup_decrypt(network, blocks).unwrap();
|
|
|
|
trial_decrypt(processor, self.vks.iter()).unwrap()
|
|
|
|
}
|
2021-06-18 01:17:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
async fn get_tree_state(client: &mut CompactTxStreamerClient<Channel>, height: u32) -> String {
|
|
|
|
let block_id = BlockId {
|
|
|
|
height: height as u64,
|
|
|
|
hash: vec![],
|
|
|
|
};
|
|
|
|
let rep = client
|
|
|
|
.get_tree_state(Request::new(block_id))
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_inner();
|
2022-06-11 05:29:14 -07:00
|
|
|
rep.sapling_tree
|
2021-06-18 01:17:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Using the IncrementalWitness */
|
|
|
|
#[allow(dead_code)]
|
|
|
|
fn calculate_tree_state_v1(
|
|
|
|
cbs: &[CompactBlock],
|
|
|
|
blocks: &[DecryptedBlock],
|
|
|
|
height: u32,
|
|
|
|
mut tree_state: CommitmentTree<Node>,
|
|
|
|
) -> Vec<IncrementalWitness<Node>> {
|
|
|
|
let mut witnesses: Vec<IncrementalWitness<Node>> = vec![];
|
|
|
|
for (cb, block) in cbs.iter().zip(blocks) {
|
|
|
|
assert_eq!(cb.height as u32, block.height);
|
|
|
|
if block.height < height {
|
|
|
|
continue;
|
|
|
|
} // skip before height
|
|
|
|
let mut notes = block.notes.iter();
|
|
|
|
let mut n = notes.next();
|
2021-06-21 17:33:13 -07:00
|
|
|
let mut i = 0usize;
|
2021-06-18 01:17:41 -07:00
|
|
|
for tx in cb.vtx.iter() {
|
|
|
|
for co in tx.outputs.iter() {
|
|
|
|
let mut cmu = [0u8; 32];
|
|
|
|
cmu.copy_from_slice(&co.cmu);
|
|
|
|
let node = Node::new(cmu);
|
|
|
|
tree_state.append(node).unwrap();
|
|
|
|
for w in witnesses.iter_mut() {
|
|
|
|
w.append(node).unwrap();
|
|
|
|
}
|
|
|
|
if let Some(nn) = n {
|
2021-06-24 05:08:20 -07:00
|
|
|
if i == nn.position_in_block {
|
2021-06-18 01:17:41 -07:00
|
|
|
let w = IncrementalWitness::from_tree(&tree_state);
|
|
|
|
witnesses.push(w);
|
|
|
|
n = notes.next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
2021-06-17 09:56:20 -07:00
|
|
|
}
|
|
|
|
}
|
2021-06-18 01:17:41 -07:00
|
|
|
|
|
|
|
witnesses
|
|
|
|
}
|
|
|
|
|
2022-10-19 23:24:36 -07:00
|
|
|
/// Connect to a lightwalletd server
|
2021-07-09 06:33:05 -07:00
|
|
|
pub async fn connect_lightwalletd(url: &str) -> anyhow::Result<CompactTxStreamerClient<Channel>> {
|
2022-08-03 08:30:34 -07:00
|
|
|
log::info!("LWD URL: {}", url);
|
2021-07-09 06:33:05 -07:00
|
|
|
let mut channel = tonic::transport::Channel::from_shared(url.to_owned())?;
|
|
|
|
if url.starts_with("https") {
|
2021-06-21 17:33:13 -07:00
|
|
|
let pem = include_bytes!("ca.pem");
|
|
|
|
let ca = Certificate::from_pem(pem);
|
|
|
|
let tls = ClientTlsConfig::new().ca_certificate(ca);
|
|
|
|
channel = channel.tls_config(tls)?;
|
|
|
|
}
|
|
|
|
let client = CompactTxStreamerClient::connect(channel).await?;
|
|
|
|
Ok(client)
|
|
|
|
}
|
|
|
|
|
2022-07-13 20:22:12 -07:00
|
|
|
async fn get_height(server: String) -> Option<(String, u32)> {
|
|
|
|
let mut client = connect_lightwalletd(&server).await.ok()?;
|
|
|
|
let height = get_latest_height(&mut client).await.ok()?;
|
|
|
|
log::info!("{} {}", server, height);
|
|
|
|
Some((server, height))
|
|
|
|
}
|
|
|
|
|
2022-10-19 23:24:36 -07:00
|
|
|
/// Return the URL of the best server given a list of servers
|
|
|
|
/// The best server is the one that has the highest height
|
2023-03-09 17:10:28 -08:00
|
|
|
pub async fn get_best_server(servers: fbServers<'_>) -> anyhow::Result<String> {
|
2022-07-13 20:22:12 -07:00
|
|
|
let mut server_heights = vec![];
|
2023-03-09 17:10:28 -08:00
|
|
|
let urls = servers.urls().unwrap();
|
|
|
|
for s in urls.iter() {
|
2022-07-13 20:22:12 -07:00
|
|
|
let server_height =
|
|
|
|
tokio::spawn(timeout(Duration::from_secs(1), get_height(s.to_string()))).boxed();
|
|
|
|
server_heights.push(server_height);
|
|
|
|
}
|
2022-11-18 06:25:11 -08:00
|
|
|
let mut server_heights = future::try_join_all(server_heights).await?;
|
2022-08-21 23:36:29 -07:00
|
|
|
server_heights.shuffle(&mut OsRng);
|
2022-07-21 18:08:29 -07:00
|
|
|
|
|
|
|
server_heights
|
2022-07-13 20:22:12 -07:00
|
|
|
.into_iter()
|
|
|
|
.filter_map(|h| h.unwrap_or(None))
|
|
|
|
.max_by_key(|(_, h)| *h)
|
2022-07-21 18:08:29 -07:00
|
|
|
.map(|x| x.0)
|
2022-11-18 06:25:11 -08:00
|
|
|
.ok_or(anyhow::anyhow!("No Lightwalletd"))
|
|
|
|
}
|
|
|
|
|
2023-02-21 00:36:29 -08:00
|
|
|
pub const EXPIRY_HEIGHT_OFFSET: u32 = 50;
|
|
|
|
|
|
|
|
pub fn get_checkpoint_height(
|
|
|
|
db: &DbAdapter,
|
|
|
|
last_height: u32,
|
|
|
|
confirmations: u32,
|
|
|
|
) -> anyhow::Result<u32> {
|
|
|
|
let anchor_height = last_height.saturating_sub(confirmations);
|
|
|
|
let checkpoint_height = db
|
|
|
|
.get_checkpoint_height(anchor_height)?
|
|
|
|
.unwrap_or_else(|| db.sapling_activation_height()); // get the latest checkpoint before the requested anchor height
|
|
|
|
Ok(checkpoint_height)
|
|
|
|
}
|