tendermint_testgen/
light_chain.rs

1use tendermint::{
2    block::{self, Height},
3    chain::Info,
4};
5
6use crate::{light_block::LightBlock, Generator};
7
8#[derive(Clone, Debug)]
9pub struct LightChain {
10    pub info: Info,
11    pub light_blocks: Vec<LightBlock>,
12}
13
14impl LightChain {
15    pub fn new(info: Info, light_blocks: Vec<LightBlock>) -> Self {
16        LightChain { info, light_blocks }
17    }
18
19    // TODO: make this fn more usable
20    // TODO: like how does someone generate a chain with different validators at each height
21    pub fn default_with_length(num: u64) -> Self {
22        let mut last_block = LightBlock::new_default(1);
23        let mut light_blocks: Vec<LightBlock> = vec![last_block.clone()];
24
25        for _i in 2..=num {
26            // add "next" light block to the vector
27            last_block = last_block.next();
28            light_blocks.push(last_block.clone());
29        }
30
31        let id = last_block.chain_id().parse().unwrap();
32        let height = last_block.height().try_into().unwrap();
33        let last_block_hash = last_block.header.map(|h| h.generate().unwrap().hash());
34        let last_block_id = last_block_hash.map(|hash| block::Id {
35            hash,
36            part_set_header: Default::default(),
37        });
38
39        let info = Info {
40            id,
41            height,
42            last_block_id,
43            // TODO: Not sure yet what this time means
44            time: None,
45        };
46
47        Self::new(info, light_blocks)
48    }
49
50    /// expects at least one LightBlock in the Chain
51    pub fn advance_chain(&mut self) -> &LightBlock {
52        let last_light_block = self
53            .light_blocks
54            .last()
55            .expect("Cannot find testgen light block");
56
57        let new_light_block = last_light_block.next();
58
59        self.info.height = Height::try_from(new_light_block.height())
60            .expect("failed to convert from u64 to Height");
61
62        let last_block_id_hash = new_light_block
63            .header
64            .as_ref()
65            .expect("missing header in new light block")
66            .generate()
67            .expect("failed to generate header")
68            .hash();
69
70        self.info.last_block_id = Some(block::Id {
71            hash: last_block_id_hash,
72            part_set_header: Default::default(),
73        });
74
75        self.light_blocks.push(new_light_block);
76        self.light_blocks.last().unwrap() // safe because of push above
77    }
78
79    /// fetches a block from LightChain at a certain height
80    /// it returns None if a block does not exist for the target_height
81    pub fn block(&self, target_height: u64) -> Option<&LightBlock> {
82        self.light_blocks
83            .iter()
84            .find(|lb| lb.height() == target_height)
85    }
86
87    /// fetches a mutable block from LightChain at a certain height
88    /// it returns None if a block does not exist for the target_height
89    pub fn block_mut(&mut self, target_height: u64) -> Option<&mut LightBlock> {
90        self.light_blocks
91            .iter_mut()
92            .find(|lb| lb.height() == target_height)
93    }
94
95    /// fetches the latest block from LightChain
96    pub fn latest_block(&self) -> &LightBlock {
97        self.light_blocks
98            .last()
99            .expect("cannot find last light block")
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_advance_chain() {
109        let mut light_chain = LightChain::default_with_length(1);
110        let advance_1 = light_chain.advance_chain();
111
112        assert_eq!(2, advance_1.height());
113        assert_eq!(2, light_chain.info.height.value());
114
115        let advance_2 = light_chain.advance_chain();
116
117        assert_eq!(3, advance_2.height());
118        assert_eq!(3, light_chain.info.height.value());
119    }
120
121    #[test]
122    fn test_block() {
123        let mut light_chain = LightChain::default_with_length(1);
124        let first_block = light_chain.block(1);
125        assert_eq!(1, first_block.unwrap().height());
126
127        light_chain.advance_chain();
128        let second_block = light_chain.block(2);
129        assert_eq!(2, second_block.unwrap().height());
130    }
131
132    #[test]
133    fn test_latest_block() {
134        let mut light_chain = LightChain::default_with_length(1);
135        let first_block = light_chain.latest_block();
136        assert_eq!(1, first_block.height());
137
138        light_chain.advance_chain();
139        let second_block = light_chain.latest_block();
140        assert_eq!(2, second_block.height());
141    }
142
143    #[test]
144    fn test_light_chain_with_length() {
145        const CHAIN_HEIGHT: u64 = 10;
146
147        let chain = LightChain::default_with_length(CHAIN_HEIGHT);
148
149        let blocks = chain
150            .light_blocks
151            .into_iter()
152            .flat_map(|lb| lb.generate())
153            .collect::<Vec<_>>();
154
155        // we have as many blocks as the height of the chain
156        assert_eq!(blocks.len(), chain.info.height.value() as usize);
157        assert_eq!(blocks.len(), CHAIN_HEIGHT as usize);
158
159        let first_block = blocks.first().unwrap();
160        let last_block = blocks.last().unwrap();
161
162        // the first block is at height 1
163        assert_eq!(first_block.signed_header.header.height.value(), 1);
164
165        // the first block does not have a last_block_id
166        assert!(first_block.signed_header.header.last_block_id.is_none());
167
168        // the last block is at the chain height
169        assert_eq!(last_block.signed_header.header.height, chain.info.height);
170
171        for i in 1..blocks.len() {
172            let prv = &blocks[i - 1];
173            let cur = &blocks[i];
174
175            // the height of the current block is the successor of the previous block
176            assert_eq!(
177                cur.signed_header.header.height.value(),
178                prv.signed_header.header.height.value() + 1
179            );
180
181            // the last_block_id hash is equal to the previous block's hash
182            assert_eq!(
183                cur.signed_header.header.last_block_id.map(|lbi| lbi.hash),
184                Some(prv.signed_header.header.hash())
185            );
186        }
187    }
188}