1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//! Network methods for fetching blockchain vectors.
//!

use std::{collections::BTreeMap, ops::RangeBounds};

use crate::{
    amount::Amount,
    block::Block,
    parameters::Network,
    serialization::ZcashDeserializeInto,
    transaction::{UnminedTx, VerifiedUnminedTx},
};

use zebra_test::vectors::{
    BLOCK_MAINNET_1046400_BYTES, BLOCK_MAINNET_653599_BYTES, BLOCK_MAINNET_982681_BYTES,
    BLOCK_TESTNET_1116000_BYTES, BLOCK_TESTNET_583999_BYTES, BLOCK_TESTNET_925483_BYTES,
    CONTINUOUS_MAINNET_BLOCKS, CONTINUOUS_TESTNET_BLOCKS, MAINNET_BLOCKS,
    MAINNET_FINAL_SAPLING_ROOTS, MAINNET_FINAL_SPROUT_ROOTS,
    SAPLING_FINAL_ROOT_MAINNET_1046400_BYTES, SAPLING_FINAL_ROOT_TESTNET_1116000_BYTES,
    TESTNET_BLOCKS, TESTNET_FINAL_SAPLING_ROOTS, TESTNET_FINAL_SPROUT_ROOTS,
};

/// Network methods for fetching blockchain vectors.
impl Network {
    /// Returns true if network is of type Mainnet.
    pub fn is_mainnet(&self) -> bool {
        matches!(self, Network::Mainnet)
    }

    /// Returns iterator over blocks.
    pub fn block_iter(&self) -> std::collections::btree_map::Iter<'static, u32, &'static [u8]> {
        if self.is_mainnet() {
            MAINNET_BLOCKS.iter()
        } else {
            TESTNET_BLOCKS.iter()
        }
    }

    /// Returns iterator over verified unmined transactions in the provided block height range.
    pub fn unmined_transactions_in_blocks(
        &self,
        block_height_range: impl RangeBounds<u32>,
    ) -> impl DoubleEndedIterator<Item = VerifiedUnminedTx> {
        let blocks = self.block_iter();

        // Deserialize the blocks that are selected based on the specified `block_height_range`.
        let selected_blocks = blocks
            .filter(move |(&height, _)| block_height_range.contains(&height))
            .map(|(_, block)| {
                block
                    .zcash_deserialize_into::<Block>()
                    .expect("block test vector is structurally valid")
            });

        // Extract the transactions from the blocks and wrap each one as an unmined transaction.
        // Use a fake zero miner fee and sigops, because we don't have the UTXOs to calculate
        // the correct fee.
        selected_blocks
            .flat_map(|block| block.transactions)
            .map(UnminedTx::from)
            // Skip transactions that fail ZIP-317 mempool checks
            .filter_map(|transaction| {
                VerifiedUnminedTx::new(
                    transaction,
                    Amount::try_from(1_000_000).expect("invalid value"),
                    0,
                )
                .ok()
            })
    }

