From dbff3b49bc6e27ef0d19c5dffcaa5cf873fb3547 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 4 Jun 2024 18:04:40 -0300 Subject: [PATCH] build(ecc):Upgrade remaining ECC dependencies (#8568) * upgrade zcash_client_backend for zebra-scan * leave zebra-utils untouched * remove unused * upgrade zcash_primitives and orchard only for zebra-chain crate * update and use TryFrom for amounts * fix imports in serialize * leave doc as it was * leave doc as it was 2 * use `try_into` for amount * use `zip_212_enforcement` * updgrades primitives in zebra-rpc * upgrade ecc dependencies for cargo-utils * fix threading issue * remove non needed Arc * update deny.toml * update primitives for zebra-grpc * fix doc * remove non needed single thread code in test * cleanup some tests * improve a bit the `ready_scan_block_keys` function * clippy * add spawn back in `scan_height_and_store_results` * remove todo * add note comment * use a more explicit import of sapling stuff in serialize * change(scan): Refactor scanning keys (#8577) * Refactor converting dfvks to scanning keys * Simplify handling of scanning keys * Remove `test-dependencies` from the scanner reader * Add comments --------- Co-authored-by: Marek --- Cargo.lock | 143 +++++++++++------- deny.toml | 5 +- zebra-chain/Cargo.toml | 7 +- zebra-chain/src/parameters/network_upgrade.rs | 2 +- zebra-chain/src/primitives/address.rs | 1 - .../src/primitives/viewing_key/sapling.rs | 10 +- .../src/primitives/zcash_note_encryption.rs | 16 +- .../src/primitives/zcash_primitives.rs | 54 +++---- zebra-chain/src/transaction/serialize.rs | 25 +-- zebra-grpc/Cargo.toml | 2 +- zebra-rpc/Cargo.toml | 2 +- zebra-scan/Cargo.toml | 7 +- zebra-scan/src/lib.rs | 2 +- zebra-scan/src/service/scan_task/commands.rs | 23 +-- zebra-scan/src/service/scan_task/scan.rs | 125 ++++++++------- .../src/service/scan_task/scan/scan_range.rs | 20 +-- zebra-scan/src/tests.rs | 44 +++--- zebra-scan/src/tests/vectors.rs | 46 +++--- zebra-utils/Cargo.toml | 5 +- .../src/bin/scanning-results-reader/main.rs | 23 +-- 20 files changed, 288 insertions(+), 274 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d650ab1a1..c75730e5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2839,9 +2839,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orchard" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d31e68534df32024dcc89a8390ec6d7bef65edd87d91b45cfb481a2eb2d77c5" +checksum = "1fb255c3ffdccd3c84fe9ebed72aef64fdc72e6a3e4180dd411002d47abaad42" dependencies = [ "aes", "bitvec", @@ -2863,13 +2863,15 @@ dependencies = [ "subtle", "tracing", "zcash_note_encryption", + "zcash_spec", + "zip32", ] [[package]] name = "orchard" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb255c3ffdccd3c84fe9ebed72aef64fdc72e6a3e4180dd411002d47abaad42" +checksum = "0462569fc8b0d1b158e4d640571867a4e4319225ebee2ab6647e60c70af19ae3" dependencies = [ "aes", "bitvec", @@ -4232,9 +4234,9 @@ dependencies = [ [[package]] name = "shardtree" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19f96dde3a8693874f7e7c53d95616569b4009379a903789efbd448f4ea9cc7" +checksum = "3b3cdd24424ce0b381646737fedddc33c4dcf7dcd2d545056b53f7982097bef5" dependencies = [ "bitflags 2.5.0", "either", @@ -5743,24 +5745,27 @@ dependencies = [ [[package]] name = "zcash_client_backend" -version = "0.10.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a382af39be9ee5a3788157145c404b7cd19acc440903f6c34b09fb44f0e991" +checksum = "0364e69c446fcf96a1f73f342c6c3fa697ea65ae7eeeae7d76ca847b9c442e40" dependencies = [ "base64 0.21.7", "bech32", "bls12_381", "bs58", "crossbeam-channel", + "document-features", "group", "hex", "incrementalmerkletree", "memuse", "nom", - "orchard 0.6.0", + "nonempty", "percent-encoding", "prost", + "rand_core 0.6.4", "rayon", + "sapling-crypto", "secrecy", "shardtree", "subtle", @@ -5770,8 +5775,11 @@ dependencies = [ "which", "zcash_address", "zcash_encoding", + "zcash_keys", "zcash_note_encryption", - "zcash_primitives 0.13.0", + "zcash_primitives 0.15.0", + "zcash_protocol", + "zip32", ] [[package]] @@ -5795,6 +5803,32 @@ dependencies = [ "primitive-types", ] +[[package]] +name = "zcash_keys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "663489ffb4e51bc4436ff8796832612a9ff3c6516f1c620b5a840cb5dcd7b866" +dependencies = [ + "bech32", + "blake2b_simd", + "bls12_381", + "bs58", + "document-features", + "group", + "memuse", + "nonempty", + "rand_core 0.6.4", + "sapling-crypto", + "secrecy", + "subtle", + "tracing", + "zcash_address", + "zcash_encoding", + "zcash_primitives 0.15.0", + "zcash_protocol", + "zip32", +] + [[package]] name = "zcash_note_encryption" version = "0.4.0" @@ -5808,42 +5842,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "zcash_primitives" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d17e4c94ca8d69d2fcf2be97522da5732a580eb2125cda3b150761952f8df8e6" -dependencies = [ - "aes", - "bip0039", - "bitvec", - "blake2b_simd", - "blake2s_simd", - "bls12_381", - "byteorder", - "equihash", - "ff", - "fpe", - "group", - "hdwallet", - "hex", - "incrementalmerkletree", - "jubjub", - "lazy_static", - "memuse", - "nonempty", - "orchard 0.6.0", - "rand 0.8.5", - "rand_core 0.6.4", - "ripemd", - "secp256k1", - "sha2", - "subtle", - "zcash_address", - "zcash_encoding", - "zcash_note_encryption", -] - [[package]] name = "zcash_primitives" version = "0.14.0" @@ -5882,6 +5880,42 @@ dependencies = [ "zip32", ] +[[package]] +name = "zcash_primitives" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a8d812efec385ecbcefc862c0005bb1336474ea7dd9b671d5bbddaadd04be2" +dependencies = [ + "aes", + "bip0039", + "blake2b_simd", + "byteorder", + "document-features", + "equihash", + "ff", + "fpe", + "group", + "hex", + "incrementalmerkletree", + "jubjub", + "memuse", + "nonempty", + "orchard 0.8.0", + "rand 0.8.5", + "rand_core 0.6.4", + "redjubjub", + "sapling-crypto", + "sha2", + "subtle", + "tracing", + "zcash_address", + "zcash_encoding", + "zcash_note_encryption", + "zcash_protocol", + "zcash_spec", + "zip32", +] + [[package]] name = "zcash_proofs" version = "0.14.0" @@ -5991,7 +6025,7 @@ dependencies = [ "jubjub", "lazy_static", "num-integer", - "orchard 0.6.0", + "orchard 0.7.1", "primitive-types", "proptest", "proptest-derive", @@ -6002,6 +6036,7 @@ dependencies = [ "reddsa", "redjubjub", "ripemd", + "sapling-crypto", "secp256k1", "serde", "serde-big-array", @@ -6021,7 +6056,7 @@ dependencies = [ "zcash_encoding", "zcash_history", "zcash_note_encryption", - "zcash_primitives 0.13.0", + "zcash_primitives 0.14.0", "zcash_protocol", "zebra-test", ] @@ -6088,7 +6123,7 @@ dependencies = [ "tonic-build 0.11.0", "tonic-reflection", "tower", - "zcash_primitives 0.13.0", + "zcash_primitives 0.14.0", "zebra-chain", "zebra-node-services", "zebra-state", @@ -6171,7 +6206,7 @@ dependencies = [ "tower", "tracing", "zcash_address", - "zcash_primitives 0.13.0", + "zcash_primitives 0.14.0", "zebra-chain", "zebra-consensus", "zebra-network", @@ -6198,14 +6233,17 @@ dependencies = [ "proptest", "proptest-derive", "rand 0.8.5", + "sapling-crypto", "semver 1.0.23", "serde", "tokio", "tower", "tracing", + "zcash_address", "zcash_client_backend", + "zcash_keys", "zcash_note_encryption", - "zcash_primitives 0.13.0", + "zcash_primitives 0.14.0", "zebra-chain", "zebra-grpc", "zebra-node-services", @@ -6321,7 +6359,8 @@ dependencies = [ "tracing-error", "tracing-subscriber", "zcash_client_backend", - "zcash_primitives 0.13.0", + "zcash_primitives 0.15.0", + "zcash_protocol", "zebra-chain", "zebra-node-services", "zebra-rpc", diff --git a/deny.toml b/deny.toml index 9a0074030..6265ca13c 100644 --- a/deny.toml +++ b/deny.toml @@ -84,9 +84,8 @@ skip-tree = [ # ECC crates # wait for zcash_client_backend - { name = "orchard", version = "=0.6.0" }, - { name = "zcash_primitives", version = "=0.13.0" }, - { name = "zcash_proofs", version = "=0.13.0" }, + { name = "orchard", version = "=0.7.0" }, + { name = "zcash_primitives", version = "=0.14.0" }, # wait for hdwallet to upgrade { name = "ring", version = "=0.16.20" }, diff --git a/zebra-chain/Cargo.toml b/zebra-chain/Cargo.toml index ef325b5ee..169c0cfc3 100644 --- a/zebra-chain/Cargo.toml +++ b/zebra-chain/Cargo.toml @@ -93,11 +93,12 @@ x25519-dalek = { version = "2.0.1", features = ["serde"] } # ECC deps halo2 = { package = "halo2_proofs", version = "0.3.0" } -orchard = "0.6.0" +orchard = "0.7.0" zcash_encoding = "0.2.0" zcash_history = "0.4.0" zcash_note_encryption = "0.4.0" -zcash_primitives = { version = "0.13.0", features = ["transparent-inputs"] } +zcash_primitives = { version = "0.14.0", features = ["transparent-inputs"] } +sapling = { package = "sapling-crypto", version = "0.1" } zcash_protocol = { version = "0.1.1" } zcash_address = { version = "0.3.2" } @@ -134,7 +135,7 @@ serde_json = { version = "1.0.117", optional = true } tokio = { version = "1.37.0", optional = true } # Experimental feature shielded-scan -zcash_client_backend = { version = "0.10.0-rc.1", optional = true } +zcash_client_backend = { version = "0.12.1", optional = true } # Optional testing dependencies proptest = { version = "1.4.0", optional = true } diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index 1cd2be65c..3c6a0544f 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -30,7 +30,7 @@ pub const NETWORK_UPGRADES_IN_ORDER: [NetworkUpgrade; 8] = [ /// /// Network upgrades can change the Zcash network protocol or consensus rules in /// incompatible ways. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Ord, PartialOrd)] #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] pub enum NetworkUpgrade { /// The Zcash protocol for a Genesis block. diff --git a/zebra-chain/src/primitives/address.rs b/zebra-chain/src/primitives/address.rs index 86d524b8d..f076c0c33 100644 --- a/zebra-chain/src/primitives/address.rs +++ b/zebra-chain/src/primitives/address.rs @@ -3,7 +3,6 @@ //! Usage: use zcash_address::unified::{self, Container}; -use zcash_primitives::sapling; use crate::{parameters::NetworkKind, transparent, BoxError}; diff --git a/zebra-chain/src/primitives/viewing_key/sapling.rs b/zebra-chain/src/primitives/viewing_key/sapling.rs index e8d423a7f..3e483def1 100644 --- a/zebra-chain/src/primitives/viewing_key/sapling.rs +++ b/zebra-chain/src/primitives/viewing_key/sapling.rs @@ -1,11 +1,11 @@ //! Defines types and implements methods for parsing Sapling viewing keys and converting them to `zebra-chain` types -use zcash_client_backend::encoding::decode_extended_full_viewing_key; -use zcash_primitives::{ - constants::*, - sapling::keys::{FullViewingKey as SaplingFvk, SaplingIvk}, - zip32::DiversifiableFullViewingKey as SaplingDfvk, +use sapling::keys::{FullViewingKey as SaplingFvk, SaplingIvk}; +use zcash_client_backend::{ + encoding::decode_extended_full_viewing_key, + keys::sapling::DiversifiableFullViewingKey as SaplingDfvk, }; +use zcash_primitives::constants::*; use crate::parameters::Network; diff --git a/zebra-chain/src/primitives/zcash_note_encryption.rs b/zebra-chain/src/primitives/zcash_note_encryption.rs index f8e47fbad..0898f3451 100644 --- a/zebra-chain/src/primitives/zcash_note_encryption.rs +++ b/zebra-chain/src/primitives/zcash_note_encryption.rs @@ -20,16 +20,22 @@ pub fn decrypts_successfully(transaction: &Transaction, network: &Network, heigh let alt_tx = convert_tx_to_librustzcash(transaction, network_upgrade) .expect("zcash_primitives and Zebra transaction formats must be compatible"); - let alt_height = height.0.into(); - let null_sapling_ovk = zcash_primitives::keys::OutgoingViewingKey([0u8; 32]); + let null_sapling_ovk = sapling::keys::OutgoingViewingKey([0u8; 32]); + + // Note that, since this function is used to validate coinbase transactions, we can ignore + // the "grace period" mentioned in ZIP-212. + let zip_212_enforcement = if network_upgrade >= NetworkUpgrade::Canopy { + sapling::note_encryption::Zip212Enforcement::On + } else { + sapling::note_encryption::Zip212Enforcement::Off + }; if let Some(bundle) = alt_tx.sapling_bundle() { for output in bundle.shielded_outputs().iter() { - let recovery = zcash_primitives::sapling::note_encryption::try_sapling_output_recovery( - network, - alt_height, + let recovery = sapling::note_encryption::try_sapling_output_recovery( &null_sapling_ovk, output, + zip_212_enforcement, ); if recovery.is_none() { return false; diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index 564afab09..2e7aa3396 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -29,14 +29,14 @@ impl zp_tx::components::transparent::Authorization for TransparentAuth<'_> { // In this block we convert our Output to a librustzcash to TxOut. // (We could do the serialize/deserialize route but it's simple enough to convert manually) impl zp_tx::sighash::TransparentAuthorizingContext for TransparentAuth<'_> { - fn input_amounts(&self) -> Vec { + fn input_amounts(&self) -> Vec { self.all_prev_outputs .iter() .map(|prevout| { - zp_tx::components::amount::Amount::from_nonnegative_i64_le_bytes( - prevout.value.to_bytes(), - ) - .expect("will not fail since it was previously validated") + prevout + .value + .try_into() + .expect("will not fail since it was previously validated") }) .collect() } @@ -83,39 +83,31 @@ impl<'a> struct IdentityMap; -impl - zp_tx::components::sapling::MapAuth< - zp_tx::components::sapling::Authorized, - zp_tx::components::sapling::Authorized, - > for IdentityMap +impl zp_tx::components::sapling::MapAuth + for IdentityMap { fn map_spend_proof( - &self, - p: ::SpendProof, - ) -> ::SpendProof - { + &mut self, + p: ::SpendProof, + ) -> ::SpendProof { p } fn map_output_proof( - &self, - p: ::OutputProof, - ) -> ::OutputProof - { + &mut self, + p: ::OutputProof, + ) -> ::OutputProof { p } fn map_auth_sig( - &self, - s: ::AuthSig, - ) -> ::AuthSig{ + &mut self, + s: ::AuthSig, + ) -> ::AuthSig { s } - fn map_authorization( - &self, - a: zp_tx::components::sapling::Authorized, - ) -> zp_tx::components::sapling::Authorized { + fn map_authorization(&mut self, a: sapling::bundle::Authorized) -> sapling::bundle::Authorized { a } } @@ -141,7 +133,7 @@ struct PrecomputedAuth<'a> { impl<'a> zp_tx::Authorization for PrecomputedAuth<'a> { type TransparentAuth = TransparentAuth<'a>; - type SaplingAuth = zp_tx::components::sapling::Authorized; + type SaplingAuth = sapling::bundle::Authorized; type OrchardAuth = orchard::bundle::Authorized; } @@ -213,12 +205,12 @@ impl TryFrom for zp_tx::components::TxOut { } } -/// Convert a Zebra Amount into a librustzcash one. -impl TryFrom> for zp_tx::components::Amount { +/// Convert a Zebra non-negative Amount into a librustzcash one. +impl TryFrom> for zp_tx::components::amount::NonNegativeAmount { type Error = (); fn try_from(amount: Amount) -> Result { - zp_tx::components::Amount::from_u64(amount.into()) + zp_tx::components::amount::NonNegativeAmount::from_nonnegative_i64(amount.into()) } } @@ -327,10 +319,10 @@ pub(crate) fn transparent_output_address( let alt_addr = tx_out.recipient_address(); match alt_addr { - Some(zcash_primitives::legacy::TransparentAddress::PublicKey(pub_key_hash)) => Some( + Some(zcash_primitives::legacy::TransparentAddress::PublicKeyHash(pub_key_hash)) => Some( transparent::Address::from_pub_key_hash(network.kind(), pub_key_hash), ), - Some(zcash_primitives::legacy::TransparentAddress::Script(script_hash)) => Some( + Some(zcash_primitives::legacy::TransparentAddress::ScriptHash(script_hash)) => Some( transparent::Address::from_script_hash(network.kind(), script_hash), ), None => None, diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index 044db7d51..ba65dd054 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -21,7 +21,7 @@ use crate::{ }; use super::*; -use sapling::{Output, SharedAnchor, Spend}; +use crate::sapling; impl ZcashDeserialize for jubjub::Fq { fn zcash_deserialize(mut reader: R) -> Result { @@ -110,7 +110,7 @@ where // range, so we can implement its serialization and deserialization separately. // (Unlike V4, where it must be serialized as part of the transaction.) -impl ZcashSerialize for Option> { +impl ZcashSerialize for Option> { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { match self { None => { @@ -127,21 +127,24 @@ impl ZcashSerialize for Option> { } } -impl ZcashSerialize for sapling::ShieldedData { +impl ZcashSerialize for sapling::ShieldedData { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { // Collect arrays for Spends // There's no unzip3, so we have to unzip twice. let (spend_prefixes, spend_proofs_sigs): (Vec<_>, Vec<_>) = self .spends() .cloned() - .map(sapling::Spend::::into_v5_parts) + .map(sapling::Spend::::into_v5_parts) .map(|(prefix, proof, sig)| (prefix, (proof, sig))) .unzip(); let (spend_proofs, spend_sigs) = spend_proofs_sigs.into_iter().unzip(); // Collect arrays for Outputs - let (output_prefixes, output_proofs): (Vec<_>, _) = - self.outputs().cloned().map(Output::into_v5_parts).unzip(); + let (output_prefixes, output_proofs): (Vec<_>, _) = self + .outputs() + .cloned() + .map(sapling::Output::into_v5_parts) + .unzip(); // Denoted as `nSpendsSapling` and `vSpendsSapling` in the spec. spend_prefixes.zcash_serialize(&mut writer)?; @@ -175,7 +178,7 @@ impl ZcashSerialize for sapling::ShieldedData { // we can't split ShieldedData out of Option deserialization, // because the counts are read along with the arrays. -impl ZcashDeserialize for Option> { +impl ZcashDeserialize for Option> { #[allow(clippy::unwrap_in_result)] fn zcash_deserialize(mut reader: R) -> Result { // Denoted as `nSpendsSapling` and `vSpendsSapling` in the spec. @@ -269,14 +272,16 @@ impl ZcashDeserialize for Option> { .into_iter() .zip(spend_proofs) .zip(spend_sigs) - .map(|((prefix, proof), sig)| Spend::::from_v5_parts(prefix, proof, sig)) + .map(|((prefix, proof), sig)| { + sapling::Spend::::from_v5_parts(prefix, proof, sig) + }) .collect(); // Create shielded outputs from deserialized parts let outputs = output_prefixes .into_iter() .zip(output_proofs) - .map(|(prefix, proof)| Output::from_v5_parts(prefix, proof)) + .map(|(prefix, proof)| sapling::Output::from_v5_parts(prefix, proof)) .collect(); // Create transfers @@ -823,7 +828,7 @@ impl ZcashDeserialize for Transaction { let shielded_outputs = Vec::::zcash_deserialize(&mut limited_reader)? .into_iter() - .map(Output::from_v4) + .map(sapling::Output::from_v4) .collect(); // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`, diff --git a/zebra-grpc/Cargo.toml b/zebra-grpc/Cargo.toml index 46d1b6010..70f667229 100644 --- a/zebra-grpc/Cargo.toml +++ b/zebra-grpc/Cargo.toml @@ -26,7 +26,7 @@ tokio-stream = "0.1.15" tower = { version = "0.4.13", features = ["util", "buffer"] } color-eyre = "0.6.3" -zcash_primitives = { version = "0.13.0" } +zcash_primitives = { version = "0.14.0" } zebra-node-services = { path = "../zebra-node-services", version = "1.0.0-beta.37", features = ["shielded-scan"] } zebra-chain = { path = "../zebra-chain" , version = "1.0.0-beta.37" } diff --git a/zebra-rpc/Cargo.toml b/zebra-rpc/Cargo.toml index 3ecf6f345..1ea74c342 100644 --- a/zebra-rpc/Cargo.toml +++ b/zebra-rpc/Cargo.toml @@ -64,7 +64,7 @@ tracing = "0.1.39" hex = { version = "0.4.3", features = ["serde"] } serde = { version = "1.0.203", features = ["serde_derive"] } -zcash_primitives = { version = "0.13.0" } +zcash_primitives = { version = "0.14.0" } # Experimental feature getblocktemplate-rpcs rand = { version = "0.8.5", optional = true } diff --git a/zebra-scan/Cargo.toml b/zebra-scan/Cargo.toml index bc5eb220f..bdc8db7b6 100644 --- a/zebra-scan/Cargo.toml +++ b/zebra-scan/Cargo.toml @@ -51,8 +51,11 @@ tower = "0.4.13" tracing = "0.1.39" futures = "0.3.30" -zcash_client_backend = "0.10.0-rc.1" -zcash_primitives = "0.13.0" +zcash_client_backend = { version = "0.12.1" } +zcash_keys = { version = "0.2.0", features = ["sapling"] } +zcash_primitives = "0.14.0" +zcash_address = "0.3.2" +sapling = { package = "sapling-crypto", version = "0.1" } zebra-chain = { path = "../zebra-chain", version = "1.0.0-beta.37", features = ["shielded-scan"] } zebra-state = { path = "../zebra-state", version = "1.0.0-beta.37", features = ["shielded-scan"] } diff --git a/zebra-scan/src/lib.rs b/zebra-scan/src/lib.rs index e1cf2d93a..6416fb582 100644 --- a/zebra-scan/src/lib.rs +++ b/zebra-scan/src/lib.rs @@ -23,4 +23,4 @@ pub mod tests; pub use config::Config; pub use init::{init_with_server, spawn_init}; -pub use zcash_primitives::{sapling::SaplingIvk, zip32::DiversifiableFullViewingKey}; +pub use sapling::{zip32::DiversifiableFullViewingKey, SaplingIvk}; diff --git a/zebra-scan/src/service/scan_task/commands.rs b/zebra-scan/src/service/scan_task/commands.rs index e718d742c..d500f44e5 100644 --- a/zebra-scan/src/service/scan_task/commands.rs +++ b/zebra-scan/src/service/scan_task/commands.rs @@ -8,12 +8,12 @@ use tokio::sync::{ oneshot, }; -use zcash_primitives::{sapling::SaplingIvk, zip32::DiversifiableFullViewingKey}; +use sapling::zip32::DiversifiableFullViewingKey; use zebra_chain::{block::Height, parameters::Network}; use zebra_node_services::scan_service::response::ScanResult; use zebra_state::SaplingScanningKey; -use crate::scan::sapling_key_to_scan_block_keys; +use crate::scan::sapling_key_to_dfvk; use super::ScanTask; @@ -57,17 +57,11 @@ impl ScanTask { /// Returns newly registered keys for scanning. pub fn process_messages( cmd_receiver: &mut tokio::sync::mpsc::Receiver, - registered_keys: &mut HashMap< - SaplingScanningKey, - (Vec, Vec), - >, + registered_keys: &mut HashMap, network: &Network, ) -> Result< ( - HashMap< - SaplingScanningKey, - (Vec, Vec, Height), - >, + HashMap, HashMap>, Vec<(Receiver, oneshot::Sender>)>, ), @@ -117,9 +111,9 @@ impl ScanTask { sapling_activation_height }; - sapling_key_to_scan_block_keys(&key.0, network) + sapling_key_to_dfvk(&key.0, network) .ok() - .map(|parsed| (key.0, (parsed.0, parsed.1, birth_height))) + .map(|parsed| (key.0, (parsed, birth_height))) }) .collect(); @@ -128,10 +122,7 @@ impl ScanTask { new_keys.extend(keys.clone()); - registered_keys.extend( - keys.into_iter() - .map(|(key, (dfvks, ivks, _))| (key, (dfvks, ivks))), - ); + registered_keys.extend(keys.into_iter().map(|(key, (dfvk, _))| (key, dfvk))); } ScanTaskCommand::RemoveKeys { done_tx, keys } => { diff --git a/zebra-scan/src/service/scan_task/scan.rs b/zebra-scan/src/service/scan_task/scan.rs index 12bbb86a1..d605328fb 100644 --- a/zebra-scan/src/service/scan_task/scan.rs +++ b/zebra-scan/src/service/scan_task/scan.rs @@ -15,18 +15,19 @@ use tokio::{ use tower::{buffer::Buffer, util::BoxService, Service, ServiceExt}; use tracing::Instrument; +use zcash_address::unified::{Encoding, Fvk, Ufvk}; use zcash_client_backend::{ data_api::ScannedBlock, encoding::decode_extended_full_viewing_key, + keys::UnifiedFullViewingKey, proto::compact_formats::{ ChainMetadata, CompactBlock, CompactSaplingOutput, CompactSaplingSpend, CompactTx, }, - scanning::{ScanError, ScanningKey}, -}; -use zcash_primitives::{ - sapling::SaplingIvk, - zip32::{AccountId, DiversifiableFullViewingKey, Scope}, + scanning::{Nullifiers, ScanError, ScanningKeys}, }; +use zcash_primitives::zip32::{AccountId, Scope}; + +use sapling::zip32::DiversifiableFullViewingKey; use zebra_chain::{ block::{Block, Height}, @@ -105,15 +106,9 @@ pub async fn start( // Parse and convert keys once, then use them to scan all blocks. // There is some cryptography here, but it should be fast even with thousands of keys. - let mut parsed_keys: HashMap< - SaplingScanningKey, - (Vec, Vec), - > = key_heights + let mut parsed_keys: HashMap = key_heights .keys() - .map(|key| { - let parsed_keys = sapling_key_to_scan_block_keys(key, &network)?; - Ok::<_, Report>((key.clone(), parsed_keys)) - }) + .map(|key| Ok::<_, Report>((key.clone(), sapling_key_to_dfvk(key, &network)?))) .try_collect()?; let mut subscribed_keys: HashMap> = HashMap::new(); @@ -165,7 +160,7 @@ pub async fn start( let start_height = new_keys .iter() - .map(|(_, (_, _, height))| *height) + .map(|(_, (_, height))| *height) .min() .unwrap_or(sapling_activation_height); @@ -251,7 +246,7 @@ pub async fn scan_height_and_store_results( chain_tip_change: Option, storage: Storage, key_last_scanned_heights: Arc>, - parsed_keys: HashMap, Vec)>, + parsed_keys: HashMap, subscribed_keys_receiver: watch::Receiver>>>, ) -> Result, Report> { let network = storage.network(); @@ -277,8 +272,8 @@ pub async fn scan_height_and_store_results( _ => unreachable!("unmatched response to a state::Block request"), }; - for (key_index_in_task, (sapling_key, (dfvks, ivks))) in parsed_keys.into_iter().enumerate() { - match key_last_scanned_heights.get(&sapling_key) { + for (key_index_in_task, (sapling_key, _)) in parsed_keys.iter().enumerate() { + match key_last_scanned_heights.get(sapling_key) { // Only scan what was not scanned for each key Some(last_scanned_height) if height <= *last_scanned_height => continue, @@ -312,6 +307,7 @@ pub async fn scan_height_and_store_results( let block = block.clone(); let mut storage = storage.clone(); let network = network.clone(); + let parsed_keys = parsed_keys.clone(); // We use a dummy size of the Sapling note commitment tree. // @@ -326,19 +322,19 @@ pub async fn scan_height_and_store_results( let sapling_tree_size = 1 << 16; tokio::task::spawn_blocking(move || { - let dfvk_res = - scan_block(&network, &block, sapling_tree_size, &dfvks).map_err(|e| eyre!(e))?; - let ivk_res = - scan_block(&network, &block, sapling_tree_size, &ivks).map_err(|e| eyre!(e))?; + // TODO: + // - Wait until https://github.com/zcash/librustzcash/pull/1400 makes it to a release. + // - Create the scanning keys outside of this thread and move them here instead. + let scanning_keys = scanning_keys(parsed_keys.values()).expect("scanning keys"); - let dfvk_res = scanned_block_to_db_result(dfvk_res); - let ivk_res = scanned_block_to_db_result(ivk_res); + let scanned_block = scan_block(&network, &block, sapling_tree_size, &scanning_keys) + .map_err(|e| eyre!(e))?; + + let scanning_result = scanned_block_to_db_result(scanned_block); let latest_subscribed_keys = subscribed_keys_receiver.borrow().clone(); if let Some(results_sender) = latest_subscribed_keys.get(&sapling_key).cloned() { - let results = dfvk_res.iter().chain(ivk_res.iter()); - - for (_tx_index, &tx_id) in results { + for (_tx_index, tx_id) in scanning_result.clone() { // TODO: Handle `SendErrors` by dropping sender from `subscribed_keys` let _ = results_sender.try_send(ScanResult { key: sapling_key.clone(), @@ -348,9 +344,7 @@ pub async fn scan_height_and_store_results( } } - storage.add_sapling_results(&sapling_key, height, dfvk_res); - storage.add_sapling_results(&sapling_key, height, ivk_res); - + storage.add_sapling_results(&sapling_key, height, scanning_result); Ok::<_, Report>(()) }) .wait_for_panics() @@ -361,11 +355,6 @@ pub async fn scan_height_and_store_results( } /// Returns the transactions from `block` belonging to the given `scanning_keys`. -/// This list of keys should come from a single configured `SaplingScanningKey`. -/// -/// For example, there are two individual viewing keys for most shielded transfers: -/// - the payment (external) key, and -/// - the change (internal) key. /// /// # Performance / Hangs /// @@ -375,12 +364,12 @@ pub async fn scan_height_and_store_results( /// TODO: /// - Pass the real `sapling_tree_size` parameter from the state. /// - Add other prior block metadata. -pub fn scan_block( +pub fn scan_block( network: &Network, block: &Block, sapling_tree_size: u32, - scanning_keys: &[K], -) -> Result, ScanError> { + scanning_key: &ScanningKeys, +) -> Result, ScanError> { // TODO: Implement a check that returns early when the block height is below the Sapling // activation height. @@ -390,45 +379,28 @@ pub fn scan_block( 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.as_slice(), + scanning_key, // Ignore whether notes are change from a viewer's own spends for now. - &[], + &Nullifiers::empty(), // Ignore previous blocks for now. None, ) } -/// Converts a Zebra-format scanning key into some `scan_block()` keys. -/// -/// Currently only accepts extended full viewing keys, and returns both their diversifiable full -/// viewing key and their individual viewing key, for testing purposes. -/// -// TODO: work out what string format is used for SaplingIvk, if any, and support it here -// performance: stop returning both the dfvk and ivk for the same key +/// Converts a Zebra-format scanning key into diversifiable full viewing key. // TODO: use `ViewingKey::parse` from zebra-chain instead -pub fn sapling_key_to_scan_block_keys( +pub fn sapling_key_to_dfvk( key: &SaplingScanningKey, network: &Network, -) -> Result<(Vec, Vec), Report> { - let efvk = - decode_extended_full_viewing_key(network.sapling_efvk_hrp(), key).map_err(|e| eyre!(e))?; - - // Just return all the keys for now, so we can be sure our code supports them. - let dfvk = efvk.to_diversifiable_full_viewing_key(); - let eivk = dfvk.to_ivk(Scope::External); - let iivk = dfvk.to_ivk(Scope::Internal); - - Ok((vec![dfvk], vec![eivk, iivk])) +) -> Result { + Ok( + decode_extended_full_viewing_key(network.sapling_efvk_hrp(), key) + .map_err(|e| eyre!(e))? + .to_diversifiable_full_viewing_key(), + ) } /// Converts a zebra block and meta data into a compact block. @@ -525,8 +497,8 @@ fn scanned_block_to_db_result( .iter() .map(|tx| { ( - TransactionIndex::from_usize(tx.index), - SaplingScannedResult::from_bytes_in_display_order(*tx.txid.as_ref()), + TransactionIndex::from_usize(tx.block_index()), + SaplingScannedResult::from_bytes_in_display_order(*tx.txid().as_ref()), ) }) .collect() @@ -565,3 +537,24 @@ pub fn spawn_init( ) -> JoinHandle> { tokio::spawn(start(state, chain_tip_change, storage, cmd_receiver).in_current_span()) } + +/// Turns an iterator of [`DiversifiableFullViewingKey`]s to [`ScanningKeys`]. +pub fn scanning_keys<'a>( + dfvks: impl IntoIterator, +) -> Result, Report> { + dfvks + .into_iter() + .enumerate() + .map(|(i, dfvk)| Ok((AccountId::try_from(u32::try_from(i)?)?, dfvk_to_ufvk(dfvk)?))) + .try_collect::<(_, _), Vec<(_, _)>, _>() + .map(ScanningKeys::from_account_ufvks) +} + +/// Turns a [`DiversifiableFullViewingKey`] to [`UnifiedFullViewingKey`]. +pub fn dfvk_to_ufvk(dfvk: &DiversifiableFullViewingKey) -> Result { + UnifiedFullViewingKey::parse(&Ufvk::try_from_items(vec![Fvk::try_from(( + 2, + &dfvk.to_bytes()[..], + ))?])?) + .map_err(|e| eyre!(e)) +} diff --git a/zebra-scan/src/service/scan_task/scan/scan_range.rs b/zebra-scan/src/service/scan_task/scan/scan_range.rs index b4b75cfe5..6b94e6f95 100644 --- a/zebra-scan/src/service/scan_task/scan/scan_range.rs +++ b/zebra-scan/src/service/scan_task/scan/scan_range.rs @@ -7,12 +7,12 @@ use crate::{ storage::Storage, }; use color_eyre::eyre::Report; +use sapling::zip32::DiversifiableFullViewingKey; use tokio::{ sync::{mpsc::Sender, watch}, task::JoinHandle, }; use tracing::Instrument; -use zcash_primitives::{sapling::SaplingIvk, zip32::DiversifiableFullViewingKey}; use zebra_chain::block::Height; use zebra_node_services::scan_service::response::ScanResult; use zebra_state::SaplingScanningKey; @@ -24,7 +24,7 @@ pub struct ScanRangeTaskBuilder { height_range: std::ops::Range, /// The keys to be used for scanning blocks in this task - keys: HashMap, Vec, Height)>, + keys: HashMap, /// A handle to the state service for reading the blocks and the chain tip height state: State, @@ -37,10 +37,7 @@ impl ScanRangeTaskBuilder { /// Creates a new [`ScanRangeTaskBuilder`] pub fn new( stop_height: Height, - keys: HashMap< - SaplingScanningKey, - (Vec, Vec, Height), - >, + keys: HashMap, state: State, storage: Storage, ) -> Self { @@ -83,7 +80,7 @@ impl ScanRangeTaskBuilder { // TODO: update the first parameter to `std::ops::Range` pub async fn scan_range( stop_before_height: Height, - keys: HashMap, Vec, Height)>, + keys: HashMap, state: State, storage: Storage, subscribed_keys_receiver: watch::Receiver>>>, @@ -99,7 +96,7 @@ pub async fn scan_range( let key_heights: HashMap = keys .iter() - .map(|(key, (_, _, height))| (key.clone(), *height)) + .map(|(key, (_, height))| (key.clone(), *height)) .collect(); let mut height = get_min_height(&key_heights).unwrap_or(sapling_activation_height); @@ -107,12 +104,9 @@ pub async fn scan_range( let key_heights = Arc::new(key_heights); // Parse and convert keys once, then use them to scan all blocks. - let parsed_keys: HashMap< - SaplingScanningKey, - (Vec, Vec), - > = keys + let parsed_keys: HashMap = keys .into_iter() - .map(|(key, (decoded_dfvks, decoded_ivks, _h))| (key, (decoded_dfvks, decoded_ivks))) + .map(|(key, (dfvk, _))| (key, dfvk)) .collect(); while height < stop_before_height { diff --git a/zebra-scan/src/tests.rs b/zebra-scan/src/tests.rs index 88cb2dcce..580e58e24 100644 --- a/zebra-scan/src/tests.rs +++ b/zebra-scan/src/tests.rs @@ -19,18 +19,14 @@ use zcash_client_backend::{ }, }; use zcash_note_encryption::Domain; -use zcash_primitives::{ - block::BlockHash, - consensus::BlockHeight, +use zcash_primitives::{block::BlockHash, consensus::BlockHeight, memo::MemoBytes}; + +use ::sapling::{ constants::SPENDING_KEY_GENERATOR, - memo::MemoBytes, - sapling::{ - note_encryption::{sapling_note_encryption, SaplingDomain}, - util::generate_random_rseed, - value::NoteValue, - Note, Nullifier, - }, - zip32, + note_encryption::{sapling_note_encryption, SaplingDomain}, + util::generate_random_rseed, + value::NoteValue, + zip32, Note, Nullifier, }; use zebra_chain::{ @@ -73,12 +69,12 @@ pub fn mock_sapling_scanning_keys(num_keys: u8, network: &Network) -> Vec zip32::sapling::ExtendedFullViewingKey { +pub fn mock_sapling_efvk(seed: &[u8]) -> zip32::ExtendedFullViewingKey { // TODO: Use `to_diversifiable_full_viewing_key` since `to_extended_full_viewing_key` is // deprecated. - zip32::sapling::ExtendedSpendingKey::master(seed).to_extended_full_viewing_key() + zip32::ExtendedSpendingKey::master(seed).to_extended_full_viewing_key() } /// Generates a fake block containing a Sapling output decryptable by `dfvk`. @@ -91,7 +87,7 @@ pub fn mock_sapling_efvk(seed: &[u8]) -> zip32::sapling::ExtendedFullViewingKey pub fn fake_block( height: BlockHeight, nf: Nullifier, - dfvk: &zip32::sapling::DiversifiableFullViewingKey, + dfvk: &zip32::DiversifiableFullViewingKey, value: u64, tx_after: bool, initial_sapling_tree_size: Option, @@ -165,7 +161,7 @@ pub fn fake_compact_block( height: BlockHeight, prev_hash: BlockHash, nf: Nullifier, - dfvk: &zip32::sapling::DiversifiableFullViewingKey, + dfvk: &zip32::DiversifiableFullViewingKey, value: u64, tx_after: bool, initial_sapling_tree_size: Option, @@ -174,23 +170,17 @@ pub fn fake_compact_block( // Create a fake Note for the account let mut rng = OsRng; - let rseed = generate_random_rseed( - &zcash_primitives::consensus::Network::TestNetwork, - height, - &mut rng, - ); + let rseed = generate_random_rseed(::sapling::note_encryption::Zip212Enforcement::Off, &mut rng); + let note = Note::from_parts(to, NoteValue::from_raw(value), rseed); - let encryptor = sapling_note_encryption::<_, zcash_primitives::consensus::Network>( + let encryptor = sapling_note_encryption::<_>( Some(dfvk.fvk().ovk), note.clone(), - MemoBytes::empty(), + *MemoBytes::empty().as_array(), &mut rng, ); let cmu = note.cmu().to_bytes().to_vec(); - let ephemeral_key = - SaplingDomain::::epk_bytes(encryptor.epk()) - .0 - .to_vec(); + let ephemeral_key = SaplingDomain::epk_bytes(encryptor.epk()).0.to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); // Create a fake CompactBlock containing the note diff --git a/zebra-scan/src/tests/vectors.rs b/zebra-scan/src/tests/vectors.rs index 9fff00c62..222901856 100644 --- a/zebra-scan/src/tests/vectors.rs +++ b/zebra-scan/src/tests/vectors.rs @@ -4,15 +4,15 @@ use std::sync::Arc; use color_eyre::Result; +use sapling::{ + zip32::{DiversifiableFullViewingKey, ExtendedSpendingKey}, + Nullifier, +}; use zcash_client_backend::{ encoding::{decode_extended_full_viewing_key, encode_extended_full_viewing_key}, proto::compact_formats::ChainMetadata, }; -use zcash_primitives::{ - constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, - sapling::Nullifier, - zip32::{DiversifiableFullViewingKey, ExtendedSpendingKey}, -}; +use zcash_primitives::constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY; use zebra_chain::{ block::{Block, Height}, @@ -23,7 +23,7 @@ use zebra_chain::{ use zebra_state::{SaplingScannedResult, TransactionIndex}; use crate::{ - scan::{block_to_compact, scan_block}, + scan::{block_to_compact, scan_block, scanning_keys}, storage::db::tests::new_test_storage, tests::{fake_block, mock_sapling_efvk, ZECPAGES_SAPLING_VIEWING_KEY}, }; @@ -42,7 +42,9 @@ async fn scanning_from_fake_generated_blocks() -> Result<()> { assert_eq!(block.transactions.len(), 4); - let res = scan_block(&Network::Mainnet, &block, sapling_tree_size, &[&dfvk]).unwrap(); + let scanning_keys = scanning_keys(&vec![dfvk]).expect("scanning key"); + + let res = scan_block(&Network::Mainnet, &block, sapling_tree_size, &scanning_keys).unwrap(); // The response should have one transaction relevant to the key we provided. assert_eq!(res.transactions().len(), 1); @@ -52,11 +54,11 @@ async fn scanning_from_fake_generated_blocks() -> Result<()> { .transactions .iter() .map(|tx| tx.hash().bytes_in_display_order()) - .any(|txid| &txid == res.transactions()[0].txid.as_ref())); + .any(|txid| &txid == res.transactions()[0].txid().as_ref())); // Check that the txid in the scanning result matches the third tx in the original block. assert_eq!( - res.transactions()[0].txid.as_ref(), + res.transactions()[0].txid().as_ref(), &block.transactions[2].hash().bytes_in_display_order() ); @@ -78,10 +80,7 @@ async fn scanning_zecpages_from_populated_zebra_state() -> Result<()> { ) .unwrap(); - // Build a vector of viewing keys `vks` to scan for. - let fvk = efvk.fvk; - let ivk = fvk.vk.ivk(); - let ivks = vec![ivk]; + let dfvk = efvk.to_diversifiable_full_viewing_key(); let network = Network::Mainnet; @@ -103,6 +102,9 @@ async fn scanning_zecpages_from_populated_zebra_state() -> Result<()> { let mut transactions_found = 0; let mut transactions_scanned = 0; let mut blocks_scanned = 0; + + let scanning_keys = scanning_keys(&vec![dfvk]).expect("scanning key"); + while let Some(block) = db.block(height.into()) { // We use a dummy size of the Sapling note commitment tree. We can't set the size to zero // because the underlying scanning function would return @@ -118,8 +120,13 @@ async fn scanning_zecpages_from_populated_zebra_state() -> Result<()> { let compact_block = block_to_compact(&block, chain_metadata); - let res = scan_block(&network, &block, sapling_commitment_tree_size, &ivks) - .expect("scanning block for the ZECpages viewing key should work"); + let res = scan_block( + &network, + &block, + sapling_commitment_tree_size, + &scanning_keys, + ) + .expect("scanning block for the ZECpages viewing key should work"); transactions_found += res.transactions().len(); transactions_scanned += compact_block.vtx.len(); @@ -177,13 +184,16 @@ fn scanning_fake_blocks_store_key_and_results() -> Result<()> { let (block, sapling_tree_size) = fake_block(1u32.into(), nf, &dfvk, 1, true, Some(0)); - let result = scan_block(&Network::Mainnet, &block, sapling_tree_size, &[&dfvk]).unwrap(); + let scanning_keys = scanning_keys(&vec![dfvk]).expect("scanning key"); + + let result = scan_block(&Network::Mainnet, &block, sapling_tree_size, &scanning_keys).unwrap(); // The response should have one transaction relevant to the key we provided. assert_eq!(result.transactions().len(), 1); - let result = - SaplingScannedResult::from_bytes_in_display_order(*result.transactions()[0].txid.as_ref()); + let result = SaplingScannedResult::from_bytes_in_display_order( + *result.transactions()[0].txid().as_ref(), + ); // Add result to database storage.add_sapling_results( diff --git a/zebra-utils/Cargo.toml b/zebra-utils/Cargo.toml index a5ccbf198..4f5cd6a2c 100644 --- a/zebra-utils/Cargo.toml +++ b/zebra-utils/Cargo.toml @@ -120,8 +120,9 @@ tokio = { version = "1.37.0", features = ["full"], optional = true } jsonrpc = { version = "0.18.0", optional = true } -zcash_primitives = { version = "0.13.0", optional = true } -zcash_client_backend = {version = "0.10.0-rc.1", optional = true} +zcash_primitives = { version = "0.15.0", optional = true } +zcash_client_backend = { version = "0.12.1", optional = true } +zcash_protocol = { version = "0.1.1" } # For the openapi generator syn = { version = "2.0.66", features = ["full"], optional = true } diff --git a/zebra-utils/src/bin/scanning-results-reader/main.rs b/zebra-utils/src/bin/scanning-results-reader/main.rs index 1bfa15d36..f6f48eb40 100644 --- a/zebra-utils/src/bin/scanning-results-reader/main.rs +++ b/zebra-utils/src/bin/scanning-results-reader/main.rs @@ -8,17 +8,15 @@ use std::collections::HashMap; use hex::ToHex; -use itertools::Itertools; use jsonrpc::simple_http::SimpleHttpTransport; use jsonrpc::Client; use zcash_client_backend::decrypt_transaction; -use zcash_client_backend::keys::UnifiedFullViewingKey; use zcash_primitives::consensus::{BlockHeight, BranchId}; use zcash_primitives::transaction::Transaction; use zcash_primitives::zip32::AccountId; -use zebra_scan::scan::sapling_key_to_scan_block_keys; +use zebra_scan::scan::{dfvk_to_ufvk, sapling_key_to_dfvk}; use zebra_scan::{storage::Storage, Config}; /// Prints the memos of transactions from Zebra's scanning results storage. @@ -47,20 +45,13 @@ pub fn main() { let mut prev_memo = "".to_owned(); for (key, _) in storage.sapling_keys_last_heights().iter() { - let dfvk = sapling_key_to_scan_block_keys(key, &network) - .expect("Scanning key from the storage should be valid") - .0 - .into_iter() - .exactly_one() - .expect("There should be exactly one dfvk"); - - let ufvk_with_acc_id = HashMap::from([( - AccountId::from(1), - UnifiedFullViewingKey::new(Some(dfvk), None).expect("`dfvk` should be `Some`"), + let ufvks = HashMap::from([( + AccountId::ZERO, + dfvk_to_ufvk(&sapling_key_to_dfvk(key, &network).expect("dfvk")).expect("ufvk"), )]); for (height, txids) in storage.sapling_results(key) { - let height = BlockHeight::from(height); + let height = BlockHeight::from(height.0); for txid in txids.iter() { let tx = Transaction::read( @@ -70,8 +61,8 @@ pub fn main() { ) .expect("TX fetched via RPC should be deserializable from raw bytes"); - for output in decrypt_transaction(&network, height, &tx, &ufvk_with_acc_id) { - let memo = memo_bytes_to_string(output.memo.as_array()); + for output in decrypt_transaction(&network, height, &tx, &ufvks).sapling_outputs() { + let memo = memo_bytes_to_string(output.memo().as_array()); if !memo.is_empty() // Filter out some uninteresting and repeating memos from ZECPages.