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