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