ckb_debugger/
analyzer.rs

1use std::fmt::Debug;
2
3use ckb_types::prelude::Entity;
4
5pub fn analyze(data: &str) -> Result<(), CheckError> {
6    prelude(data)?;
7    let mock: ckb_mock_tx_types::ReprMockTransaction = serde_json::from_str(&data).unwrap();
8    analyze_cell_dep(&mock)?;
9    analyze_header_dep(&mock)?;
10    analyze_input(&mock)?;
11    analyze_output(&mock)?;
12    Ok(())
13}
14
15pub fn analyze_cell_dep(data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> {
16    let cset: Vec<ckb_jsonrpc_types::CellDep> = data.mock_info.cell_deps.iter().map(|e| e.cell_dep.clone()).collect();
17    for (i, e) in data.tx.cell_deps.iter().enumerate() {
18        if !cset.contains(&e) {
19            let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("cell_deps")), Key::Index(i)];
20            return Err(CheckError(format!("Check Fail: {} used unprovided cell dep", keyfmt(&path))));
21        }
22    }
23    let mut ccnt = vec![0u8; cset.len()];
24    for (_, e) in data.tx.cell_deps.iter().enumerate() {
25        let i = cset.iter().position(|r| r == e).unwrap();
26        ccnt[i] += 1;
27        if data.mock_info.cell_deps[i].cell_dep.dep_type == ckb_jsonrpc_types::DepType::Code {
28            continue;
29        }
30        let outpoints =
31            ckb_types::packed::OutPointVec::from_slice(data.mock_info.cell_deps[i].data.as_bytes()).unwrap();
32        let outpoints: Vec<ckb_types::packed::OutPoint> = outpoints.into_iter().collect();
33        for (j, f) in outpoints.iter().enumerate() {
34            let cdep =
35                ckb_jsonrpc_types::CellDep { out_point: f.clone().into(), dep_type: ckb_jsonrpc_types::DepType::Code };
36            if !cset.contains(&cdep) {
37                let path = vec![
38                    Key::Table(String::from("mock_info")),
39                    Key::Table(String::from("cell_deps")),
40                    Key::Index(i),
41                    Key::Table(String::from("data")),
42                    Key::Index(j),
43                ];
44                return Err(CheckError(format!("Check Fail: {} used unprovided cell dep", keyfmt(&path))));
45            }
46            let k = cset.iter().position(|r| r == &cdep).unwrap();
47            ccnt[k] += 1;
48        }
49    }
50    for (i, e) in ccnt.iter().enumerate() {
51        if *e != 0 {
52            continue;
53        }
54        let path = vec![Key::Table(String::from("mock_info")), Key::Table(String::from("cell_deps")), Key::Index(i)];
55        return Err(CheckError(format!("Check Fail: {} unused", keyfmt(&path))));
56    }
57    Ok(())
58}
59
60pub fn analyze_header_dep(data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> {
61    let hset: Vec<ckb_types::H256> = data.mock_info.header_deps.iter().map(|e| e.hash.clone()).collect();
62    for (i, e) in data.tx.header_deps.iter().enumerate() {
63        if !hset.contains(&e) {
64            let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("header_deps")), Key::Index(i)];
65            return Err(CheckError(format!("Check Fail: {} used unprovided header dep", keyfmt(&path))));
66        }
67    }
68    for (i, e) in hset.iter().enumerate() {
69        if !data.tx.header_deps.contains(&e) {
70            let path =
71                vec![Key::Table(String::from("mock_info")), Key::Table(String::from("header_deps")), Key::Index(i)];
72            return Err(CheckError(format!("Check Fail: {} unused", keyfmt(&path))));
73        }
74    }
75    Ok(())
76}
77
78pub fn analyze_input(data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> {
79    let iset: Vec<ckb_jsonrpc_types::CellInput> = data.mock_info.inputs.iter().map(|e| e.input.clone()).collect();
80    for (i, e) in data.tx.inputs.iter().enumerate() {
81        if !iset.contains(&e) {
82            let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("inputs")), Key::Index(i)];
83            return Err(CheckError(format!("Check Fail: {} used unprovided input", keyfmt(&path))));
84        }
85    }
86    for (i, e) in iset.iter().enumerate() {
87        if !data.tx.inputs.contains(&e) {
88            let path = vec![Key::Table(String::from("mock_info")), Key::Table(String::from("inputs")), Key::Index(i)];
89            return Err(CheckError(format!("Check Fail: {} unused", keyfmt(&path))));
90        }
91    }
92    Ok(())
93}
94
95pub fn analyze_output(data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> {
96    if data.tx.outputs.len() != data.tx.outputs_data.len() {
97        let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("outputs"))];
98        return Err(CheckError(format!(
99            "Check Fail: {} outputs and outputs_data are not one-to-one correspondence",
100            keyfmt(&path)
101        )));
102    }
103    Ok(())
104}
105
106pub fn prelude(data: &str) -> Result<(), CheckError> {
107    let j: serde_json::Value = serde_json::from_str(data).map_err(|e| CheckError(e.to_string()))?;
108    prelude_contains_key(vec![], &j, "mock_info")?;
109    prelude_contains_key(vec![], &j, "tx")?;
110    prelude_mock_info(keyadd_table(vec![], "mock_info"), j.as_object().unwrap().get("mock_info").unwrap())?;
111    prelude_tx(keyadd_table(vec![], "tx"), j.as_object().unwrap().get("tx").unwrap())?;
112    Ok(())
113}
114
115pub fn prelude_cell_dep(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
116    prelude_contains_key(path.clone(), &data, "out_point")?;
117    prelude_contains_key(path.clone(), &data, "dep_type")?;
118    prelude_out_point(keyadd_table(path.clone(), "out_point"), data.as_object().unwrap().get("out_point").unwrap())?;
119    prelude_dep_type(keyadd_table(path.clone(), "dep_type"), data.as_object().unwrap().get("dep_type").unwrap())?;
120    Ok(())
121}
122
123pub fn prelude_contains_key(path: Vec<Key>, data: &serde_json::Value, key: &str) -> Result<(), CheckError> {
124    if !data.is_object() {
125        return Err(CheckError(format!("Check Fail: {} is not an object", keyfmt(&path))));
126    }
127    if !data.as_object().unwrap().contains_key(key) {
128        return Err(CheckError(format!("Check Fail: {} missing members: {}", keyfmt(&path), key)));
129    }
130    Ok(())
131}
132
133pub fn prelude_dep_type(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
134    if !data.is_string() {
135        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal dep type")));
136    }
137    if serde_json::from_str::<ckb_jsonrpc_types::DepType>(&format!("{:?}", &data.as_str().unwrap())).is_err() {
138        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal dep type")));
139    }
140    Ok(())
141}
142
143pub fn prelude_hash(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
144    prelude_hex(path.clone(), data)?;
145    if data.as_str().unwrap().len() != 66 {
146        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hash")));
147    }
148    Ok(())
149}
150
151pub fn prelude_hash_type(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
152    if !data.is_string() {
153        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hash type")));
154    }
155    if serde_json::from_str::<ckb_jsonrpc_types::ScriptHashType>(&format!("{:?}", &data.as_str().unwrap())).is_err() {
156        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hash type")));
157    }
158    Ok(())
159}
160
161pub fn prelude_hex(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
162    if !data.is_string() {
163        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hex string")));
164    }
165    if !data.as_str().unwrap().starts_with("0x") {
166        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hex string")));
167    }
168    if hex::decode(&data.as_str().unwrap()[2..]).is_err() {
169        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hex string")));
170    }
171    Ok(())
172}
173
174pub fn prelude_input(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
175    prelude_contains_key(path.clone(), &data, "since")?;
176    prelude_contains_key(path.clone(), &data, "previous_output")?;
177    prelude_u64(keyadd_table(path.clone(), "since"), data.as_object().unwrap().get("since").unwrap())?;
178    prelude_out_point(
179        keyadd_table(path.clone(), "previous_output"),
180        data.as_object().unwrap().get("previous_output").unwrap(),
181    )?;
182    Ok(())
183}
184
185pub fn prelude_mock_info(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
186    prelude_contains_key(path.clone(), &data, "inputs")?;
187    prelude_contains_key(path.clone(), &data, "cell_deps")?;
188    prelude_contains_key(path.clone(), &data, "header_deps")?;
189    for (i, e) in data.as_object().unwrap().get("inputs").unwrap().as_array().unwrap().iter().enumerate() {
190        let path = keyadd_index(keyadd_table(path.clone(), "inputs"), i);
191        prelude_contains_key(path.clone(), e, "input")?;
192        prelude_contains_key(path.clone(), e, "output")?;
193        prelude_contains_key(path.clone(), e, "data")?;
194        prelude_input(keyadd_table(path.clone(), "input"), e.as_object().unwrap().get("input").unwrap())?;
195        prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap())?;
196        prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap())?;
197        if e.as_object().unwrap().contains_key("header") && !e.as_object().unwrap().get("header").unwrap().is_null() {
198            prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap())?;
199        }
200    }
201    for (i, e) in data.as_object().unwrap().get("cell_deps").unwrap().as_array().unwrap().iter().enumerate() {
202        let path = keyadd_index(keyadd_table(path.clone(), "cell_deps"), i);
203        prelude_contains_key(path.clone(), e, "cell_dep")?;
204        prelude_contains_key(path.clone(), e, "output")?;
205        prelude_contains_key(path.clone(), e, "data")?;
206        prelude_cell_dep(keyadd_table(path.clone(), "cell_dep"), e.as_object().unwrap().get("cell_dep").unwrap())?;
207        prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap())?;
208        prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap())?;
209        if e.as_object().unwrap().contains_key("header") && !e.as_object().unwrap().get("header").unwrap().is_null() {
210            prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap())?;
211        }
212    }
213    for (i, e) in data.as_object().unwrap().get("header_deps").unwrap().as_array().unwrap().iter().enumerate() {
214        let path = keyadd_index(keyadd_table(path.clone(), "header_deps"), i);
215        prelude_contains_key(path.clone(), e, "compact_target")?;
216        prelude_contains_key(path.clone(), e, "dao")?;
217        prelude_contains_key(path.clone(), e, "epoch")?;
218        prelude_contains_key(path.clone(), e, "extra_hash")?;
219        prelude_contains_key(path.clone(), e, "hash")?;
220        prelude_contains_key(path.clone(), e, "nonce")?;
221        prelude_contains_key(path.clone(), e, "number")?;
222        prelude_contains_key(path.clone(), e, "parent_hash")?;
223        prelude_contains_key(path.clone(), e, "proposals_hash")?;
224        prelude_contains_key(path.clone(), e, "timestamp")?;
225        prelude_contains_key(path.clone(), e, "transactions_root")?;
226        prelude_contains_key(path.clone(), e, "version")?;
227        prelude_u32(
228            keyadd_table(path.clone(), "compact_target"),
229            e.as_object().unwrap().get("compact_target").unwrap(),
230        )?;
231        prelude_hash(keyadd_table(path.clone(), "dao"), e.as_object().unwrap().get("dao").unwrap())?;
232        prelude_u64(keyadd_table(path.clone(), "epoch"), e.as_object().unwrap().get("epoch").unwrap())?;
233        prelude_hash(keyadd_table(path.clone(), "extra_hash"), e.as_object().unwrap().get("extra_hash").unwrap())?;
234        prelude_hash(keyadd_table(path.clone(), "hash"), e.as_object().unwrap().get("hash").unwrap())?;
235        prelude_u128(keyadd_table(path.clone(), "nonce"), e.as_object().unwrap().get("nonce").unwrap())?;
236        prelude_u64(keyadd_table(path.clone(), "number"), e.as_object().unwrap().get("number").unwrap())?;
237        prelude_hash(keyadd_table(path.clone(), "parent_hash"), e.as_object().unwrap().get("parent_hash").unwrap())?;
238        prelude_hash(
239            keyadd_table(path.clone(), "proposals_hash"),
240            e.as_object().unwrap().get("proposals_hash").unwrap(),
241        )?;
242        prelude_u64(keyadd_table(path.clone(), "timestamp"), e.as_object().unwrap().get("timestamp").unwrap())?;
243        prelude_hash(
244            keyadd_table(path.clone(), "transactions_root"),
245            e.as_object().unwrap().get("transactions_root").unwrap(),
246        )?;
247        prelude_u32(keyadd_table(path.clone(), "version"), e.as_object().unwrap().get("version").unwrap())?;
248    }
249    Ok(())
250}
251
252pub fn prelude_out_point(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
253    prelude_contains_key(path.clone(), data, "tx_hash")?;
254    prelude_contains_key(path.clone(), data, "index")?;
255    prelude_hash(keyadd_table(path.clone(), "tx_hash"), data.as_object().unwrap().get("tx_hash").unwrap())?;
256    prelude_u32(keyadd_table(path.clone(), "index"), data.as_object().unwrap().get("index").unwrap())?;
257    Ok(())
258}
259
260pub fn prelude_output(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
261    prelude_contains_key(path.clone(), data, "capacity")?;
262    prelude_contains_key(path.clone(), data, "lock")?;
263    prelude_u64(keyadd_table(path.clone(), "capacity"), data.as_object().unwrap().get("capacity").unwrap())?;
264    prelude_script(keyadd_table(path.clone(), "lock"), data.as_object().unwrap().get("lock").unwrap())?;
265    if data.as_object().unwrap().contains_key("type") && !data.as_object().unwrap().get("type").unwrap().is_null() {
266        prelude_script(keyadd_table(path.clone(), "type"), data.as_object().unwrap().get("type").unwrap())?;
267    }
268    Ok(())
269}
270
271pub fn prelude_script(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
272    prelude_contains_key(path.clone(), data, "code_hash")?;
273    prelude_contains_key(path.clone(), data, "hash_type")?;
274    prelude_contains_key(path.clone(), data, "args")?;
275    prelude_hash(keyadd_table(path.clone(), "code_hash"), data.as_object().unwrap().get("code_hash").unwrap())?;
276    prelude_hash_type(keyadd_table(path.clone(), "hash_type"), data.as_object().unwrap().get("hash_type").unwrap())?;
277    prelude_hex(keyadd_table(path.clone(), "args"), data.as_object().unwrap().get("args").unwrap())?;
278    Ok(())
279}
280
281pub fn prelude_tx(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
282    prelude_contains_key(path.clone(), &data, "version")?;
283    prelude_contains_key(path.clone(), &data, "cell_deps")?;
284    prelude_contains_key(path.clone(), &data, "header_deps")?;
285    prelude_contains_key(path.clone(), &data, "inputs")?;
286    prelude_contains_key(path.clone(), &data, "outputs")?;
287    prelude_contains_key(path.clone(), &data, "outputs_data")?;
288    prelude_contains_key(path.clone(), &data, "witnesses")?;
289    prelude_u32(keyadd_table(path.clone(), "version"), data.as_object().unwrap().get("version").unwrap())?;
290    for (i, e) in data.as_object().unwrap().get("cell_deps").unwrap().as_array().unwrap().iter().enumerate() {
291        let path = keyadd_index(keyadd_table(path.clone(), "cell_deps"), i);
292        prelude_cell_dep(path, e)?;
293    }
294    for (i, e) in data.as_object().unwrap().get("header_deps").unwrap().as_array().unwrap().iter().enumerate() {
295        let path = keyadd_index(keyadd_table(path.clone(), "header_deps"), i);
296        prelude_hash(path, e)?;
297    }
298    for (i, e) in data.as_object().unwrap().get("inputs").unwrap().as_array().unwrap().iter().enumerate() {
299        let path = keyadd_index(keyadd_table(path.clone(), "inputs"), i);
300        prelude_input(path, e)?;
301    }
302    for (i, e) in data.as_object().unwrap().get("outputs").unwrap().as_array().unwrap().iter().enumerate() {
303        let path = keyadd_index(keyadd_table(path.clone(), "outputs"), i);
304        prelude_output(path, e)?;
305    }
306    for (i, e) in data.as_object().unwrap().get("outputs_data").unwrap().as_array().unwrap().iter().enumerate() {
307        let path = keyadd_index(keyadd_table(path.clone(), "outputs_data"), i);
308        prelude_hex(path, e)?;
309    }
310    for (i, e) in data.as_object().unwrap().get("witnesses").unwrap().as_array().unwrap().iter().enumerate() {
311        let path = keyadd_index(keyadd_table(path.clone(), "witnesses"), i);
312        prelude_hex(path, e)?;
313    }
314    Ok(())
315}
316
317pub fn prelude_u128(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
318    if !data.is_string() || !data.as_str().unwrap().starts_with("0x") {
319        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u128")));
320    }
321    if u128::from_str_radix(&data.as_str().unwrap()[2..], 16).is_err() {
322        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u128")));
323    }
324    Ok(())
325}
326
327pub fn prelude_u32(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
328    if !data.is_string() || !data.as_str().unwrap().starts_with("0x") {
329        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u32")));
330    }
331    if u32::from_str_radix(&data.as_str().unwrap()[2..], 16).is_err() {
332        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u32")));
333    }
334    Ok(())
335}
336
337pub fn prelude_u64(path: Vec<Key>, data: &serde_json::Value) -> Result<(), CheckError> {
338    if !data.is_string() || !data.as_str().unwrap().starts_with("0x") {
339        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u64")));
340    }
341    if u64::from_str_radix(&data.as_str().unwrap()[2..], 16).is_err() {
342        return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u64")));
343    }
344    Ok(())
345}
346
347pub struct CheckError(String);
348
349impl std::fmt::Debug for CheckError {
350    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351        f.write_str(&self.0)
352    }
353}
354
355impl std::fmt::Display for CheckError {
356    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
357        f.write_str(&self.0)
358    }
359}
360
361impl std::error::Error for CheckError {}
362
363#[derive(Clone, Debug)]
364pub enum Key {
365    Table(String),
366    Index(usize),
367}
368
369pub fn keyfmt(key: &[Key]) -> String {
370    let mut s = String::from("json");
371    for e in key {
372        match e {
373            Key::Table(k) => {
374                s.push_str(&format!("[\"{}\"]", k));
375            }
376            Key::Index(i) => {
377                s.push_str(&format!("[{}]", i));
378            }
379        }
380    }
381    s
382}
383
384pub fn keyadd_index(mut key: Vec<Key>, add: usize) -> Vec<Key> {
385    key.push(Key::Index(add));
386    key
387}
388
389pub fn keyadd_table(mut key: Vec<Key>, add: &str) -> Vec<Key> {
390    key.push(Key::Table(String::from(add)));
391    key
392}