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}