Skip to main content

ckb_debugger/
mock_tx_embed.rs

1use ckb_chain_spec::consensus::TYPE_ID_CODE_HASH;
2use ckb_hash::blake2b_256;
3use ckb_types::core::ScriptHashType;
4use ckb_types::packed::Script;
5use ckb_types::prelude::{Builder, Entity, Pack};
6use ckb_vm::Bytes;
7use regex::{Captures, Regex};
8use std::collections::HashMap;
9use std::path::{Path, PathBuf};
10
11/// Processes a transaction template by replacing placeholders in the provided data and returns the final processed
12/// string.
13pub fn mock_tx_embed(path: PathBuf, data: &str) -> String {
14    let mut embed = MockTxEmbed::new(path, data.to_string());
15    embed.replace_all()
16}
17
18/// Struct to hold embedded data, its file path, and a dictionary for type IDs
19pub struct MockTxEmbed {
20    pub data: String,
21    pub path: PathBuf,
22    pub type_id_dict: HashMap<String, String>,
23}
24
25impl MockTxEmbed {
26    pub fn new(path: PathBuf, data: String) -> Self {
27        Self { data, path, type_id_dict: HashMap::new() }
28    }
29
30    /// Replaces {{ data <path> }} placeholders with hex-encoded file content.
31    pub fn replace_data(&mut self) -> &mut Self {
32        let regex = Regex::new(r"\{\{ ?data (.+?) ?\}\}").unwrap();
33        self.data = regex
34            .replace_all(&self.data, |caps: &Captures| -> String {
35                let cap1 = &caps[1];
36                let path = if !Path::new(cap1).is_absolute() {
37                    let root = self.path.parent().unwrap();
38                    root.join(cap1)
39                } else {
40                    Path::new(cap1).to_path_buf()
41                };
42                let data = std::fs::read(&path);
43                if data.is_err() {
44                    panic!("Read {:?} failed : {:?}", path, data);
45                }
46                let data = data.unwrap();
47                hex::encode(data)
48            })
49            .to_string();
50        self
51    }
52
53    /// Replaces {{ hash <path> }} placeholders with Blake2b hash of file content.
54    pub fn replace_hash(&mut self) -> &mut Self {
55        let regex = Regex::new(r"\{\{ ?hash (.+?) ?\}\}").unwrap();
56        self.data = regex
57            .replace_all(&self.data, |caps: &Captures| -> String {
58                let cap1 = &caps[1];
59                let path = if !Path::new(cap1).is_absolute() {
60                    let root = self.path.parent().unwrap();
61                    root.join(cap1)
62                } else {
63                    Path::new(cap1).to_path_buf()
64                };
65                let data = std::fs::read(path).unwrap();
66                hex::encode(blake2b_256(data))
67            })
68            .to_string();
69        self
70    }
71
72    /// Processes {{ def_type <name> }} placeholders to generate and store type ID script hashes.
73    pub fn prelude_type_id(&mut self) -> &mut Self {
74        let rule = Regex::new(r"\{\{ ?def_type (.+?) ?\}\}").unwrap();
75        for caps in rule.captures_iter(&self.data) {
76            let type_id_name = &caps[1];
77            assert!(!self.type_id_dict.contains_key(type_id_name));
78            let type_id_script = Script::new_builder()
79                .args(Bytes::from(type_id_name.to_string()).pack())
80                .code_hash(TYPE_ID_CODE_HASH.pack())
81                .hash_type(ScriptHashType::Type)
82                .build();
83            let type_id_script_hash = type_id_script.calc_script_hash();
84            let type_id_script_hash = format!("{:x}", type_id_script_hash);
85            self.type_id_dict.insert(type_id_name.to_string(), type_id_script_hash);
86        }
87        self
88    }
89
90    /// Replaces {{ def_type <name> }} placeholders with JSON-serialized script objects.
91    pub fn replace_def_type(&mut self) -> &mut Self {
92        let regex = Regex::new(r#""?\{\{ ?def_type (.+?) ?\}\}"?"#).unwrap();
93        self.data = regex
94            .replace_all(&self.data, |caps: &Captures| -> String {
95                let cap1 = &caps[1];
96                let type_id_script_json = ckb_jsonrpc_types::Script {
97                    code_hash: TYPE_ID_CODE_HASH,
98                    hash_type: ckb_jsonrpc_types::ScriptHashType::Type,
99                    args: ckb_jsonrpc_types::JsonBytes::from_vec(cap1.as_bytes().to_vec()),
100                };
101                return serde_json::to_string_pretty(&type_id_script_json).unwrap();
102            })
103            .to_string();
104        self
105    }
106
107    /// Replaces {{ ref_type <name> }} placeholders with previously computed type ID script hashes.
108    pub fn replace_ref_type(&mut self) -> &mut Self {
109        let regex = Regex::new(r"\{\{ ?ref_type (.+?) ?\}\}").unwrap();
110        self.data = regex
111            .replace_all(&self.data, |caps: &Captures| -> String {
112                let cap1 = &caps[1];
113                return self.type_id_dict[&cap1.to_string()].clone();
114            })
115            .to_string();
116        self
117    }
118
119    /// Chains all replacement operations and returns the final processed data.
120    pub fn replace_all(&mut self) -> String {
121        self.replace_data().replace_hash().prelude_type_id().replace_def_type().replace_ref_type();
122        self.data.clone()
123    }
124}