1use ckb_chain_spec::consensus::TYPE_ID_CODE_HASH;
2use ckb_hash::blake2b_256;
3use ckb_mock_tx_types::{MockResourceLoader, MockTransaction};
4use ckb_script::ScriptGroupType;
5use ckb_types::H256;
6use ckb_types::core::{HeaderView, ScriptHashType};
7use ckb_types::packed::{Byte32, CellOutput, OutPoint, Script};
8use ckb_types::prelude::{Builder, Entity, Pack};
9use ckb_vm::Bytes;
10use regex::{Captures, Regex};
11use std::collections::HashMap;
12use std::path::{Path, PathBuf};
13
14pub struct DummyResourceLoader {}
15
16impl MockResourceLoader for DummyResourceLoader {
17 fn get_header(&mut self, hash: H256) -> Result<Option<HeaderView>, String> {
18 return Err(format!("Header {:x} is missing!", hash));
19 }
20
21 fn get_live_cell(&mut self, out_point: OutPoint) -> Result<Option<(CellOutput, Bytes, Option<Byte32>)>, String> {
22 return Err(format!("Cell: {:?} is missing!", out_point));
23 }
24}
25
26pub struct Embed {
27 pub data: String,
28 pub path: PathBuf,
29 pub type_id_dict: HashMap<String, String>,
30}
31
32impl Embed {
33 pub fn new(path: PathBuf, data: String) -> Self {
34 Self { data, path, type_id_dict: HashMap::new() }
35 }
36
37 pub fn replace_data(&mut self) -> &mut Self {
38 let regex = Regex::new(r"\{\{ ?data (.+?) ?\}\}").unwrap();
39 self.data = regex
40 .replace_all(&self.data, |caps: &Captures| -> String {
41 let cap1 = &caps[1];
42 let path = if !Path::new(cap1).is_absolute() {
43 let root = self.path.parent().unwrap();
44 root.join(cap1)
45 } else {
46 Path::new(cap1).to_path_buf()
47 };
48 let data = std::fs::read(&path);
49 if data.is_err() {
50 panic!("Read {:?} failed : {:?}", path, data);
51 }
52 let data = data.unwrap();
53 hex::encode(data)
54 })
55 .to_string();
56 self
57 }
58
59 pub fn replace_hash(&mut self) -> &mut Self {
60 let regex = Regex::new(r"\{\{ ?hash (.+?) ?\}\}").unwrap();
61 self.data = regex
62 .replace_all(&self.data, |caps: &Captures| -> String {
63 let cap1 = &caps[1];
64 let path = if !Path::new(cap1).is_absolute() {
65 let root = self.path.parent().unwrap();
66 root.join(cap1)
67 } else {
68 Path::new(cap1).to_path_buf()
69 };
70 let data = std::fs::read(path).unwrap();
71 hex::encode(blake2b_256(data))
72 })
73 .to_string();
74 self
75 }
76
77 pub fn prelude_type_id(&mut self) -> &mut Self {
78 let rule = Regex::new(r"\{\{ ?def_type (.+?) ?\}\}").unwrap();
79 for caps in rule.captures_iter(&self.data) {
80 let type_id_name = &caps[1];
81 assert!(!self.type_id_dict.contains_key(type_id_name));
82 let type_id_script = Script::new_builder()
83 .args(Bytes::from(type_id_name.to_string()).pack())
84 .code_hash(TYPE_ID_CODE_HASH.pack())
85 .hash_type(ScriptHashType::Type.into())
86 .build();
87 let type_id_script_hash = type_id_script.calc_script_hash();
88 let type_id_script_hash = format!("{:x}", type_id_script_hash);
89 self.type_id_dict.insert(type_id_name.to_string(), type_id_script_hash);
90 }
91 self
92 }
93
94 pub fn replace_def_type(&mut self) -> &mut Self {
95 let regex = Regex::new(r#""?\{\{ ?def_type (.+?) ?\}\}"?"#).unwrap();
96 self.data = regex
97 .replace_all(&self.data, |caps: &Captures| -> String {
98 let cap1 = &caps[1];
99 let type_id_script_json = ckb_jsonrpc_types::Script {
100 code_hash: TYPE_ID_CODE_HASH,
101 hash_type: ckb_jsonrpc_types::ScriptHashType::Type,
102 args: ckb_jsonrpc_types::JsonBytes::from_vec(cap1.as_bytes().to_vec()),
103 };
104 return serde_json::to_string_pretty(&type_id_script_json).unwrap();
105 })
106 .to_string();
107 self
108 }
109
110 pub fn replace_ref_type(&mut self) -> &mut Self {
111 let regex = Regex::new(r"\{\{ ?ref_type (.+?) ?\}\}").unwrap();
112 self.data = regex
113 .replace_all(&self.data, |caps: &Captures| -> String {
114 let cap1 = &caps[1];
115 return self.type_id_dict[&cap1.to_string()].clone();
116 })
117 .to_string();
118 self
119 }
120
121 pub fn replace_all(&mut self) -> String {
122 self.replace_data().replace_hash().prelude_type_id().replace_def_type().replace_ref_type();
123 self.data.clone()
124 }
125}
126
127pub struct HumanReadableCycles(pub u64);
128
129impl std::fmt::Display for HumanReadableCycles {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 write!(f, "{}", self.0)?;
132 if self.0 >= 1024 * 1024 {
133 write!(f, "({:.1}M)", self.0 as f64 / 1024. / 1024.)?;
134 } else if self.0 >= 1024 {
135 write!(f, "({:.1}K)", self.0 as f64 / 1024.)?;
136 } else {
137 }
138 Ok(())
139 }
140}
141
142pub fn get_script_hash_by_index(
145 mock_tx: &MockTransaction,
146 script_group_type: &ScriptGroupType,
147 cell_type: &str,
148 cell_index: usize,
149) -> Byte32 {
150 match (&script_group_type, cell_type) {
151 (ScriptGroupType::Lock, "input") => mock_tx.mock_info.inputs[cell_index].output.calc_lock_hash(),
152 (ScriptGroupType::Type, "input") => mock_tx.mock_info.inputs[cell_index]
153 .output
154 .type_()
155 .to_opt()
156 .expect("cell should have type script")
157 .calc_script_hash(),
158 (ScriptGroupType::Type, "output") => mock_tx
159 .tx
160 .raw()
161 .outputs()
162 .get(cell_index)
163 .expect("index out of bound")
164 .type_()
165 .to_opt()
166 .expect("cell should have type script")
167 .calc_script_hash(),
168 _ => panic!("Invalid specified script: {:?} {} {}", script_group_type, cell_type, cell_index),
169 }
170}