Skip to content

Commit

Permalink
Create a scan_block function to use across scanning tasks (#7994)
Browse files Browse the repository at this point in the history
* Wrap `zcash_client_backend::scanning::scan_block`

* Use the new `scan_block` fn

* Use full path for `zcash::primitives::Network`

* Add docs for `scan_block`

* Impl `From` for networks in `zcash_primitives`

* Update zebra-scan/src/tests.rs

Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>

* Move code from `tests.rs` to `scan.rs`

---------

Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>
  • Loading branch information
upbqdn and oxarbitrage authored Nov 28, 2023
1 parent 1d241af commit e00a762
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 180 deletions.
9 changes: 9 additions & 0 deletions zebra-chain/src/primitives/zcash_primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,12 @@ pub(crate) fn transparent_output_address(
None => None,
}
}

impl From<Network> for zcash_primitives::consensus::Network {
fn from(network: Network) -> Self {
match network {
Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork,
Network::Testnet => zcash_primitives::consensus::Network::TestNetwork,
}
}
}
6 changes: 3 additions & 3 deletions zebra-scan/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ categories = ["cryptography::cryptocurrencies"]
zebra-chain = { path = "../zebra-chain", version = "1.0.0-beta.31" }
zebra-state = { path = "../zebra-state", version = "1.0.0-beta.31" }

zcash_primitives = "0.13.0-rc.1"
zcash_client_backend = "0.10.0-rc.1"

color-eyre = "0.6.2"
indexmap = { version = "2.0.1", features = ["serde"] }
serde = { version = "1.0.193", features = ["serde_derive"] }
Expand All @@ -30,9 +33,6 @@ tower = "0.4.13"
tracing = "0.1.39"

[dev-dependencies]

zcash_client_backend = "0.10.0-rc.1"
zcash_primitives = "0.13.0-rc.1"
zcash_note_encryption = "0.4.0"

rand = "0.8.5"
Expand Down
138 changes: 137 additions & 1 deletion zebra-scan/src/scan.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
//! The scan task.
use std::time::Duration;
use std::{sync::Arc, time::Duration};

use color_eyre::{eyre::eyre, Report};
use tower::{buffer::Buffer, util::BoxService, Service, ServiceExt};
use tracing::info;
use zcash_client_backend::{
data_api::ScannedBlock,
proto::compact_formats::{
ChainMetadata, CompactBlock, CompactSaplingOutput, CompactSaplingSpend, CompactTx,
},
scanning::{ScanError, ScanningKey},
};
use zcash_primitives::zip32::AccountId;
use zebra_chain::{
block::Block, parameters::Network, serialization::ZcashSerialize, transaction::Transaction,
};

use crate::storage::Storage;

Expand Down Expand Up @@ -56,3 +67,128 @@ pub async fn start(mut state: State, storage: Storage) -> Result<(), Report> {
tokio::time::sleep(CHECK_INTERVAL).await;
}
}

/// Returns transactions belonging to any of the given [`ScanningKey`]s.
///
/// TODO:
/// - Remove the `sapling_tree_size` parameter or turn it into an `Option` once we have access to
/// Zebra's state, and we can retrieve the tree size ourselves.
/// - Add prior block metadata once we have access to Zebra's state.
pub fn scan_block<K: ScanningKey>(
network: Network,
block: Arc<Block>,
sapling_tree_size: u32,
scanning_keys: &[&K],
) -> Result<ScannedBlock<K::Nf>, ScanError> {
// TODO: Implement a check that returns early when the block height is below the Sapling
// activation height.

let network: zcash_primitives::consensus::Network = network.into();

let chain_metadata = ChainMetadata {
sapling_commitment_tree_size: sapling_tree_size,
// Orchard is not supported at the moment so the tree size can be 0.
orchard_commitment_tree_size: 0,
};

// Use a dummy `AccountId` as we don't use accounts yet.
let dummy_account = AccountId::from(0);

let scanning_keys: Vec<_> = scanning_keys
.iter()
.map(|key| (&dummy_account, key))
.collect();

zcash_client_backend::scanning::scan_block(
&network,
block_to_compact(block, chain_metadata),
&scanning_keys,
// Ignore whether notes are change from a viewer's own spends for now.
&[],
// Ignore previous blocks for now.
None,
)
}

/// Converts a zebra block and meta data into a compact block.
pub fn block_to_compact(block: Arc<Block>, chain_metadata: ChainMetadata) -> CompactBlock {
CompactBlock {
height: block
.coinbase_height()
.expect("verified block should have a valid height")
.0
.into(),
hash: block.hash().bytes_in_display_order().to_vec(),
prev_hash: block
.header
.previous_block_hash
.bytes_in_display_order()
.to_vec(),
time: block
.header
.time
.timestamp()
.try_into()
.expect("unsigned 32-bit times should work until 2105"),
header: block
.header
.zcash_serialize_to_vec()
.expect("verified block should serialize"),
vtx: block
.transactions
.iter()
.cloned()
.enumerate()
.map(transaction_to_compact)
.collect(),
chain_metadata: Some(chain_metadata),

// The protocol version is used for the gRPC wire format, so it isn't needed here.
proto_version: 0,
}
}

/// Converts a zebra transaction into a compact transaction.
fn transaction_to_compact((index, tx): (usize, Arc<Transaction>)) -> CompactTx {
CompactTx {
index: index
.try_into()
.expect("tx index in block should fit in u64"),
hash: tx.hash().bytes_in_display_order().to_vec(),

// `fee` is not checked by the `scan_block` function. It is allowed to be unset.
// <https://docs.rs/zcash_client_backend/latest/zcash_client_backend/proto/compact_formats/struct.CompactTx.html#structfield.fee>
fee: 0,

spends: tx
.sapling_nullifiers()
.map(|nf| CompactSaplingSpend {
nf: <[u8; 32]>::from(*nf).to_vec(),
})
.collect(),

// > output encodes the cmu field, ephemeralKey field, and a 52-byte prefix of the encCiphertext field of a Sapling Output
//
// <https://docs.rs/zcash_client_backend/latest/zcash_client_backend/proto/compact_formats/struct.CompactSaplingOutput.html>
outputs: tx
.sapling_outputs()
.map(|output| CompactSaplingOutput {
cmu: output.cm_u.to_bytes().to_vec(),
ephemeral_key: output
.ephemeral_key
.zcash_serialize_to_vec()
.expect("verified output should serialize successfully"),
ciphertext: output
.enc_ciphertext
.zcash_serialize_to_vec()
.expect("verified output should serialize successfully")
.into_iter()
.take(52)
.collect(),
})
.collect(),

// `actions` is not checked by the `scan_block` function.
actions: vec![],
}
}
Loading

0 comments on commit e00a762

Please sign in to comment.