1use crate::tx_verifier::OutputsDataVerifier;
2use ckb_chain_spec::consensus::{ConsensusBuilder, TYPE_ID_CODE_HASH};
3use ckb_error::Error as CKBError;
4use ckb_mock_tx_types::{MockCellDep, MockInfo, MockInput, MockTransaction, ReprMockTransaction};
5#[cfg(feature = "native-simulator")]
6use ckb_script::ScriptError;
7use ckb_script::{TransactionScriptsVerifier, TxVerifyEnv};
8use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider};
9use ckb_types::{
10 bytes::Bytes,
11 core::{
12 Capacity, Cycle, DepType, EpochExt, HeaderBuilder, HeaderView, ScriptHashType,
13 TransactionInfo, TransactionView,
14 cell::{CellMetaBuilder, ResolvedTransaction},
15 hardfork::{CKB2021, CKB2023, HardForks},
16 },
17 packed::{Byte32, CellDep, CellDepBuilder, CellOutput, OutPoint, OutPointVec, Script},
18 prelude::*,
19};
20use rand::{Rng, SeedableRng, rngs::StdRng, thread_rng};
21use std::collections::HashMap;
22use std::env;
23use std::path::PathBuf;
24use std::sync::{Arc, Mutex};
25
26pub fn random_hash() -> Byte32 {
28 let mut rng = thread_rng();
29 let mut buf = [0u8; 32];
30 rng.fill(&mut buf);
31 buf.pack()
32}
33
34pub fn random_out_point() -> OutPoint {
36 OutPoint::new_builder().tx_hash(random_hash()).build()
37}
38
39pub fn random_type_id_script() -> Script {
41 let args = random_hash().as_bytes();
42 debug_assert_eq!(args.len(), 32);
43 Script::new_builder()
44 .code_hash(TYPE_ID_CODE_HASH.pack())
45 .hash_type(ScriptHashType::Type)
46 .args(args.pack())
47 .build()
48}
49
50#[derive(Debug, Clone, Eq, PartialEq)]
52pub struct Message {
53 pub id: Byte32,
55 pub message: String,
57}
58
59#[derive(Clone)]
61pub struct Context {
62 pub cells: HashMap<OutPoint, (CellOutput, Bytes)>,
63 pub transaction_infos: HashMap<OutPoint, TransactionInfo>,
64 pub headers: HashMap<Byte32, HeaderView>,
65 pub epoches: HashMap<Byte32, EpochExt>,
66 pub block_extensions: HashMap<Byte32, Bytes>,
67 pub cells_by_data_hash: HashMap<Byte32, OutPoint>,
68 pub cells_by_type_hash: HashMap<Byte32, OutPoint>,
69 deterministic_rng: bool,
70 capture_debug: bool,
71 captured_messages: Arc<Mutex<Vec<Message>>>,
72 contracts_dirs: Vec<PathBuf>,
73 #[cfg(feature = "native-simulator")]
74 simulator_binaries: HashMap<Byte32, PathBuf>,
75 #[cfg(feature = "native-simulator")]
76 simulator_bin_name: String,
77}
78
79impl Default for Context {
80 fn default() -> Self {
81 let mut contracts_dir = env::var("TOP").map(PathBuf::from).unwrap_or_default();
84 contracts_dir.push("build");
85 if !contracts_dir.exists() {
86 contracts_dir.pop();
87 contracts_dir.push("../build");
88 }
89 let contracts_dir = contracts_dir.join(env::var("MODE").unwrap_or("release".to_string()));
90
91 Self {
92 cells: Default::default(),
93 transaction_infos: Default::default(),
94 headers: Default::default(),
95 epoches: Default::default(),
96 block_extensions: Default::default(),
97 cells_by_data_hash: Default::default(),
98 cells_by_type_hash: Default::default(),
99 deterministic_rng: false,
100 capture_debug: Default::default(),
101 captured_messages: Default::default(),
102 contracts_dirs: vec![contracts_dir],
103 #[cfg(feature = "native-simulator")]
104 simulator_binaries: Default::default(),
105 #[cfg(feature = "native-simulator")]
106 simulator_bin_name: "lib<contract>_sim".to_string(),
107 }
108 }
109}
110
111impl Context {
112 pub fn new_with_deterministic_rng() -> Self {
115 Self {
116 deterministic_rng: true,
117 ..Default::default()
118 }
119 }
120
121 pub fn add_contract_dir(&mut self, path: &str) {
123 self.contracts_dirs.push(path.into());
124 }
125
126 pub fn deploy_cell(&mut self, data: Bytes) -> OutPoint {
128 let data_hash = CellOutput::calc_data_hash(&data);
129 if let Some(out_point) = self.cells_by_data_hash.get(&data_hash) {
130 return out_point.to_owned();
132 }
133 let (out_point, type_id_script) = if self.deterministic_rng {
134 let mut rng = StdRng::from_seed(data_hash.as_slice().try_into().unwrap());
135 let mut tx_hash = [0u8; 32];
136 rng.fill(&mut tx_hash);
137 let mut script_args = [0u8; 32];
138 rng.fill(&mut script_args);
139 (
140 OutPoint::new_builder().tx_hash(tx_hash.pack()).build(),
141 Script::new_builder()
142 .code_hash(TYPE_ID_CODE_HASH.pack())
143 .hash_type(ScriptHashType::Type)
144 .args(script_args.as_slice().pack())
145 .build(),
146 )
147 } else {
148 (random_out_point(), random_type_id_script())
149 };
150 let type_id_hash = type_id_script.calc_script_hash();
151 let cell = {
152 let cell = CellOutput::new_builder()
153 .type_(Some(type_id_script).pack())
154 .build();
155 let occupied_capacity = cell
156 .occupied_capacity(Capacity::bytes(data.len()).expect("data occupied capacity"))
157 .expect("cell capacity");
158 cell.as_builder().capacity(occupied_capacity.pack()).build()
159 };
160 self.cells.insert(out_point.clone(), (cell, data));
161 self.cells_by_data_hash.insert(data_hash, out_point.clone());
162 self.cells_by_type_hash
163 .insert(type_id_hash, out_point.clone());
164 out_point
165 }
166
167 pub fn deploy_cell_by_name(&mut self, filename: &str) -> OutPoint {
170 let path = self.get_contract_path(filename).expect("get contract path");
171 let data = std::fs::read(&path).unwrap_or_else(|_| panic!("read local file: {:?}", path));
172
173 #[cfg(feature = "native-simulator")]
174 {
175 let native_path = self.get_native_simulator_path(filename);
176 if native_path.is_some() {
177 let code_hash = CellOutput::calc_data_hash(&data);
178 self.simulator_binaries
179 .insert(code_hash, native_path.unwrap());
180 }
181 }
182
183 self.deploy_cell(data.into())
184 }
185
186 fn get_contract_path(&self, filename: &str) -> Option<PathBuf> {
188 for dir in &self.contracts_dirs {
189 let path = dir.join(filename);
190 if path.is_file() {
191 return Some(path);
192 }
193 }
194 None
195 }
196
197 #[cfg(feature = "native-simulator")]
198 fn get_native_simulator_path(&self, filename: &str) -> Option<PathBuf> {
200 let cdylib_name = format!(
201 "{}.{}",
202 self.simulator_bin_name
203 .replace("<contract>", &filename.replace("-", "_")),
204 std::env::consts::DLL_EXTENSION
205 );
206 for dir in &self.contracts_dirs {
207 let path = dir.join(&cdylib_name);
208 if path.is_file() {
209 return Some(path);
210 }
211 }
212 None
213 }
214
215 pub fn insert_header(&mut self, header: HeaderView) {
217 self.headers.insert(header.hash(), header);
218 }
219
220 pub fn link_cell_with_block(
222 &mut self,
223 out_point: OutPoint,
224 block_hash: Byte32,
225 tx_index: usize,
226 ) {
227 let header = self
228 .headers
229 .get(&block_hash)
230 .expect("can't find the header");
231 self.transaction_infos.insert(
232 out_point,
233 TransactionInfo::new(header.number(), header.epoch(), block_hash, tx_index),
234 );
235 }
236
237 pub fn get_cell_by_data_hash(&self, data_hash: &Byte32) -> Option<OutPoint> {
239 self.cells_by_data_hash.get(data_hash).cloned()
240 }
241
242 pub fn create_cell(&mut self, cell: CellOutput, data: Bytes) -> OutPoint {
244 let out_point = random_out_point();
245 self.create_cell_with_out_point(out_point.clone(), cell, data);
246 out_point
247 }
248
249 pub fn create_cell_with_out_point(
251 &mut self,
252 out_point: OutPoint,
253 cell: CellOutput,
254 data: Bytes,
255 ) {
256 let data_hash = CellOutput::calc_data_hash(&data);
257 self.cells_by_data_hash.insert(data_hash, out_point.clone());
258 if let Some(_type) = cell.type_().to_opt() {
259 let type_hash = _type.calc_script_hash();
260 self.cells_by_type_hash.insert(type_hash, out_point.clone());
261 }
262 self.cells.insert(out_point, (cell, data));
263 }
264
265 pub fn get_cell(&self, out_point: &OutPoint) -> Option<(CellOutput, Bytes)> {
267 self.cells.get(out_point).cloned()
268 }
269
270 pub fn build_script_with_hash_type(
272 &mut self,
273 out_point: &OutPoint,
274 hash_type: ScriptHashType,
275 args: Bytes,
276 ) -> Option<Script> {
277 let (cell, contract_data) = self.cells.get(out_point)?;
278 let code_hash = match hash_type {
279 ScriptHashType::Data | ScriptHashType::Data1 | ScriptHashType::Data2 => {
280 CellOutput::calc_data_hash(contract_data)
281 }
282 ScriptHashType::Type => cell
283 .type_()
284 .to_opt()
285 .expect("get cell's type hash")
286 .calc_script_hash(),
287 _ => unreachable!(),
288 };
289 Some(
290 Script::new_builder()
291 .code_hash(code_hash)
292 .hash_type(hash_type)
293 .args(args.pack())
294 .build(),
295 )
296 }
297
298 pub fn build_script(&mut self, out_point: &OutPoint, args: Bytes) -> Option<Script> {
301 self.build_script_with_hash_type(out_point, ScriptHashType::Type, args)
302 }
303
304 fn find_cell_dep_for_script(&self, script: &Script) -> CellDep {
306 let out_point = match ScriptHashType::try_from(u8::from(script.hash_type()))
307 .expect("invalid script hash type")
308 {
309 ScriptHashType::Data | ScriptHashType::Data1 | ScriptHashType::Data2 => self
310 .get_cell_by_data_hash(&script.code_hash())
311 .expect("find contract out point by data_hash"),
312 ScriptHashType::Type => self
313 .cells_by_type_hash
314 .get(&script.code_hash())
315 .cloned()
316 .expect("find contract out point by type_hash"),
317 _ => unreachable!(),
318 };
319
320 CellDep::new_builder()
321 .out_point(out_point)
322 .dep_type(DepType::Code)
323 .build()
324 }
325
326 pub fn complete_tx(&mut self, tx: TransactionView) -> TransactionView {
329 let mut cell_deps: Vec<CellDep> = Vec::new();
330
331 for cell_dep in tx.cell_deps_iter() {
332 cell_deps.push(cell_dep);
333 }
334
335 for i in tx.input_pts_iter() {
336 if let Some((cell, _data)) = self.cells.get(&i) {
337 let dep = self.find_cell_dep_for_script(&cell.lock());
338 if !cell_deps.contains(&dep) {
339 cell_deps.push(dep);
340 }
341 if let Some(script) = cell.type_().to_opt() {
342 let dep = self.find_cell_dep_for_script(&script);
343 if !cell_deps.contains(&dep) {
344 cell_deps.push(dep);
345 }
346 }
347 }
348 }
349
350 for (cell, _data) in tx.outputs_with_data_iter() {
351 if let Some(script) = cell.type_().to_opt() {
352 let dep = self.find_cell_dep_for_script(&script);
353 if !cell_deps.contains(&dep) {
354 cell_deps.push(dep);
355 }
356 }
357 }
358
359 tx.as_advanced_builder()
360 .set_cell_deps(Vec::new())
361 .cell_deps(cell_deps.pack())
362 .build()
363 }
364
365 fn build_resolved_tx(&self, tx: &TransactionView) -> ResolvedTransaction {
367 let input_cells = tx
368 .inputs()
369 .into_iter()
370 .map(|input| {
371 let previous_out_point = input.previous_output();
372 let (input_output, input_data) = self.cells.get(&previous_out_point).unwrap();
373 let tx_info_opt = self.transaction_infos.get(&previous_out_point);
374 let mut b = CellMetaBuilder::from_cell_output(
375 input_output.to_owned(),
376 input_data.to_vec().into(),
377 )
378 .out_point(previous_out_point);
379 if let Some(tx_info) = tx_info_opt {
380 b = b.transaction_info(tx_info.to_owned());
381 }
382 b.build()
383 })
384 .collect();
385 let mut resolved_cell_deps = vec![];
386 let mut resolved_dep_groups = vec![];
387 tx.cell_deps().into_iter().for_each(|cell_dep| {
388 let mut out_points = vec![];
389 if cell_dep.dep_type() == DepType::DepGroup.into() {
390 let (dep_group_output, dep_group_data) =
391 self.cells.get(&cell_dep.out_point()).unwrap();
392 let dep_group_tx_info_opt = self.transaction_infos.get(&cell_dep.out_point());
393 let mut b = CellMetaBuilder::from_cell_output(
394 dep_group_output.to_owned(),
395 dep_group_data.to_vec().into(),
396 )
397 .out_point(cell_dep.out_point());
398 if let Some(tx_info) = dep_group_tx_info_opt {
399 b = b.transaction_info(tx_info.to_owned());
400 }
401 resolved_dep_groups.push(b.build());
402
403 let sub_out_points =
404 OutPointVec::from_slice(dep_group_data).expect("Parsing dep group error!");
405 out_points.extend(sub_out_points);
406 } else {
407 out_points.push(cell_dep.out_point());
408 }
409
410 for out_point in out_points {
411 let (dep_output, dep_data) = self.cells.get(&out_point).unwrap();
412 let tx_info_opt = self.transaction_infos.get(&out_point);
413 let mut b = CellMetaBuilder::from_cell_output(
414 dep_output.to_owned(),
415 dep_data.to_vec().into(),
416 )
417 .out_point(out_point);
418 if let Some(tx_info) = tx_info_opt {
419 b = b.transaction_info(tx_info.to_owned());
420 }
421 resolved_cell_deps.push(b.build());
422 }
423 });
424 ResolvedTransaction {
425 transaction: tx.clone(),
426 resolved_cell_deps,
427 resolved_inputs: input_cells,
428 resolved_dep_groups,
429 }
430 }
431
432 fn verify_tx_consensus(&self, tx: &TransactionView) -> Result<(), CKBError> {
434 OutputsDataVerifier::new(tx).verify()?;
435 Ok(())
436 }
437
438 pub fn capture_debug(&self) -> bool {
440 self.capture_debug
441 }
442
443 pub fn set_capture_debug(&mut self, capture_debug: bool) {
445 self.capture_debug = capture_debug;
446 }
447
448 pub fn captured_messages(&self) -> Vec<Message> {
450 self.captured_messages.lock().unwrap().clone()
451 }
452
453 pub fn verify_tx(&self, tx: &TransactionView, max_cycles: u64) -> Result<Cycle, CKBError> {
455 self.verify_tx_consensus(tx)?;
456 let resolved_tx = self.build_resolved_tx(tx);
457 let consensus = ConsensusBuilder::default()
458 .hardfork_switch(HardForks {
459 ckb2021: CKB2021::new_dev_default(),
460 ckb2023: CKB2023::new_dev_default(),
461 })
462 .build();
463 let tip = HeaderBuilder::default().number(0).build();
464 let tx_verify_env = TxVerifyEnv::new_submit(&tip);
465 let verifier = if self.capture_debug {
466 let captured_messages = self.captured_messages.clone();
467 TransactionScriptsVerifier::new_with_debug_printer(
468 Arc::new(resolved_tx),
469 self.clone(),
470 Arc::new(consensus),
471 Arc::new(tx_verify_env),
472 Arc::new(move |id, message| {
473 let msg = Message {
475 id: id.clone(),
476 message: message.to_string(),
477 };
478 captured_messages.lock().unwrap().push(msg);
479 }),
480 )
481 } else {
482 TransactionScriptsVerifier::new_with_debug_printer(
483 Arc::new(resolved_tx),
484 self.clone(),
485 Arc::new(consensus),
486 Arc::new(tx_verify_env),
487 Arc::new(|_id, msg| {
488 println!("[contract debug] {}", msg);
489 }),
490 )
491 };
492
493 #[cfg(feature = "native-simulator")]
494 {
495 self.native_simulator_verify(tx, verifier, max_cycles)
496 }
497 #[cfg(not(feature = "native-simulator"))]
498 verifier.verify(max_cycles)
499 }
500
501 #[cfg(feature = "native-simulator")]
502 fn native_simulator_verify<DL>(
504 &self,
505 tx: &TransactionView,
506 verifier: TransactionScriptsVerifier<DL>,
507 max_cycles: u64,
508 ) -> Result<Cycle, CKBError>
509 where
510 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
511 {
512 let mut cycles: Cycle = 0;
513
514 for (hash, group) in verifier.groups() {
515 let code_hash = if group.script.hash_type() == ScriptHashType::Type.into() {
516 let code_hash = group.script.code_hash();
517 let out_point = match self.cells_by_type_hash.get(&code_hash) {
518 Some(out_point) => out_point,
519 None => panic!("unknow code hash(ScriptHashType::Type)"),
520 };
521
522 match self.cells.get(out_point) {
523 Some((_cell, bin)) => CellOutput::calc_data_hash(bin),
524 None => panic!("unknow code hash(ScriptHashType::Type) in deps"),
525 }
526 } else {
527 group.script.code_hash()
528 };
529
530 let use_cycles = match self.simulator_binaries.get(&code_hash) {
531 Some(sim_path) => self
532 .run_simulator(sim_path, tx, group)
533 .map_err(|e| e.source(group))?,
534 None => {
535 group.script.code_hash();
536 verifier
537 .verify_single(group.group_type, hash, max_cycles)
538 .map_err(|e| e.source(group))?
539 }
540 };
541 let r = cycles.overflowing_add(use_cycles);
542 assert!(!r.1, "cycles overflow");
543 cycles = r.0;
544 }
545 Ok(cycles)
546 }
547
548 #[cfg(feature = "native-simulator")]
549 fn run_simulator(
551 &self,
552 sim_path: &PathBuf,
553 tx: &TransactionView,
554 group: &ckb_script::ScriptGroup,
555 ) -> Result<u64, ScriptError> {
556 println!(
557 "run native-simulator: {}",
558 sim_path.file_name().unwrap().to_str().unwrap()
559 );
560 let tmp_dir = if !self.simulator_binaries.is_empty() {
561 let tmp_dir = std::env::temp_dir().join("ckb-simulator-debugger");
562 if !tmp_dir.exists() {
563 std::fs::create_dir(tmp_dir.clone())
564 .expect("create tmp dir: ckb-simulator-debugger");
565 }
566 let tx_file: PathBuf = tmp_dir.join("ckb_running_tx.json");
567 let dump_tx = self.dump_tx(&tx).unwrap();
568
569 let tx_json = serde_json::to_string(&dump_tx).expect("dump tx to string");
570 std::fs::write(&tx_file, tx_json).expect("write setup");
571
572 unsafe {
573 std::env::set_var("CKB_TX_FILE", tx_file.to_str().unwrap());
574 }
575 Some(tmp_dir)
576 } else {
577 None
578 };
579 let running_setup = tmp_dir.as_ref().unwrap().join("ckb_running_setup.json");
580
581 let mut native_binaries = self
582 .simulator_binaries
583 .iter()
584 .map(|(code_hash, path)| {
585 let buf = vec![
586 code_hash.as_bytes().to_vec(),
587 vec![0xff],
588 0u32.to_le_bytes().to_vec(),
589 0u32.to_le_bytes().to_vec(),
590 ]
591 .concat();
592
593 format!(
594 "\"0x{}\" : \"{}\",",
595 faster_hex::hex_string(&buf),
596 path.to_str().unwrap()
597 )
598 })
599 .collect::<Vec<String>>()
600 .concat();
601 if !native_binaries.is_empty() {
602 native_binaries.pop();
603 }
604
605 let native_binaries = format!("{{ {} }}", native_binaries);
606
607 let (is_output, script_index) = if !group.input_indices.is_empty() {
610 (false, group.input_indices[0])
611 } else if !group.output_indices.is_empty() {
612 (true, group.output_indices[0])
613 } else {
614 panic!("script group has no inputs or outputs");
615 };
616
617 let setup = format!(
618 "{{\"is_lock_script\": {}, \"is_output\": {}, \"script_index\": {}, \"vm_version\": {}, \"native_binaries\": {}, \"run_type\": \"DynamicLib\" }}",
619 group.group_type == ckb_script::ScriptGroupType::Lock,
620 is_output,
621 script_index,
622 2,
623 native_binaries
624 );
625 std::fs::write(&running_setup, setup).expect("write setup");
626 unsafe {
627 std::env::set_var("CKB_RUNNING_SETUP", running_setup.to_str().unwrap());
628 }
629
630 type CkbMainFunc<'a> =
631 libloading::Symbol<'a, unsafe extern "C" fn(argc: i32, argv: *const *const i8) -> i8>;
632 type SetScriptInfo<'a> = libloading::Symbol<
633 'a,
634 unsafe extern "C" fn(ptr: *const std::ffi::c_void, tx_ctx_id: u64, vm_ctx_id: u64),
635 >;
636
637 unsafe {
639 let lib = libloading::Library::new(sim_path).expect("Load library");
640
641 let func: SetScriptInfo = lib
642 .get(b"__set_script_info")
643 .expect("load function : __update_spawn_info");
644 func(std::ptr::null(), 0, 0);
645
646 let func: CkbMainFunc = lib
647 .get(b"__ckb_std_main")
648 .expect("load function : __ckb_std_main");
649 let argv = vec![];
650 let result = func(0, argv.as_ptr());
651 if result != 0 {
652 return Err(ScriptError::validation_failure(&group.script, result));
653 }
654 }
655 Ok(0)
656 }
657
658 #[cfg(feature = "native-simulator")]
659 pub fn set_simulator(&mut self, code_hash: Byte32, path: &str) {
661 let path = PathBuf::from(path);
662 assert!(path.is_file());
663 self.simulator_binaries.insert(code_hash, path);
664 }
665
666 pub fn dump_tx(&self, tx: &TransactionView) -> Result<ReprMockTransaction, CKBError> {
668 let rtx = self.build_resolved_tx(tx);
669 let mut inputs = Vec::with_capacity(rtx.resolved_inputs.len());
670 for (i, input) in rtx.resolved_inputs.iter().enumerate() {
672 inputs.push(MockInput {
673 input: rtx.transaction.inputs().get(i).unwrap(),
674 output: input.cell_output.clone(),
675 data: input.mem_cell_data.clone().unwrap(),
676 header: input.transaction_info.clone().map(|info| info.block_hash),
677 });
678 }
679 let mut cell_deps =
682 Vec::with_capacity(rtx.resolved_cell_deps.len() + rtx.resolved_dep_groups.len());
683 for dep in rtx.resolved_cell_deps.iter() {
684 cell_deps.push(MockCellDep {
685 cell_dep: CellDepBuilder::default()
686 .out_point(dep.out_point.clone())
687 .dep_type(DepType::Code)
688 .build(),
689 output: dep.cell_output.clone(),
690 data: dep.mem_cell_data.clone().unwrap(),
691 header: dep.transaction_info.clone().map(|info| info.block_hash),
692 });
693 }
694 for dep in rtx.resolved_dep_groups.iter() {
695 cell_deps.push(MockCellDep {
696 cell_dep: CellDepBuilder::default()
697 .out_point(dep.out_point.clone())
698 .dep_type(DepType::DepGroup)
699 .build(),
700 output: dep.cell_output.clone(),
701 data: dep.mem_cell_data.clone().unwrap(),
702 header: dep.transaction_info.clone().map(|info| info.block_hash),
703 });
704 }
705 let mut header_deps = Vec::with_capacity(rtx.transaction.header_deps().len());
706 let mut extensions = Vec::new();
707 for header_hash in rtx.transaction.header_deps_iter() {
708 header_deps.push(self.get_header(&header_hash).unwrap());
709 if let Some(extension) = self.get_block_extension(&header_hash) {
710 extensions.push((header_hash, extension.unpack()));
711 }
712 }
713 Ok(MockTransaction {
714 mock_info: MockInfo {
715 inputs,
716 cell_deps,
717 header_deps,
718 extensions,
719 },
720 tx: rtx.transaction.data(),
721 }
722 .into())
723 }
724}
725
726impl CellDataProvider for Context {
727 fn get_cell_data(&self, out_point: &OutPoint) -> Option<Bytes> {
728 self.cells
729 .get(out_point)
730 .map(|(_, data)| Bytes::from(data.to_vec()))
731 }
732
733 fn get_cell_data_hash(&self, out_point: &OutPoint) -> Option<Byte32> {
734 self.cells
735 .get(out_point)
736 .map(|(_, data)| CellOutput::calc_data_hash(data))
737 }
738}
739
740impl HeaderProvider for Context {
741 fn get_header(&self, block_hash: &Byte32) -> Option<HeaderView> {
742 self.headers.get(block_hash).cloned()
743 }
744}
745
746impl ExtensionProvider for Context {
747 fn get_block_extension(
748 &self,
749 hash: &ckb_types::packed::Byte32,
750 ) -> Option<ckb_types::packed::Bytes> {
751 self.block_extensions.get(hash).map(|b| b.pack())
752 }
753}