From ef18131f6a8fcbb25797f6c0d1ff4160aa130e8b Mon Sep 17 00:00:00 2001 From: Marek Date: Mon, 4 Dec 2023 13:04:16 +0100 Subject: [PATCH 1/7] Derive & impl helper traits from `std` --- zebra-chain/src/amount.rs | 2 +- zebra-chain/src/block/hash.rs | 2 +- zebra-chain/src/block/header.rs | 2 +- zebra-chain/src/block/merkle.rs | 2 +- zebra-chain/src/fmt.rs | 2 +- zebra-chain/src/sapling/commitment.rs | 4 ++-- zebra-chain/src/sapling/note/ciphertexts.rs | 12 ++++++++++++ zebra-chain/src/work/difficulty.rs | 2 +- zebra-chain/src/work/equihash.rs | 6 ++++++ 9 files changed, 26 insertions(+), 8 deletions(-) diff --git a/zebra-chain/src/amount.rs b/zebra-chain/src/amount.rs index b07a59642bd..ae7f5726358 100644 --- a/zebra-chain/src/amount.rs +++ b/zebra-chain/src/amount.rs @@ -491,7 +491,7 @@ impl Error { /// -MAX_MONEY..=MAX_MONEY, /// ); /// ``` -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] pub struct NegativeAllowed; impl Constraint for NegativeAllowed { diff --git a/zebra-chain/src/block/hash.rs b/zebra-chain/src/block/hash.rs index bf2922054fa..f1c37371b21 100644 --- a/zebra-chain/src/block/hash.rs +++ b/zebra-chain/src/block/hash.rs @@ -20,7 +20,7 @@ use proptest_derive::Arbitrary; /// /// Note: Zebra displays transaction and block hashes in big-endian byte-order, /// following the u256 convention set by Bitcoin and zcashd. -#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Default)] #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] pub struct Hash(pub [u8; 32]); diff --git a/zebra-chain/src/block/header.rs b/zebra-chain/src/block/header.rs index c59d3972ff5..b47adac0b44 100644 --- a/zebra-chain/src/block/header.rs +++ b/zebra-chain/src/block/header.rs @@ -22,7 +22,7 @@ use proptest_derive::Arbitrary; /// backwards reference (previous header hash) present in the block /// header. Each block points backwards to its parent, all the way /// back to the genesis block (the first block in the blockchain). -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Default)] pub struct Header { /// The block's version field. This is supposed to be `4`: /// diff --git a/zebra-chain/src/block/merkle.rs b/zebra-chain/src/block/merkle.rs index 42762bbe6ca..3f0c9ebf973 100644 --- a/zebra-chain/src/block/merkle.rs +++ b/zebra-chain/src/block/merkle.rs @@ -69,7 +69,7 @@ use proptest_derive::Arbitrary; /// aggressive anti-DoS mechanism. /// /// [ZIP-244]: https://zips.z.cash/zip-0244 -#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Default)] #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] pub struct Root(pub [u8; 32]); diff --git a/zebra-chain/src/fmt.rs b/zebra-chain/src/fmt.rs index 98923446c99..708e6302121 100644 --- a/zebra-chain/src/fmt.rs +++ b/zebra-chain/src/fmt.rs @@ -163,7 +163,7 @@ where /// Wrapper to override `Debug`, redirecting it to hex-encode the type. /// The type must implement `AsRef<[u8]>`. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default)] #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] #[serde(transparent)] pub struct HexDebug>(pub T); diff --git a/zebra-chain/src/sapling/commitment.rs b/zebra-chain/src/sapling/commitment.rs index 65c789dc6ed..bda033eab3d 100644 --- a/zebra-chain/src/sapling/commitment.rs +++ b/zebra-chain/src/sapling/commitment.rs @@ -157,7 +157,7 @@ impl NoteCommitment { /// [`NotSmallOrderValueCommitment`]. /// /// -#[derive(Clone, Copy, Deserialize, PartialEq, Eq, Serialize)] +#[derive(Clone, Copy, Deserialize, PartialEq, Eq, Serialize, Default)] pub struct ValueCommitment(#[serde(with = "serde_helpers::AffinePoint")] jubjub::AffinePoint); impl<'a> std::ops::Add<&'a ValueCommitment> for ValueCommitment { @@ -301,7 +301,7 @@ lazy_static! { /// /// /// -#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq, Serialize)] +#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq, Serialize, Default)] pub struct NotSmallOrderValueCommitment(ValueCommitment); impl TryFrom for NotSmallOrderValueCommitment { diff --git a/zebra-chain/src/sapling/note/ciphertexts.rs b/zebra-chain/src/sapling/note/ciphertexts.rs index 472dbfb0a44..75d25730627 100644 --- a/zebra-chain/src/sapling/note/ciphertexts.rs +++ b/zebra-chain/src/sapling/note/ciphertexts.rs @@ -12,6 +12,12 @@ use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize} #[derive(Deserialize, Serialize)] pub struct EncryptedNote(#[serde(with = "BigArray")] pub(crate) [u8; 580]); +impl From<[u8; 580]> for EncryptedNote { + fn from(byte_array: [u8; 580]) -> Self { + Self(byte_array) + } +} + impl fmt::Debug for EncryptedNote { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("EncryptedNote") @@ -59,6 +65,12 @@ impl ZcashDeserialize for EncryptedNote { #[derive(Deserialize, Serialize)] pub struct WrappedNoteKey(#[serde(with = "BigArray")] pub(crate) [u8; 80]); +impl From<[u8; 80]> for WrappedNoteKey { + fn from(byte_array: [u8; 80]) -> Self { + Self(byte_array) + } +} + impl fmt::Debug for WrappedNoteKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("WrappedNoteKey") diff --git a/zebra-chain/src/work/difficulty.rs b/zebra-chain/src/work/difficulty.rs index a15c1f13ce8..604ea7fa60f 100644 --- a/zebra-chain/src/work/difficulty.rs +++ b/zebra-chain/src/work/difficulty.rs @@ -62,7 +62,7 @@ mod tests; /// > (see equations in the Zcash Specification [section 7.7.4]) /// /// [section 7.7.4]: https://zips.z.cash/protocol/protocol.pdf#nbits -#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Default)] pub struct CompactDifficulty(pub(crate) u32); /// An invalid CompactDifficulty value, for testing. diff --git a/zebra-chain/src/work/equihash.rs b/zebra-chain/src/work/equihash.rs index e8b73b1614a..684961e0eb1 100644 --- a/zebra-chain/src/work/equihash.rs +++ b/zebra-chain/src/work/equihash.rs @@ -93,6 +93,12 @@ impl Clone for Solution { impl Eq for Solution {} +impl Default for Solution { + fn default() -> Self { + Self([0; SOLUTION_SIZE]) + } +} + impl ZcashSerialize for Solution { fn zcash_serialize(&self, writer: W) -> Result<(), io::Error> { zcash_serialize_bytes(&self.0.to_vec(), writer) From 9cf75745e985b66e97d36e30670d9dd0b2efc393 Mon Sep 17 00:00:00 2001 From: Marek Date: Mon, 4 Dec 2023 13:09:52 +0100 Subject: [PATCH 2/7] Create `compact_to_v4` fn --- zebra-chain/src/sapling.rs | 4 +- zebra-scan/src/tests.rs | 100 ++++++++++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 7 deletions(-) diff --git a/zebra-chain/src/sapling.rs b/zebra-chain/src/sapling.rs index 11bfd899d9c..d9b30ba430c 100644 --- a/zebra-chain/src/sapling.rs +++ b/zebra-chain/src/sapling.rs @@ -24,7 +24,9 @@ pub mod shielded_data; pub mod spend; pub mod tree; -pub use commitment::{CommitmentRandomness, NoteCommitment, ValueCommitment}; +pub use commitment::{ + CommitmentRandomness, NotSmallOrderValueCommitment, NoteCommitment, ValueCommitment, +}; pub use keys::Diversifier; pub use note::{EncryptedNote, Note, Nullifier, WrappedNoteKey}; pub use output::{Output, OutputInTransactionV4, OutputPrefixInTransactionV5}; diff --git a/zebra-scan/src/tests.rs b/zebra-scan/src/tests.rs index d11b6a10258..c5447ea319b 100644 --- a/zebra-scan/src/tests.rs +++ b/zebra-scan/src/tests.rs @@ -5,10 +5,10 @@ use std::sync::Arc; -use color_eyre::Result; +use color_eyre::{Report, Result}; use ff::{Field, PrimeField}; use group::GroupEncoding; -use rand::{rngs::OsRng, RngCore}; +use rand::{rngs::OsRng, thread_rng, RngCore}; use zcash_client_backend::{ encoding::decode_extended_full_viewing_key, @@ -28,15 +28,21 @@ use zcash_primitives::{ value::NoteValue, Note, Nullifier, SaplingIvk, }, - zip32::{AccountId, DiversifiableFullViewingKey, ExtendedSpendingKey}, + zip32::{DiversifiableFullViewingKey, ExtendedSpendingKey}, }; use zebra_chain::{ - block::{Block, Height}, + amount::{Amount, NegativeAllowed}, + block::{self, merkle, Block, Header, Height}, chain_tip::ChainTip, + fmt::HexDebug, parameters::Network, - serialization::ZcashDeserializeInto, - transaction::Hash, + primitives::{redjubjub, Groth16Proof}, + sapling::{self, PerSpendAnchor, Spend, TransferData}, + serialization::{AtLeastOne, ZcashDeserializeInto}, + transaction::{self, LockTime, Transaction}, + transparent::{CoinbaseData, Input}, + work::{difficulty::CompactDifficulty, equihash::Solution}, }; use crate::{ @@ -366,3 +372,85 @@ fn random_compact_tx(mut rng: impl RngCore) -> CompactTx { ctx.outputs.push(cout); ctx } + +/// Converts [`CompactTx`] to [`Transaction::V4`]. +fn compact_to_v4(tx: &CompactTx) -> Result { + let sk = redjubjub::SigningKey::::new(thread_rng()); + let vk = redjubjub::VerificationKey::from(&sk); + let dummy_rk = sapling::keys::ValidatingKey::try_from(vk) + .expect("Internally generated verification key should be convertible to a validating key."); + + let spends = tx + .spends + .iter() + .map(|spend| { + Ok(Spend { + cv: sapling::NotSmallOrderValueCommitment::default(), + per_spend_anchor: sapling::tree::Root::default(), + nullifier: sapling::Nullifier::from( + spend.nf().map_err(|_| Report::msg("Invalid nullifier."))?.0, + ), + rk: dummy_rk.clone(), + zkproof: Groth16Proof([0; 192]), + spend_auth_sig: redjubjub::Signature::::from([0; 64]), + }) + }) + .collect::>>>()?; + + let spends = AtLeastOne::>::try_from(spends)?; + + let maybe_outputs = tx + .outputs + .iter() + .map(|output| { + let mut ciphertext = output.ciphertext.clone(); + ciphertext.resize(580, 0); + let ciphertext: [u8; 580] = ciphertext + .try_into() + .map_err(|_| Report::msg("Could not convert ciphertext to `[u8; 580]`"))?; + let enc_ciphertext = sapling::EncryptedNote::from(ciphertext); + + Ok(sapling::Output { + cv: sapling::NotSmallOrderValueCommitment::default(), + cm_u: Option::from(jubjub::Fq::from_bytes( + &output + .cmu() + .map_err(|_| Report::msg("Invalid commitment."))? + .to_bytes(), + )) + .ok_or(Report::msg("Invalid commitment."))?, + ephemeral_key: sapling::keys::EphemeralPublicKey::try_from( + output + .ephemeral_key() + .map_err(|_| Report::msg("Invalid ephemeral key."))? + .0, + ) + .map_err(Report::msg)?, + enc_ciphertext, + out_ciphertext: sapling::WrappedNoteKey::from([0; 80]), + zkproof: Groth16Proof([0; 192]), + }) + }) + .collect::>>()?; + + let transfers = TransferData::SpendsAndMaybeOutputs { + shared_anchor: sapling::FieldNotPresent, + spends, + maybe_outputs, + }; + + let shielded_data = sapling::ShieldedData { + value_balance: Amount::::default(), + transfers, + binding_sig: redjubjub::Signature::::from([0; 64]), + }; + + Ok(Transaction::V4 { + inputs: vec![], + outputs: vec![], + lock_time: LockTime::Height(Height(0)), + expiry_height: Height(0), + joinsplit_data: None, + sapling_shielded_data: (Some(shielded_data)), + }) +} From 1692f3085b96ddf3419357c3c9de309f7e881f2e Mon Sep 17 00:00:00 2001 From: Marek Date: Mon, 4 Dec 2023 13:10:49 +0100 Subject: [PATCH 3/7] Create `fake_block` fn --- zebra-scan/src/tests.rs | 69 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/zebra-scan/src/tests.rs b/zebra-scan/src/tests.rs index c5447ea319b..4893a4ae8d6 100644 --- a/zebra-scan/src/tests.rs +++ b/zebra-scan/src/tests.rs @@ -247,6 +247,75 @@ fn scanning_fake_blocks_store_key_and_results() -> Result<()> { Ok(()) } +/// Generates a fake block containing a Sapling output decryptable by `dfvk`. +fn fake_block( + height: BlockHeight, + nf: Nullifier, + dfvk: &DiversifiableFullViewingKey, + value: u64, + tx_after: bool, + initial_sapling_tree_size: Option, +) -> (Block, u32) { + let header = Header { + version: 4, + previous_block_hash: block::Hash::default(), + merkle_root: merkle::Root::default(), + commitment_bytes: HexDebug::default(), + time: DateTime::::default(), + difficulty_threshold: CompactDifficulty::default(), + nonce: HexDebug::default(), + solution: Solution::default(), + }; + + let block = fake_compact_block( + height, + BlockHash([0; 32]), + nf, + dfvk, + value, + tx_after, + initial_sapling_tree_size, + ); + + let mut transactions: Vec> = block + .vtx + .iter() + .map(|tx| compact_to_v4(tx).expect("A fake compact tx should be convertible to V4.")) + .map(Arc::new) + .collect(); + + let coinbase_input = Input::Coinbase { + height: Height(1), + data: CoinbaseData::new(vec![]), + sequence: u32::MAX, + }; + + let coinbase = Transaction::V4 { + inputs: vec![coinbase_input], + outputs: vec![], + lock_time: LockTime::Height(Height(1)), + expiry_height: Height(1), + joinsplit_data: None, + sapling_shielded_data: None, + }; + + transactions.insert(0, Arc::new(coinbase)); + + let sapling_tree_size = block + .chain_metadata + .as_ref() + .unwrap() + .sapling_commitment_tree_size; + + ( + Block { + header: Arc::new(header), + transactions, + }, + sapling_tree_size, + ) +} + /// Create a fake compact block with provided fake account data. // This is a copy of zcash_primitives `fake_compact_block` where the `value` argument was changed to // be a number for easier conversion: From c84f1bc0d7fba97ea215367dced4a447a7bebcde Mon Sep 17 00:00:00 2001 From: Marek Date: Mon, 4 Dec 2023 13:11:31 +0100 Subject: [PATCH 4/7] Refactor existing tests to use the new functions --- Cargo.lock | 1 + zebra-scan/Cargo.toml | 2 + zebra-scan/src/tests.rs | 109 ++++++++++++++++++++-------------------- 3 files changed, 58 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6fdbefe341c..b0908dc2463 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5798,6 +5798,7 @@ name = "zebra-scan" version = "0.1.0-alpha.0" dependencies = [ "bls12_381", + "chrono", "color-eyre", "ff", "group", diff --git a/zebra-scan/Cargo.toml b/zebra-scan/Cargo.toml index c4cc20f40ea..802897a22fa 100644 --- a/zebra-scan/Cargo.toml +++ b/zebra-scan/Cargo.toml @@ -35,6 +35,8 @@ zcash_primitives = "0.13.0-rc.1" zebra-chain = { path = "../zebra-chain", version = "1.0.0-beta.31" } zebra-state = { path = "../zebra-state", version = "1.0.0-beta.31", features = ["shielded-scan"] } +chrono = { version = "0.4.31", default-features = false, features = ["clock", "std", "serde"] } + [dev-dependencies] bls12_381 = "0.8.0" diff --git a/zebra-scan/src/tests.rs b/zebra-scan/src/tests.rs index 4893a4ae8d6..658e0f5015b 100644 --- a/zebra-scan/src/tests.rs +++ b/zebra-scan/src/tests.rs @@ -5,6 +5,8 @@ use std::sync::Arc; +use chrono::{DateTime, Utc}; + use color_eyre::{Report, Result}; use ff::{Field, PrimeField}; use group::GroupEncoding; @@ -26,7 +28,7 @@ use zcash_primitives::{ note_encryption::{sapling_note_encryption, SaplingDomain}, util::generate_random_rseed, value::NoteValue, - Note, Nullifier, SaplingIvk, + Note, Nullifier, }, zip32::{DiversifiableFullViewingKey, ExtendedSpendingKey}, }; @@ -50,49 +52,51 @@ use crate::{ scan::{block_to_compact, scan_block}, }; -/// Prove that we can create fake blocks with fake notes and scan them using the -/// `zcash_client_backend::scanning::scan_block` function: -/// - Function `fake_compact_block` will generate 1 block with one pre created fake nullifier in -/// the transaction and one additional random transaction without it. -/// - Verify one relevant transaction is found in the chain when scanning for the pre created fake -/// account's nullifier. -#[test] -fn scanning_from_fake_generated_blocks() -> Result<()> { - let account = AccountId::from(12); +/// This test: +/// - Creates a viewing key and a fake block containing a Sapling output decryptable by the key. +/// - Scans the block. +/// - Checks that the result contains the txid of the tx containin the Sapling output. +#[tokio::test] +async fn scanning_from_fake_generated_blocks() -> Result<()> { let extsk = ExtendedSpendingKey::master(&[]); let dfvk: DiversifiableFullViewingKey = extsk.to_diversifiable_full_viewing_key(); - let vks: Vec<(&AccountId, &SaplingIvk)> = vec![]; let nf = Nullifier([7; 32]); - let cb = fake_compact_block( - 1u32.into(), - BlockHash([0; 32]), - nf, - &dfvk, - 1, - false, - Some(0), - ); + let (block, sapling_tree_size) = fake_block(1u32.into(), nf, &dfvk, 1, true, Some(0)); - // The fake block function will have our transaction and a random one. - assert_eq!(cb.vtx.len(), 2); + // The fake block has the following transactions in this order: + // 1. a transparent coinbase tx, + // 2. a V4 tx containing a random Sapling output, + // 3. a V4 tx containing a Sapling output decryptable by `dfvk`, + // 4. another V4 tx containing a random Sapling output. + assert_eq!(block.transactions.len(), 4); - let res = zcash_client_backend::scanning::scan_block( - &zcash_primitives::consensus::MainNetwork, - cb.clone(), - &vks[..], - &[(account, nf)], - None, + let res = scan_block( + Network::Mainnet, + &Arc::new(block.clone()), + sapling_tree_size, + &[&dfvk], ) .unwrap(); // The response should have one transaction relevant to the key we provided. assert_eq!(res.transactions().len(), 1); - // The transaction should be the one we provided, second one in the block. - // (random transaction is added before ours in `fake_compact_block` function) - assert_eq!(res.transactions()[0].txid, cb.vtx[1].txid()); + + // Check that the original block contains the txid in the scanning result. + assert!(block + .transactions + .iter() + .map(|tx| tx.hash().bytes_in_display_order()) + .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(), + &block.transactions[2].hash().bytes_in_display_order() + ); + // The block hash of the response should be the same as the one provided. - assert_eq!(res.block_hash(), cb.hash()); + assert_eq!(res.block_hash().0, block.hash().0); Ok(()) } @@ -176,13 +180,14 @@ async fn scanning_zecpages_from_populated_zebra_state() -> Result<()> { Ok(()) } -/// In this test we generate a viewing key and manually add it to the database. Also we send results to the Storage database. +/// Creates a viewing key and a fake block containing a Sapling output decryptable by the key, scans +/// the block using the key, and adds the results to the database. +/// /// The purpose of this test is to check if our database and our scanning code are compatible. #[test] #[allow(deprecated)] fn scanning_fake_blocks_store_key_and_results() -> Result<()> { // Generate a key - let account = AccountId::from(12); let extsk = ExtendedSpendingKey::master(&[]); // TODO: find out how to do it with `to_diversifiable_full_viewing_key` as `to_extended_full_viewing_key` is deprecated. let extfvk = extsk.to_extended_full_viewing_key(); @@ -203,33 +208,29 @@ fn scanning_fake_blocks_store_key_and_results() -> Result<()> { Some(&s.min_sapling_birthday_height()) ); - let vks: Vec<(&AccountId, &SaplingIvk)> = vec![]; let nf = Nullifier([7; 32]); - // Add key to fake block - let cb = fake_compact_block( - 1u32.into(), - BlockHash([0; 32]), - nf, - &dfvk, - 1, - false, - Some(0), - ); + // The fake block has the following transactions in this order: + // 1. a transparent coinbase tx, + // 2. a V4 tx containing a random Sapling output, + // 3. a V4 tx containing a Sapling output decryptable by `dfvk`, + // 4. another V4 tx containing a random Sapling output. + let (block, sapling_tree_size) = fake_block(1u32.into(), nf, &dfvk, 1, true, Some(0)); - // Scan with our key - let res = zcash_client_backend::scanning::scan_block( - &zcash_primitives::consensus::MainNetwork, - cb.clone(), - &vks[..], - &[(account, nf)], - None, + let res = scan_block( + Network::Mainnet, + &Arc::new(block), + sapling_tree_size, + &[&dfvk], ) .unwrap(); + // The response should have one transaction relevant to the key we provided. + assert_eq!(res.transactions().len(), 1); + // Get transaction hash - let found_transaction = res.transactions()[0].txid.as_ref(); - let found_transaction_hash = Hash::from_bytes_in_display_order(found_transaction); + let found_txid = res.transactions()[0].txid.as_ref(); + let found_transaction_hash = transaction::Hash::from_bytes_in_display_order(found_txid); // Add result to database s.add_sapling_result( From 408cb721dac653f4e791b6d02f552276cde0d15c Mon Sep 17 00:00:00 2001 From: Marek Date: Mon, 4 Dec 2023 13:12:11 +0100 Subject: [PATCH 5/7] Cosmetics --- zebra-scan/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zebra-scan/src/tests.rs b/zebra-scan/src/tests.rs index 658e0f5015b..4bc1132bb02 100644 --- a/zebra-scan/src/tests.rs +++ b/zebra-scan/src/tests.rs @@ -55,7 +55,7 @@ use crate::{ /// This test: /// - Creates a viewing key and a fake block containing a Sapling output decryptable by the key. /// - Scans the block. -/// - Checks that the result contains the txid of the tx containin the Sapling output. +/// - Checks that the result contains the txid of the tx containing the Sapling output. #[tokio::test] async fn scanning_from_fake_generated_blocks() -> Result<()> { let extsk = ExtendedSpendingKey::master(&[]); @@ -121,7 +121,7 @@ async fn scanning_zecpages_from_populated_zebra_state() -> Result<()> { let ivk = fvk.vk.ivk(); let ivks = vec![ivk]; - let network = zebra_chain::parameters::Network::Mainnet; + let network = Network::Mainnet; // Create a continuous chain of mainnet blocks from genesis let blocks: Vec> = zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS From b67c342957e14ee53fd261bb53e55f3abc28706d Mon Sep 17 00:00:00 2001 From: Marek Date: Mon, 4 Dec 2023 17:37:22 +0100 Subject: [PATCH 6/7] Refactor docs --- zebra-scan/src/tests.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/zebra-scan/src/tests.rs b/zebra-scan/src/tests.rs index 4bc1132bb02..8777d0eacd6 100644 --- a/zebra-scan/src/tests.rs +++ b/zebra-scan/src/tests.rs @@ -64,11 +64,6 @@ async fn scanning_from_fake_generated_blocks() -> Result<()> { let (block, sapling_tree_size) = fake_block(1u32.into(), nf, &dfvk, 1, true, Some(0)); - // The fake block has the following transactions in this order: - // 1. a transparent coinbase tx, - // 2. a V4 tx containing a random Sapling output, - // 3. a V4 tx containing a Sapling output decryptable by `dfvk`, - // 4. another V4 tx containing a random Sapling output. assert_eq!(block.transactions.len(), 4); let res = scan_block( @@ -210,11 +205,6 @@ fn scanning_fake_blocks_store_key_and_results() -> Result<()> { let nf = Nullifier([7; 32]); - // The fake block has the following transactions in this order: - // 1. a transparent coinbase tx, - // 2. a V4 tx containing a random Sapling output, - // 3. a V4 tx containing a Sapling output decryptable by `dfvk`, - // 4. another V4 tx containing a random Sapling output. let (block, sapling_tree_size) = fake_block(1u32.into(), nf, &dfvk, 1, true, Some(0)); let res = scan_block( @@ -249,6 +239,12 @@ fn scanning_fake_blocks_store_key_and_results() -> Result<()> { } /// Generates a fake block containing a Sapling output decryptable by `dfvk`. +/// +/// The fake block has the following transactions in this order: +/// 1. a transparent coinbase tx, +/// 2. a V4 tx containing a random Sapling output, +/// 3. a V4 tx containing a Sapling output decryptable by `dfvk`, +/// 4. depending on the value of `tx_after`, another V4 tx containing a random Sapling output. fn fake_block( height: BlockHeight, nf: Nullifier, From b0bdf17009fcdae32e810e164e7a6595581db645 Mon Sep 17 00:00:00 2001 From: Marek Date: Tue, 5 Dec 2023 12:35:52 +0100 Subject: [PATCH 7/7] Put `Default` behind `cfg_attr(test)` Rationale --------- We avoid implementing `Default` on consensus-critical types because it's easy to miss an incorrect use in a review. It's easy to hide a `default()` in a call like `unwrap_or_default()` or even more subtle methods. --- zebra-chain/src/block/hash.rs | 4 ++-- zebra-chain/src/block/header.rs | 2 +- zebra-chain/src/block/merkle.rs | 4 ++-- zebra-chain/src/sapling/commitment.rs | 6 ++++-- zebra-chain/src/work/difficulty.rs | 3 ++- zebra-chain/src/work/equihash.rs | 1 + 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/zebra-chain/src/block/hash.rs b/zebra-chain/src/block/hash.rs index f1c37371b21..09a2ddc9a20 100644 --- a/zebra-chain/src/block/hash.rs +++ b/zebra-chain/src/block/hash.rs @@ -20,8 +20,8 @@ use proptest_derive::Arbitrary; /// /// Note: Zebra displays transaction and block hashes in big-endian byte-order, /// following the u256 convention set by Bitcoin and zcashd. -#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Default)] -#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Default))] pub struct Hash(pub [u8; 32]); impl Hash { diff --git a/zebra-chain/src/block/header.rs b/zebra-chain/src/block/header.rs index b47adac0b44..c59d3972ff5 100644 --- a/zebra-chain/src/block/header.rs +++ b/zebra-chain/src/block/header.rs @@ -22,7 +22,7 @@ use proptest_derive::Arbitrary; /// backwards reference (previous header hash) present in the block /// header. Each block points backwards to its parent, all the way /// back to the genesis block (the first block in the blockchain). -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Default)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct Header { /// The block's version field. This is supposed to be `4`: /// diff --git a/zebra-chain/src/block/merkle.rs b/zebra-chain/src/block/merkle.rs index 3f0c9ebf973..639324f9d82 100644 --- a/zebra-chain/src/block/merkle.rs +++ b/zebra-chain/src/block/merkle.rs @@ -69,8 +69,8 @@ use proptest_derive::Arbitrary; /// aggressive anti-DoS mechanism. /// /// [ZIP-244]: https://zips.z.cash/zip-0244 -#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Default)] -#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] +#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Default))] pub struct Root(pub [u8; 32]); impl fmt::Debug for Root { diff --git a/zebra-chain/src/sapling/commitment.rs b/zebra-chain/src/sapling/commitment.rs index bda033eab3d..1ef23081550 100644 --- a/zebra-chain/src/sapling/commitment.rs +++ b/zebra-chain/src/sapling/commitment.rs @@ -157,7 +157,8 @@ impl NoteCommitment { /// [`NotSmallOrderValueCommitment`]. /// /// -#[derive(Clone, Copy, Deserialize, PartialEq, Eq, Serialize, Default)] +#[derive(Clone, Copy, Deserialize, PartialEq, Eq, Serialize)] +#[cfg_attr(any(test, feature = "proptest-impl"), derive(Default))] pub struct ValueCommitment(#[serde(with = "serde_helpers::AffinePoint")] jubjub::AffinePoint); impl<'a> std::ops::Add<&'a ValueCommitment> for ValueCommitment { @@ -301,7 +302,8 @@ lazy_static! { /// /// /// -#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq, Serialize, Default)] +#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq, Serialize)] +#[cfg_attr(any(test, feature = "proptest-impl"), derive(Default))] pub struct NotSmallOrderValueCommitment(ValueCommitment); impl TryFrom for NotSmallOrderValueCommitment { diff --git a/zebra-chain/src/work/difficulty.rs b/zebra-chain/src/work/difficulty.rs index 604ea7fa60f..4724188060c 100644 --- a/zebra-chain/src/work/difficulty.rs +++ b/zebra-chain/src/work/difficulty.rs @@ -62,7 +62,8 @@ mod tests; /// > (see equations in the Zcash Specification [section 7.7.4]) /// /// [section 7.7.4]: https://zips.z.cash/protocol/protocol.pdf#nbits -#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Default)] +#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "proptest-impl"), derive(Default))] pub struct CompactDifficulty(pub(crate) u32); /// An invalid CompactDifficulty value, for testing. diff --git a/zebra-chain/src/work/equihash.rs b/zebra-chain/src/work/equihash.rs index 684961e0eb1..f65438a5314 100644 --- a/zebra-chain/src/work/equihash.rs +++ b/zebra-chain/src/work/equihash.rs @@ -93,6 +93,7 @@ impl Clone for Solution { impl Eq for Solution {} +#[cfg(any(test, feature = "proptest-impl"))] impl Default for Solution { fn default() -> Self { Self([0; SOLUTION_SIZE])