Skip to main content

zebra_chain/tests/
vectors.rs

1//! Network methods for fetching blockchain vectors.
2//!
3
4use std::{collections::BTreeMap, ops::RangeBounds};
5
6use crate::{
7    amount::Amount,
8    block::Block,
9    parameters::Network,
10    serialization::ZcashDeserializeInto,
11    transaction::{UnminedTx, VerifiedUnminedTx},
12};
13
14use zebra_test::vectors::{
15    BLOCK_MAINNET_1046400_BYTES, BLOCK_MAINNET_653599_BYTES, BLOCK_MAINNET_982681_BYTES,
16    BLOCK_TESTNET_1116000_BYTES, BLOCK_TESTNET_583999_BYTES, BLOCK_TESTNET_925483_BYTES,
17    CONTINUOUS_MAINNET_BLOCKS, CONTINUOUS_TESTNET_BLOCKS, MAINNET_BLOCKS,
18    MAINNET_FINAL_ORCHARD_ROOTS, MAINNET_FINAL_SAPLING_ROOTS, MAINNET_FINAL_SPROUT_ROOTS,
19    SAPLING_FINAL_ROOT_MAINNET_1046400_BYTES, SAPLING_FINAL_ROOT_TESTNET_1116000_BYTES,
20    TESTNET_BLOCKS, TESTNET_FINAL_ORCHARD_ROOTS, TESTNET_FINAL_SAPLING_ROOTS,
21    TESTNET_FINAL_SPROUT_ROOTS,
22};
23
24/// Network methods for fetching blockchain vectors.
25impl Network {
26    /// Returns true if network is of type Mainnet.
27    pub fn is_mainnet(&self) -> bool {
28        matches!(self, Network::Mainnet)
29    }
30
31    /// Returns iterator over blocks.
32    pub fn block_iter(&self) -> std::collections::btree_map::Iter<'static, u32, &'static [u8]> {
33        if self.is_mainnet() {
34            MAINNET_BLOCKS.iter()
35        } else {
36            TESTNET_BLOCKS.iter()
37        }
38    }
39
40    /// Returns iterator over deserialized blocks.
41    pub fn block_parsed_iter(&self) -> impl Iterator<Item = Block> {
42        self.block_iter().map(|(_, block_bytes)| {
43            block_bytes
44                .zcash_deserialize_into::<Block>()
45                .expect("block is structurally valid")
46        })
47    }
48
49    /// Returns iterator over verified unmined transactions in the provided block height range.
50    pub fn unmined_transactions_in_blocks(
51        &self,
52        block_height_range: impl RangeBounds<u32>,
53    ) -> impl DoubleEndedIterator<Item = VerifiedUnminedTx> {
54        let blocks = self.block_iter();
55
56        // Deserialize the blocks that are selected based on the specified `block_height_range`.
57        let selected_blocks = blocks
58            .filter(move |(&height, _)| block_height_range.contains(&height))
59            .map(|(_, block)| {
60                block
61                    .zcash_deserialize_into::<Block>()
62                    .expect("block test vector is structurally valid")
63            });
64
65        // Extract the transactions from the blocks and wrap each one as an unmined transaction.
66        // Use a fake zero miner fee and sigops, because we don't have the UTXOs to calculate
67        // the correct fee.
68        selected_blocks
69            .flat_map(|block| block.transactions)
70            .map(UnminedTx::from)
71            // Skip transactions that fail ZIP-317 mempool checks
72            .filter_map(|transaction| {
73                VerifiedUnminedTx::new(
74                    transaction,
75                    Amount::try_from(1_000_000).expect("valid amount"),
76                    0,
77                    0,
78                    std::sync::Arc::new(vec![]),
79                )
80                .ok()
81            })
82    }
83
84    /// Returns blocks indexed by height in a [`BTreeMap`].
85    ///
86    /// Returns Mainnet blocks if `self` is set to Mainnet, and Testnet blocks otherwise.
87    pub fn block_map(&self) -> BTreeMap<u32, &'static [u8]> {
88        if self.is_mainnet() {
89            zebra_test::vectors::MAINNET_BLOCKS.clone()
90        } else {
91            zebra_test::vectors::TESTNET_BLOCKS.clone()
92        }
93    }
94
95    /// Returns genesis block for chain.
96    pub fn gen_block(&self) -> std::option::Option<&&[u8]> {
97        if self.is_mainnet() {
98            MAINNET_BLOCKS.get(&0)
99        } else {
100            TESTNET_BLOCKS.get(&0)
101        }
102    }
103
104    /// Returns block bytes
105    pub fn test_block(&self, main_height: u32, test_height: u32) -> Option<Block> {
106        match (self.is_mainnet(), main_height, test_height) {
107            (true, 653_599, _) => BLOCK_MAINNET_653599_BYTES.zcash_deserialize_into().ok(),
108            (true, 982_681, _) => BLOCK_MAINNET_982681_BYTES.zcash_deserialize_into().ok(),
109            (false, _, 583_999) => BLOCK_TESTNET_583999_BYTES.zcash_deserialize_into().ok(),
110            (false, _, 925_483) => BLOCK_TESTNET_925483_BYTES.zcash_deserialize_into().ok(),
111            _ => None,
112        }
113    }
114
115    /// Returns iterator over blockchain.
116    pub fn blockchain_iter(&self) -> std::collections::btree_map::Iter<'_, u32, &[u8]> {
117        if self.is_mainnet() {
118            CONTINUOUS_MAINNET_BLOCKS.iter()
119        } else {
120            CONTINUOUS_TESTNET_BLOCKS.iter()
121        }
122    }
123
124    /// Returns BTreemap of blockchain.
125    pub fn blockchain_map(&self) -> &BTreeMap<u32, &'static [u8]> {
126        if self.is_mainnet() {
127            &CONTINUOUS_MAINNET_BLOCKS
128        } else {
129            &CONTINUOUS_TESTNET_BLOCKS
130        }
131    }
132
133    /// Returns a [`BTreeMap`] of heights and Sapling anchors for this network.
134    pub fn sapling_anchors(&self) -> std::collections::BTreeMap<u32, &[u8; 32]> {
135        if self.is_mainnet() {
136            MAINNET_FINAL_SAPLING_ROOTS.clone()
137        } else {
138            TESTNET_FINAL_SAPLING_ROOTS.clone()
139        }
140    }
141
142    /// Returns a [`BTreeMap`] of heights and Orchard anchors for this network.
143    pub fn orchard_anchors(&self) -> std::collections::BTreeMap<u32, &[u8; 32]> {
144        if self.is_mainnet() {
145            MAINNET_FINAL_ORCHARD_ROOTS.clone()
146        } else {
147            TESTNET_FINAL_ORCHARD_ROOTS.clone()
148        }
149    }
150
151    /// Returns BTreemap of blocks and sapling roots.
152    pub fn block_sapling_roots_map(
153        &self,
154    ) -> (
155        &std::collections::BTreeMap<u32, &'static [u8]>,
156        &std::collections::BTreeMap<u32, &'static [u8; 32]>,
157    ) {
158        if self.is_mainnet() {
159            (&*MAINNET_BLOCKS, &*MAINNET_FINAL_SAPLING_ROOTS)
160        } else {
161            (&*TESTNET_BLOCKS, &*TESTNET_FINAL_SAPLING_ROOTS)
162        }
163    }
164
165    /// Returns block and sapling root bytes
166    pub fn test_block_sapling_roots(
167        &self,
168        main_height: u32,
169        test_height: u32,
170    ) -> Option<(&[u8], [u8; 32])> {
171        match (self.is_mainnet(), main_height, test_height) {
172            (true, 1_046_400, _) => Some((
173                &BLOCK_MAINNET_1046400_BYTES[..],
174                *SAPLING_FINAL_ROOT_MAINNET_1046400_BYTES,
175            )),
176            (false, _, 1_116_000) => Some((
177                &BLOCK_TESTNET_1116000_BYTES[..],
178                *SAPLING_FINAL_ROOT_TESTNET_1116000_BYTES,
179            )),
180            _ => None,
181        }
182    }
183
184    /// Returns BTreemap of blocks and sprout roots, and last split height.
185    pub fn block_sprout_roots_height(
186        &self,
187    ) -> (
188        &std::collections::BTreeMap<u32, &'static [u8]>,
189        &std::collections::BTreeMap<u32, &'static [u8; 32]>,
190        u32,
191    ) {
192        // The mainnet block height at which the first JoinSplit occurred.
193        const MAINNET_FIRST_JOINSPLIT_HEIGHT: u32 = 396;
194
195        // The testnet block height at which the first JoinSplit occurred.
196        const TESTNET_FIRST_JOINSPLIT_HEIGHT: u32 = 2259;
197        if self.is_mainnet() {
198            (
199                &*MAINNET_BLOCKS,
200                &*MAINNET_FINAL_SPROUT_ROOTS,
201                MAINNET_FIRST_JOINSPLIT_HEIGHT,
202            )
203        } else {
204            (
205                &*TESTNET_BLOCKS,
206                &*TESTNET_FINAL_SPROUT_ROOTS,
207                TESTNET_FIRST_JOINSPLIT_HEIGHT,
208            )
209        }
210    }
211}