1use ckb_jsonrpc_types as json_types;
2use ckb_traits::{CellDataProvider, HeaderProvider};
3use ckb_types::{
4 bytes::Bytes,
5 core::{
6 cell::{CellMeta, CellMetaBuilder, CellProvider, CellStatus, HeaderChecker},
7 error::OutPointError,
8 DepType, EpochNumberWithFraction, HeaderView, TransactionInfo, TransactionView,
9 },
10 packed::{Byte32, CellDep, CellInput, CellOutput, OutPoint, OutPointVec, Transaction},
11 prelude::*,
12 H256,
13};
14use serde_derive::{Deserialize, Serialize};
15use std::collections::HashMap;
16
17#[derive(Clone, Default)]
18pub struct MockCellDep {
19 pub cell_dep: CellDep,
20 pub output: CellOutput,
21 pub data: Bytes,
22 pub block_hash: H256,
24}
25
26#[derive(Clone, Default)]
27pub struct MockInput {
28 pub input: CellInput,
29 pub output: CellOutput,
30 pub data: Bytes,
31 pub block_hash: H256,
33}
34
35#[derive(Clone, Default)]
36pub struct MockInfo {
37 pub inputs: Vec<MockInput>,
38 pub cell_deps: Vec<MockCellDep>,
39 pub header_deps: Vec<HeaderView>,
40}
41
42#[derive(Clone, Default)]
44pub struct MockTransaction {
45 pub mock_info: MockInfo,
46 pub tx: Transaction,
47}
48
49impl MockTransaction {
50 pub fn get_input_cell<
51 F: FnMut(OutPoint) -> Result<Option<(CellOutput, Bytes, H256)>, String>,
52 >(
53 &self,
54 input: &CellInput,
55 mut live_cell_getter: F,
56 ) -> Result<Option<(CellOutput, Bytes, H256)>, String> {
57 for mock_input in &self.mock_info.inputs {
58 if input == &mock_input.input {
59 return Ok(Some((
60 mock_input.output.clone(),
61 mock_input.data.clone(),
62 mock_input.block_hash.clone(),
63 )));
64 }
65 }
66 live_cell_getter(input.previous_output())
67 }
68
69 pub fn get_dep_cell<F: FnMut(OutPoint) -> Result<Option<(CellOutput, Bytes, H256)>, String>>(
70 &self,
71 out_point: &OutPoint,
72 mut live_cell_getter: F,
73 ) -> Result<Option<(CellOutput, Bytes, H256)>, String> {
74 for mock_cell in &self.mock_info.cell_deps {
75 if out_point == &mock_cell.cell_dep.out_point() {
76 return Ok(Some((
77 mock_cell.output.clone(),
78 mock_cell.data.clone(),
79 mock_cell.block_hash.clone(),
80 )));
81 }
82 }
83 live_cell_getter(out_point.clone())
84 }
85
86 pub fn get_header<F: FnMut(H256) -> Result<Option<HeaderView>, String>>(
87 &self,
88 block_hash: &H256,
89 mut header_getter: F,
90 ) -> Result<Option<HeaderView>, String> {
91 for mock_header in &self.mock_info.header_deps {
92 if block_hash == &mock_header.hash().unpack() {
93 return Ok(Some(mock_header.clone()));
94 }
95 }
96 header_getter(block_hash.clone())
97 }
98
99 pub fn core_transaction(&self) -> TransactionView {
101 self.tx.clone().into_view()
102 }
103}
104
105pub trait MockResourceLoader {
106 fn get_header(&mut self, hash: H256) -> Result<Option<HeaderView>, String>;
107 fn get_live_cell(
108 &mut self,
109 out_point: OutPoint,
110 ) -> Result<Option<(CellOutput, Bytes, H256)>, String>;
111}
112
113pub struct Resource {
114 required_cells: HashMap<OutPoint, CellMeta>,
115 required_headers: HashMap<Byte32, HeaderView>,
116}
117
118impl Resource {
119 pub fn from_both<L: MockResourceLoader>(
120 mock_tx: &MockTransaction,
121 mut loader: L,
122 ) -> Result<Resource, String> {
123 let tx = mock_tx.core_transaction();
124 let mut required_cells = HashMap::default();
125 let mut required_headers = HashMap::default();
126
127 for input in tx.inputs().into_iter() {
128 let (output, data, block_hash) = mock_tx
129 .get_input_cell(&input, |out_point| loader.get_live_cell(out_point))?
130 .ok_or_else(|| format!("Can not get CellOutput by input={}", input))?;
131 let cell_meta = CellMetaBuilder::from_cell_output(output, data)
132 .out_point(input.previous_output())
133 .transaction_info(TransactionInfo::new(
134 1,
135 EpochNumberWithFraction::new(1, 1, 1),
136 block_hash.pack(),
137 1,
138 ))
139 .build();
140 required_cells.insert(input.previous_output(), cell_meta);
141 }
142
143 for cell_dep in tx.cell_deps().into_iter() {
144 let (output, data, block_hash) = mock_tx
145 .get_dep_cell(&cell_dep.out_point(), |out_point| {
146 loader.get_live_cell(out_point)
147 })?
148 .ok_or_else(|| format!("Can not get CellOutput by dep={}", cell_dep))?;
149 if cell_dep.dep_type() == DepType::DepGroup.into() {
151 for sub_out_point in OutPointVec::from_slice(&data)
152 .map_err(|err| format!("Parse dep group data error: {}", err))?
153 .into_iter()
154 {
155 let (sub_output, sub_data, block_hash) = mock_tx
156 .get_dep_cell(&sub_out_point, |out_point| loader.get_live_cell(out_point))?
157 .ok_or_else(|| {
158 format!(
159 "(dep group) Can not get CellOutput by out_point={}",
160 sub_out_point
161 )
162 })?;
163
164 let sub_cell_meta = CellMetaBuilder::from_cell_output(sub_output, sub_data)
165 .out_point(sub_out_point.clone())
166 .transaction_info(TransactionInfo::new(
167 1,
168 EpochNumberWithFraction::new(1, 1, 1),
169 block_hash.pack(),
170 1,
171 ))
172 .build();
173 required_cells.insert(sub_out_point, sub_cell_meta);
174 }
175 }
176 let cell_meta = CellMetaBuilder::from_cell_output(output, data)
177 .out_point(cell_dep.out_point())
178 .transaction_info(TransactionInfo::new(
179 1,
180 EpochNumberWithFraction::new(1, 1, 1),
181 block_hash.pack(),
182 1,
183 ))
184 .build();
185 required_cells.insert(cell_dep.out_point(), cell_meta);
186 }
187
188 for block_hash in tx.header_deps().into_iter() {
189 let header = mock_tx
190 .get_header(&block_hash.unpack(), |block_hash| {
191 loader.get_header(block_hash)
192 })?
193 .ok_or_else(|| format!("Can not get header: {:x}", block_hash))?;
194 required_headers.insert(block_hash, header);
195 }
196
197 Ok(Resource {
198 required_cells,
199 required_headers,
200 })
201 }
202}
203
204impl<'a> HeaderChecker for Resource {
205 fn check_valid(
206 &self,
207 block_hash: &Byte32,
208 ) -> Result<(), ckb_types::core::error::OutPointError> {
209 if !self.required_headers.contains_key(block_hash) {
210 return Err(OutPointError::InvalidHeader(block_hash.clone()));
211 }
212 Ok(())
213 }
214}
215
216impl CellProvider for Resource {
217 fn cell(&self, out_point: &OutPoint, _with_data: bool) -> CellStatus {
218 self.required_cells
219 .get(out_point)
220 .cloned()
221 .map(CellStatus::live_cell)
222 .unwrap_or(CellStatus::Unknown)
223 }
224}
225
226impl CellDataProvider for Resource {
227 fn load_cell_data(&self, cell: &CellMeta) -> Option<Bytes> {
228 cell.mem_cell_data
229 .as_ref()
230 .map(ToOwned::to_owned)
231 .or_else(|| self.get_cell_data(&cell.out_point))
232 }
233
234 fn get_cell_data(&self, out_point: &OutPoint) -> Option<Bytes> {
235 self.required_cells
236 .get(out_point)
237 .and_then(|cell_meta| cell_meta.mem_cell_data.clone())
238 }
239
240 fn get_cell_data_hash(&self, out_point: &OutPoint) -> Option<Byte32> {
241 self.required_cells
242 .get(out_point)
243 .and_then(|cell_meta| cell_meta.mem_cell_data_hash.clone())
244 }
245}
246
247impl HeaderProvider for Resource {
248 fn get_header(&self, hash: &Byte32) -> Option<HeaderView> {
249 self.required_headers.get(hash).cloned()
250 }
251}
252
253#[derive(Clone, Serialize, Deserialize)]
254pub struct ReprMockCellDep {
255 pub cell_dep: json_types::CellDep,
256 pub output: json_types::CellOutput,
257 pub data: json_types::JsonBytes,
258 pub block_hash: Option<H256>,
259}
260#[derive(Clone, Serialize, Deserialize)]
261pub struct ReprMockInput {
262 pub input: json_types::CellInput,
263 pub output: json_types::CellOutput,
264 pub data: json_types::JsonBytes,
265 pub block_hash: Option<H256>,
266}
267#[derive(Clone, Serialize, Deserialize)]
268pub struct ReprMockInfo {
269 pub inputs: Vec<ReprMockInput>,
270 pub cell_deps: Vec<ReprMockCellDep>,
271 pub header_deps: Vec<json_types::HeaderView>,
272}
273#[derive(Clone, Serialize, Deserialize)]
274pub struct ReprMockTransaction {
275 pub mock_info: ReprMockInfo,
276 pub tx: json_types::Transaction,
277}
278
279impl From<MockCellDep> for ReprMockCellDep {
280 fn from(dep: MockCellDep) -> ReprMockCellDep {
281 ReprMockCellDep {
282 cell_dep: dep.cell_dep.into(),
283 output: dep.output.into(),
284 data: json_types::JsonBytes::from_bytes(dep.data),
285 block_hash: Some(dep.block_hash),
286 }
287 }
288}
289impl From<ReprMockCellDep> for MockCellDep {
290 fn from(dep: ReprMockCellDep) -> MockCellDep {
291 MockCellDep {
292 cell_dep: dep.cell_dep.into(),
293 output: dep.output.into(),
294 data: dep.data.into_bytes(),
295 block_hash: dep.block_hash.unwrap_or_default(),
296 }
297 }
298}
299
300impl From<MockInput> for ReprMockInput {
301 fn from(input: MockInput) -> ReprMockInput {
302 ReprMockInput {
303 input: input.input.into(),
304 output: input.output.into(),
305 data: json_types::JsonBytes::from_bytes(input.data),
306 block_hash: Some(input.block_hash),
307 }
308 }
309}
310impl From<ReprMockInput> for MockInput {
311 fn from(input: ReprMockInput) -> MockInput {
312 MockInput {
313 input: input.input.into(),
314 output: input.output.into(),
315 data: input.data.into_bytes(),
316 block_hash: input.block_hash.unwrap_or_default(),
317 }
318 }
319}
320
321impl From<MockInfo> for ReprMockInfo {
322 fn from(info: MockInfo) -> ReprMockInfo {
323 ReprMockInfo {
324 inputs: info.inputs.into_iter().map(Into::into).collect(),
325 cell_deps: info.cell_deps.into_iter().map(Into::into).collect(),
326 header_deps: info
327 .header_deps
328 .into_iter()
329 .map(|header| {
330 let hash = header.hash().unpack();
332 let mut json_header: json_types::HeaderView = header.into();
333 json_header.hash = hash;
334 json_header
335 })
336 .collect(),
337 }
338 }
339}
340
341impl From<ReprMockInfo> for MockInfo {
342 fn from(info: ReprMockInfo) -> MockInfo {
343 MockInfo {
344 inputs: info.inputs.into_iter().map(Into::into).collect(),
345 cell_deps: info.cell_deps.into_iter().map(Into::into).collect(),
346 header_deps: info
347 .header_deps
348 .into_iter()
349 .map(|json_header| {
350 let hash = json_header.hash.pack();
352 HeaderView::from(json_header).fake_hash(hash)
353 })
354 .collect(),
355 }
356 }
357}
358
359impl From<MockTransaction> for ReprMockTransaction {
360 fn from(tx: MockTransaction) -> ReprMockTransaction {
361 ReprMockTransaction {
362 mock_info: tx.mock_info.into(),
363 tx: tx.tx.into(),
364 }
365 }
366}
367impl From<ReprMockTransaction> for MockTransaction {
368 fn from(tx: ReprMockTransaction) -> MockTransaction {
369 MockTransaction {
370 mock_info: tx.mock_info.into(),
371 tx: tx.tx.into(),
372 }
373 }
374}