    /// Returns blocks indexed by height in a [`BTreeMap`].
    ///
    /// Returns Mainnet blocks if `self` is set to Mainnet, and Testnet blocks otherwise.
    pub fn block_map(&self) -> BTreeMap<u32, &'static [u8]> {
        if self.is_mainnet() {
            zebra_test::vectors::MAINNET_BLOCKS.clone()
        } else {
            zebra_test::vectors::TESTNET_BLOCKS.clone()
        }
    }

    /// Returns genesis block for chain.
    pub fn gen_block(&self) -> std::option::Option<&&[u8]> {
        if self.is_mainnet() {
            MAINNET_BLOCKS.get(&0)
        } else {
            TESTNET_BLOCKS.get(&0)
        }
    }

    /// Returns block bytes
    pub fn test_block(&self, main_height: u32, test_height: u32) -> Option<Block> {
        match (self.is_mainnet(), main_height, test_height) {
            (true, 653_599, _) => BLOCK_MAINNET_653599_BYTES.zcash_deserialize_into().ok(),
            (true, 982_681, _) => BLOCK_MAINNET_982681_BYTES.zcash_deserialize_into().ok(),
            (false, _, 583_999) => BLOCK_TESTNET_583999_BYTES.zcash_deserialize_into().ok(),
            (false, _, 925_483) => BLOCK_TESTNET_925483_BYTES.zcash_deserialize_into().ok(),
            _ => None,
        }
    }

    /// Returns iterator over blockchain.
    pub fn blockchain_iter(&self) -> std::collections::btree_map::Iter<'_, u32, &[u8]> {
        if self.is_mainnet() {
            CONTINUOUS_MAINNET_BLOCKS.iter()
        } else {
            CONTINUOUS_TESTNET_BLOCKS.iter()
        }
    }

    /// Returns BTreemap of blockchain.
    pub fn blockchain_map(&self) -> &BTreeMap<u32, &'static [u8]> {
        if self.is_mainnet() {
            &CONTINUOUS_MAINNET_BLOCKS
        } else {
            &CONTINUOUS_TESTNET_BLOCKS
        }
    }

    /// Returns iterator over blocks and sapling roots.
    pub fn block_sapling_roots_iter(
        &self,
    ) -> (
        std::collections::btree_map::Iter<'_, u32, &[u8]>,
        std::collections::BTreeMap<u32, &[u8; 32]>,
    ) {
        if self.is_mainnet() {
            (MAINNET_BLOCKS.iter(), MAINNET_FINAL_SAPLING_ROOTS.clone())
        } else {
            (TESTNET_BLOCKS.iter(), TESTNET_FINAL_SAPLING_ROOTS.clone())
        }
    }

    /// Returns BTreemap of blocks and sapling roots.
    pub fn block_sapling_roots_map(
        &self,
    ) -> (
        &std::collections::BTreeMap<u32, &'static [u8]>,
        &std::collections::BTreeMap<u32, &'static [u8; 32]>,
    ) {
        if self.is_mainnet() {
            (&*MAINNET_BLOCKS, &*MAINNET_FINAL_SAPLING_ROOTS)
        } else {
            (&*TESTNET_BLOCKS, &*TESTNET_FINAL_SAPLING_ROOTS)
        }
    }

    /// Returns block and sapling root bytes
    pub fn test_block_sapling_roots(
        &self,
        main_height: u32,
        test_height: u32,
    ) -> Option<(&[u8], [u8; 32])> {
        match (self.is_mainnet(), main_height, test_height) {
            (true, 1_046_400, _) => Some((
                &BLOCK_MAINNET_1046400_BYTES[..],
                *SAPLING_FINAL_ROOT_MAINNET_1046400_BYTES,
            )),
            (false, _, 1_116_000) => Some((
                &BLOCK_TESTNET_1116000_BYTES[..],
                *SAPLING_FINAL_ROOT_TESTNET_1116000_BYTES,
            )),
            _ => None,
        }
    }

    /// Returns BTreemap of blocks and sprout roots, and last split height.
    pub fn block_sprout_roots_height(
        &self,
    ) -> (
        &std::collections::BTreeMap<u32, &'static [u8]>,
        &std::collections::BTreeMap<u32, &'static [u8; 32]>,
        u32,
    ) {
        // The mainnet block height at which the first JoinSplit occurred.
        const MAINNET_FIRST_JOINSPLIT_HEIGHT: u32 = 396;

        // The testnet block height at which the first JoinSplit occurred.
        const TESTNET_FIRST_JOINSPLIT_HEIGHT: u32 = 2259;
        if self.is_mainnet() {
            (
                &*MAINNET_BLOCKS,
                &*MAINNET_FINAL_SPROUT_ROOTS,
                MAINNET_FIRST_JOINSPLIT_HEIGHT,
            )
        } else {
            (
                &*TESTNET_BLOCKS,
                &*TESTNET_FINAL_SPROUT_ROOTS,
                TESTNET_FIRST_JOINSPLIT_HEIGHT,
            )
        }
    }
}