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}