Skip to main content

ckb_test_chain_utils/
mock_chain.rs

1#![allow(dead_code)]
2#![allow(missing_docs)]
3
4use crate::MockStore;
5use crate::mock_utils::{create_cellbase, dao_data};
6use ckb_chain_spec::consensus::Consensus;
7use ckb_store::ChainStore;
8use ckb_types::core::{BlockBuilder, BlockView, HeaderView, TransactionView};
9use ckb_types::utilities::difficulty_to_compact;
10use ckb_types::{U256, packed};
11
12#[derive(Clone)]
13pub struct MockChain<'a> {
14    blocks: Vec<BlockView>,
15    parent: HeaderView,
16    consensus: &'a Consensus,
17}
18
19impl<'a> MockChain<'a> {
20    pub fn new(parent: HeaderView, consensus: &'a Consensus) -> Self {
21        Self {
22            blocks: vec![],
23            parent,
24            consensus,
25        }
26    }
27
28    fn commit_block(&mut self, store: &MockStore, block: BlockView) {
29        store.insert_block(&block, self.consensus.genesis_epoch_ext());
30        self.blocks.push(block);
31    }
32
33    pub fn rollback(&mut self, store: &MockStore) {
34        if let Some(block) = self.blocks.pop() {
35            store.remove_block(&block);
36        }
37    }
38
39    pub fn gen_block_with_proposal_txs(&mut self, txs: Vec<TransactionView>, store: &MockStore) {
40        let parent = self.tip_header();
41        let cellbase = create_cellbase(store, self.consensus, &parent);
42        let dao = dao_data(
43            self.consensus,
44            &parent,
45            std::slice::from_ref(&cellbase),
46            store,
47            false,
48        );
49
50        let epoch = self
51            .consensus
52            .next_epoch_ext(&parent, &store.store().borrow_as_data_loader())
53            .unwrap()
54            .epoch();
55
56        let new_block = BlockBuilder::default()
57            .parent_hash(parent.hash())
58            .number(parent.number() + 1)
59            .compact_target(epoch.compact_target())
60            .epoch(epoch.number_with_fraction(parent.number() + 1))
61            .dao(dao)
62            .transaction(cellbase)
63            .proposals(txs.iter().map(TransactionView::proposal_short_id))
64            .build();
65
66        self.commit_block(store, new_block)
67    }
68
69    pub fn gen_block_with_proposal_ids(
70        &mut self,
71        difficulty: u64,
72        ids: Vec<packed::ProposalShortId>,
73        store: &MockStore,
74    ) {
75        let parent = self.tip_header();
76        let cellbase = create_cellbase(store, self.consensus, &parent);
77        let dao = dao_data(
78            self.consensus,
79            &parent,
80            std::slice::from_ref(&cellbase),
81            store,
82            false,
83        );
84
85        let epoch = self
86            .consensus
87            .next_epoch_ext(&parent, &store.store().borrow_as_data_loader())
88            .unwrap()
89            .epoch();
90
91        let new_block = BlockBuilder::default()
92            .parent_hash(parent.hash())
93            .number(parent.number() + 1)
94            .epoch(epoch.number_with_fraction(parent.number() + 1))
95            .compact_target(difficulty_to_compact(U256::from(difficulty)))
96            .dao(dao)
97            .transaction(cellbase)
98            .proposals(ids)
99            .build();
100
101        self.commit_block(store, new_block)
102    }
103
104    pub fn gen_empty_block_with_diff(&mut self, difficulty: u64, store: &MockStore) {
105        let parent = self.tip_header();
106        let cellbase = create_cellbase(store, self.consensus, &parent);
107        let dao = dao_data(
108            self.consensus,
109            &parent,
110            std::slice::from_ref(&cellbase),
111            store,
112            false,
113        );
114
115        let epoch = self
116            .consensus
117            .next_epoch_ext(&parent, &store.store().borrow_as_data_loader())
118            .unwrap()
119            .epoch();
120
121        let new_block = BlockBuilder::default()
122            .parent_hash(parent.hash())
123            .number(parent.number() + 1)
124            .epoch(epoch.number_with_fraction(parent.number() + 1))
125            .compact_target(difficulty_to_compact(U256::from(difficulty)))
126            .dao(dao)
127            .transaction(cellbase)
128            .build();
129
130        self.commit_block(store, new_block)
131    }
132
133    pub fn gen_empty_block_with_inc_diff(&mut self, inc: u64, store: &MockStore) {
134        let difficulty = self.difficulty();
135        let parent = self.tip_header();
136        let cellbase = create_cellbase(store, self.consensus, &parent);
137        let dao = dao_data(
138            self.consensus,
139            &parent,
140            std::slice::from_ref(&cellbase),
141            store,
142            false,
143        );
144
145        let epoch = self
146            .consensus
147            .next_epoch_ext(&parent, &store.store().borrow_as_data_loader())
148            .unwrap()
149            .epoch();
150
151        let new_block = BlockBuilder::default()
152            .parent_hash(parent.hash())
153            .number(parent.number() + 1)
154            .compact_target(difficulty_to_compact(difficulty + U256::from(inc)))
155            .epoch(epoch.number_with_fraction(parent.number() + 1))
156            .dao(dao)
157            .transaction(cellbase)
158            .build();
159
160        self.commit_block(store, new_block)
161    }
162
163    pub fn gen_empty_block_with_nonce(&mut self, nonce: u128, store: &MockStore) {
164        let parent = self.tip_header();
165        let cellbase = create_cellbase(store, self.consensus, &parent);
166        let dao = dao_data(
167            self.consensus,
168            &parent,
169            std::slice::from_ref(&cellbase),
170            store,
171            false,
172        );
173
174        let epoch = self
175            .consensus
176            .next_epoch_ext(&parent, &store.store().borrow_as_data_loader())
177            .unwrap()
178            .epoch();
179
180        let new_block = BlockBuilder::default()
181            .parent_hash(parent.hash())
182            .number(parent.number() + 1)
183            .compact_target(epoch.compact_target())
184            .epoch(epoch.number_with_fraction(parent.number() + 1))
185            .nonce(nonce)
186            .dao(dao)
187            .transaction(cellbase)
188            .build();
189
190        self.commit_block(store, new_block)
191    }
192
193    pub fn gen_empty_block(&mut self, store: &MockStore) {
194        let parent = self.tip_header();
195        let cellbase = create_cellbase(store, self.consensus, &parent);
196        let dao = dao_data(
197            self.consensus,
198            &parent,
199            std::slice::from_ref(&cellbase),
200            store,
201            false,
202        );
203
204        let epoch = self
205            .consensus
206            .next_epoch_ext(&parent, &store.store().borrow_as_data_loader())
207            .unwrap()
208            .epoch();
209
210        let new_block = BlockBuilder::default()
211            .parent_hash(parent.hash())
212            .number(parent.number() + 1)
213            .compact_target(epoch.compact_target())
214            .epoch(epoch.number_with_fraction(parent.number() + 1))
215            .dao(dao)
216            .transaction(cellbase)
217            .build();
218
219        self.commit_block(store, new_block)
220    }
221
222    pub fn gen_block_with_commit_txs(
223        &mut self,
224        txs: Vec<TransactionView>,
225        store: &MockStore,
226        ignore_resolve_error: bool,
227    ) {
228        let parent = self.tip_header();
229        let cellbase = create_cellbase(store, self.consensus, &parent);
230        let mut txs_to_resolve = vec![cellbase.clone()];
231        txs_to_resolve.extend_from_slice(&txs);
232        let dao = dao_data(
233            self.consensus,
234            &parent,
235            &txs_to_resolve,
236            store,
237            ignore_resolve_error,
238        );
239
240        let epoch = self
241            .consensus
242            .next_epoch_ext(&parent, &store.store().borrow_as_data_loader())
243            .unwrap()
244            .epoch();
245
246        let new_block = BlockBuilder::default()
247            .parent_hash(parent.hash())
248            .number(parent.number() + 1)
249            .compact_target(epoch.compact_target())
250            .epoch(epoch.number_with_fraction(parent.number() + 1))
251            .dao(dao)
252            .transaction(cellbase)
253            .transactions(txs)
254            .build();
255
256        self.commit_block(store, new_block)
257    }
258
259    pub fn tip_header(&self) -> HeaderView {
260        self.blocks
261            .last()
262            .map_or(self.parent.clone(), |b| b.header())
263    }
264
265    pub fn tip(&self) -> &BlockView {
266        self.blocks.last().expect("should have tip")
267    }
268
269    pub fn difficulty(&self) -> U256 {
270        self.tip_header().difficulty()
271    }
272
273    pub fn blocks(&self) -> &Vec<BlockView> {
274        &self.blocks
275    }
276
277    pub fn total_difficulty(&self) -> U256 {
278        self.blocks()
279            .iter()
280            .fold(U256::from(0u64), |sum, b| sum + b.header().difficulty())
281    }
282}