From 246029b016ac4ae8fc0cad87c375764dac872111 Mon Sep 17 00:00:00 2001 From: mohanson Date: Fri, 7 Feb 2025 17:23:44 +0800 Subject: [PATCH 1/4] Add analyzer --- ckb-debugger/examples/exec.json | 6 +- ckb-debugger/examples/spawn.json | 6 +- ckb-debugger/src/analyzer.rs | 346 +++++++++++++++++++++++++++++++ ckb-debugger/src/lib.rs | 4 +- ckb-debugger/src/main.rs | 10 +- ckb-debugger/src/misc.rs | 42 +--- 6 files changed, 360 insertions(+), 54 deletions(-) create mode 100644 ckb-debugger/src/analyzer.rs diff --git a/ckb-debugger/examples/exec.json b/ckb-debugger/examples/exec.json index 72d613c..bc3f369 100644 --- a/ckb-debugger/examples/exec.json +++ b/ckb-debugger/examples/exec.json @@ -19,7 +19,7 @@ "type": null }, "data": "0x", - "header": null + "header": "0x0000000000000000000000000000000000000000000000000000000000000000" } ], "cell_deps": [ @@ -41,7 +41,7 @@ "type": null }, "data": "0x{{ data exec_caller }}", - "header": null + "header": "0x0000000000000000000000000000000000000000000000000000000000000000" }, { "cell_dep": { @@ -61,7 +61,7 @@ "type": null }, "data": "0x{{ data exec_callee }}", - "header": null + "header": "0x0000000000000000000000000000000000000000000000000000000000000000" } ], "header_deps": [] diff --git a/ckb-debugger/examples/spawn.json b/ckb-debugger/examples/spawn.json index 3fc389c..cad55c8 100644 --- a/ckb-debugger/examples/spawn.json +++ b/ckb-debugger/examples/spawn.json @@ -19,7 +19,7 @@ "type": null }, "data": "0x", - "header": null + "header": "0x0000000000000000000000000000000000000000000000000000000000000000" } ], "cell_deps": [ @@ -41,7 +41,7 @@ "type": null }, "data": "0x{{ data spawn_caller_strcat }}", - "header": null + "header": "0x0000000000000000000000000000000000000000000000000000000000000000" }, { "cell_dep": { @@ -61,7 +61,7 @@ "type": null }, "data": "0x{{ data spawn_callee_strcat }}", - "header": null + "header": "0x0000000000000000000000000000000000000000000000000000000000000000" } ], "header_deps": [] diff --git a/ckb-debugger/src/analyzer.rs b/ckb-debugger/src/analyzer.rs new file mode 100644 index 0000000..fb1a6e2 --- /dev/null +++ b/ckb-debugger/src/analyzer.rs @@ -0,0 +1,346 @@ +use ckb_types::prelude::Entity; + +pub fn analyze(data: &str) { + prelude(data); + let mock: ckb_mock_tx_types::ReprMockTransaction = serde_json::from_str(&data).unwrap(); + + for (i, e) in mock.mock_info.cell_deps.iter().enumerate() { + if e.cell_dep.dep_type == ckb_jsonrpc_types::DepType::Code { + continue; + } + let outpoints = ckb_types::packed::OutPointVec::from_slice(e.data.as_bytes()).unwrap(); + let outpoints: Vec = outpoints.into_iter().collect(); + for (j, f) in outpoints.iter().enumerate() { + let path = vec![ + Key::Table(String::from("mock_info")), + Key::Table(String::from("cell_deps")), + Key::Index(i), + Key::Table(String::from("data")), + Key::Index(j), + ]; + analyze_cell_dep( + path, + &mock, + &ckb_jsonrpc_types::CellDep { out_point: f.clone().into(), dep_type: ckb_jsonrpc_types::DepType::Code }, + ); + } + } + for (i, e) in mock.tx.cell_deps.iter().enumerate() { + let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("cell_deps")), Key::Index(i)]; + analyze_cell_dep(path, &mock, &e); + } + for (i, e) in mock.tx.header_deps.iter().enumerate() { + let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("header_deps")), Key::Index(i)]; + analyze_header_dep(path, &mock, e); + } + for (i, e) in mock.tx.inputs.iter().enumerate() { + let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("inputs")), Key::Index(i)]; + analyze_input(path, &mock, &e); + } + analyze_output(vec![Key::Table(String::from("tx")), Key::Table(String::from("outputs"))], &mock); +} + +pub fn analyze_cell_dep( + path: Vec, + data: &ckb_mock_tx_types::ReprMockTransaction, + cell_dep: &ckb_jsonrpc_types::CellDep, +) { + let cset: Vec = data.mock_info.cell_deps.iter().map(|e| e.cell_dep.clone()).collect(); + if !cset.contains(&cell_dep) { + println!("Check Fail: {} used unprovided cell dep", keyfmt(path)); + std::process::exit(1); + } +} + +pub fn analyze_header_dep(path: Vec, data: &ckb_mock_tx_types::ReprMockTransaction, header_dep: &ckb_types::H256) { + let hset: Vec = data.mock_info.header_deps.iter().map(|e| e.hash.clone()).collect(); + if !hset.contains(&header_dep) { + println!("Check Fail: {} used unprovided header dep", keyfmt(path)); + std::process::exit(1); + } +} + +pub fn analyze_input( + path: Vec, + data: &ckb_mock_tx_types::ReprMockTransaction, + input: &ckb_jsonrpc_types::CellInput, +) { + let iset: Vec = data.mock_info.inputs.iter().map(|e| e.input.clone()).collect(); + if !iset.contains(&input) { + println!("Check Fail: {} used unprovided input", keyfmt(path)); + } +} + +pub fn analyze_output(path: Vec, data: &ckb_mock_tx_types::ReprMockTransaction) { + if data.tx.outputs.len() != data.tx.outputs_data.len() { + println!("Check Fail: {} outputs and outputs_data are not one-to-one correspondence", keyfmt(path)); + std::process::exit(1); + } +} + +pub fn prelude(data: &str) { + let j: serde_json::Value = serde_json::from_str(data).unwrap(); + prelude_contains_key(vec![], &j, "mock_info"); + prelude_contains_key(vec![], &j, "tx"); + prelude_mock_info(keyadd_table(vec![], "mock_info"), j.as_object().unwrap().get("mock_info").unwrap()); + prelude_tx(keyadd_table(vec![], "tx"), j.as_object().unwrap().get("tx").unwrap()); +} + +pub fn prelude_cell_dep(path: Vec, data: &serde_json::Value) { + prelude_contains_key(path.clone(), &data, "out_point"); + prelude_contains_key(path.clone(), &data, "dep_type"); + prelude_out_point(keyadd_table(path.clone(), "out_point"), data.as_object().unwrap().get("out_point").unwrap()); + prelude_dep_type(keyadd_table(path.clone(), "dep_type"), data.as_object().unwrap().get("dep_type").unwrap()); +} + +pub fn prelude_contains_key(path: Vec, data: &serde_json::Value, key: &str) { + if !data.as_object().unwrap().contains_key(key) { + println!("Check Fail: {} missing members: {}", keyfmt(path), key); + std::process::exit(1); + } +} + +pub fn prelude_dep_type(path: Vec, data: &serde_json::Value) { + if data.as_str().is_none() { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal dep type"); + std::process::exit(1); + } + if serde_json::from_str::(&format!("{:?}", &data.as_str().unwrap())).is_err() { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal dep type"); + std::process::exit(1); + } +} + +pub fn prelude_hash(path: Vec, data: &serde_json::Value) { + prelude_hex(path.clone(), data); + if data.as_str().unwrap().len() != 66 { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal hash"); + std::process::exit(1); + } +} + +pub fn prelude_hash_type(path: Vec, data: &serde_json::Value) { + if data.as_str().is_none() { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal hash type"); + std::process::exit(1); + } + if serde_json::from_str::(&format!("{:?}", &data.as_str().unwrap())).is_err() { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal hash type"); + std::process::exit(1); + } +} + +pub fn prelude_hex(path: Vec, data: &serde_json::Value) { + if data.as_str().is_none() { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal hex string"); + std::process::exit(1); + } + if !data.as_str().unwrap().starts_with("0x") { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal hex string"); + std::process::exit(1); + } + if hex::decode(&data.as_str().unwrap()[2..]).is_err() { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal hex string"); + std::process::exit(1); + } +} + +pub fn prelude_input(path: Vec, data: &serde_json::Value) { + prelude_contains_key(path.clone(), &data, "since"); + prelude_contains_key(path.clone(), &data, "previous_output"); + prelude_u64(keyadd_table(path.clone(), "since"), data.as_object().unwrap().get("since").unwrap()); + prelude_out_point( + keyadd_table(path.clone(), "previous_output"), + data.as_object().unwrap().get("previous_output").unwrap(), + ); +} + +pub fn prelude_mock_info(path: Vec, data: &serde_json::Value) { + prelude_contains_key(path.clone(), &data, "inputs"); + prelude_contains_key(path.clone(), &data, "cell_deps"); + prelude_contains_key(path.clone(), &data, "header_deps"); + for (i, e) in data.as_object().unwrap().get("inputs").unwrap().as_array().unwrap().iter().enumerate() { + let path = keyadd_index(keyadd_table(path.clone(), "inputs"), i); + prelude_contains_key(path.clone(), e, "input"); + prelude_contains_key(path.clone(), e, "output"); + prelude_contains_key(path.clone(), e, "data"); + prelude_contains_key(path.clone(), e, "header"); + prelude_input(keyadd_table(path.clone(), "input"), e.as_object().unwrap().get("input").unwrap()); + prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap()); + prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap()); + prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap()); + } + for (i, e) in data.as_object().unwrap().get("cell_deps").unwrap().as_array().unwrap().iter().enumerate() { + let path = keyadd_index(keyadd_table(path.clone(), "cell_deps"), i); + prelude_contains_key(path.clone(), e, "cell_dep"); + prelude_contains_key(path.clone(), e, "output"); + prelude_contains_key(path.clone(), e, "data"); + prelude_contains_key(path.clone(), e, "header"); + prelude_cell_dep(keyadd_table(path.clone(), "cell_dep"), e.as_object().unwrap().get("cell_dep").unwrap()); + prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap()); + prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap()); + prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap()); + } + for (i, e) in data.as_object().unwrap().get("header_deps").unwrap().as_array().unwrap().iter().enumerate() { + let path = keyadd_index(keyadd_table(path.clone(), "header_deps"), i); + prelude_contains_key(path.clone(), e, "compact_target"); + prelude_contains_key(path.clone(), e, "dao"); + prelude_contains_key(path.clone(), e, "epoch"); + prelude_contains_key(path.clone(), e, "extra_hash"); + prelude_contains_key(path.clone(), e, "hash"); + prelude_contains_key(path.clone(), e, "nonce"); + prelude_contains_key(path.clone(), e, "number"); + prelude_contains_key(path.clone(), e, "parent_hash"); + prelude_contains_key(path.clone(), e, "proposals_hash"); + prelude_contains_key(path.clone(), e, "timestamp"); + prelude_contains_key(path.clone(), e, "transactions_root"); + prelude_contains_key(path.clone(), e, "version"); + prelude_u32( + keyadd_table(path.clone(), "compact_target"), + e.as_object().unwrap().get("compact_target").unwrap(), + ); + prelude_hash(keyadd_table(path.clone(), "dao"), e.as_object().unwrap().get("dao").unwrap()); + prelude_u64(keyadd_table(path.clone(), "epoch"), e.as_object().unwrap().get("epoch").unwrap()); + prelude_hash(keyadd_table(path.clone(), "extra_hash"), e.as_object().unwrap().get("extra_hash").unwrap()); + prelude_hash(keyadd_table(path.clone(), "hash"), e.as_object().unwrap().get("hash").unwrap()); + prelude_u128(keyadd_table(path.clone(), "nonce"), e.as_object().unwrap().get("nonce").unwrap()); + prelude_u64(keyadd_table(path.clone(), "number"), e.as_object().unwrap().get("number").unwrap()); + prelude_hash(keyadd_table(path.clone(), "parent_hash"), e.as_object().unwrap().get("parent_hash").unwrap()); + prelude_hash( + keyadd_table(path.clone(), "proposals_hash"), + e.as_object().unwrap().get("proposals_hash").unwrap(), + ); + prelude_u64(keyadd_table(path.clone(), "timestamp"), e.as_object().unwrap().get("timestamp").unwrap()); + prelude_hash( + keyadd_table(path.clone(), "transactions_root"), + e.as_object().unwrap().get("transactions_root").unwrap(), + ); + prelude_u32(keyadd_table(path.clone(), "version"), e.as_object().unwrap().get("version").unwrap()); + } +} + +pub fn prelude_out_point(path: Vec, data: &serde_json::Value) { + prelude_contains_key(path.clone(), data, "tx_hash"); + prelude_contains_key(path.clone(), data, "index"); + prelude_hash(keyadd_table(path.clone(), "tx_hash"), data.as_object().unwrap().get("tx_hash").unwrap()); + prelude_u32(keyadd_table(path.clone(), "index"), data.as_object().unwrap().get("index").unwrap()); +} + +pub fn prelude_output(path: Vec, data: &serde_json::Value) { + prelude_contains_key(path.clone(), data, "capacity"); + prelude_contains_key(path.clone(), data, "lock"); + prelude_contains_key(path.clone(), data, "type"); + prelude_u64(keyadd_table(path.clone(), "capacity"), data.as_object().unwrap().get("capacity").unwrap()); + prelude_script(keyadd_table(path.clone(), "lock"), data.as_object().unwrap().get("lock").unwrap()); + if !data.as_object().unwrap().get("type").unwrap().is_null() { + prelude_script(keyadd_table(path.clone(), "type"), data.as_object().unwrap().get("type").unwrap()); + } +} + +pub fn prelude_script(path: Vec, data: &serde_json::Value) { + prelude_contains_key(path.clone(), data, "code_hash"); + prelude_contains_key(path.clone(), data, "hash_type"); + prelude_contains_key(path.clone(), data, "args"); + prelude_hash(keyadd_table(path.clone(), "code_hash"), data.as_object().unwrap().get("code_hash").unwrap()); + prelude_hash_type(keyadd_table(path.clone(), "hash_type"), data.as_object().unwrap().get("hash_type").unwrap()); + prelude_hex(keyadd_table(path.clone(), "args"), data.as_object().unwrap().get("args").unwrap()); +} + +pub fn prelude_tx(path: Vec, data: &serde_json::Value) { + prelude_contains_key(path.clone(), &data, "version"); + prelude_contains_key(path.clone(), &data, "cell_deps"); + prelude_contains_key(path.clone(), &data, "header_deps"); + prelude_contains_key(path.clone(), &data, "inputs"); + prelude_contains_key(path.clone(), &data, "outputs"); + prelude_contains_key(path.clone(), &data, "outputs_data"); + prelude_contains_key(path.clone(), &data, "witnesses"); + prelude_u32(keyadd_table(path.clone(), "version"), data.as_object().unwrap().get("version").unwrap()); + for (i, e) in data.as_object().unwrap().get("cell_deps").unwrap().as_array().unwrap().iter().enumerate() { + let path = keyadd_index(keyadd_table(path.clone(), "cell_deps"), i); + prelude_cell_dep(path, e); + } + for (i, e) in data.as_object().unwrap().get("header_deps").unwrap().as_array().unwrap().iter().enumerate() { + let path = keyadd_index(keyadd_table(path.clone(), "header_deps"), i); + prelude_hash(path, e); + } + for (i, e) in data.as_object().unwrap().get("inputs").unwrap().as_array().unwrap().iter().enumerate() { + let path = keyadd_index(keyadd_table(path.clone(), "inputs"), i); + prelude_input(path, e); + } + for (i, e) in data.as_object().unwrap().get("outputs").unwrap().as_array().unwrap().iter().enumerate() { + let path = keyadd_index(keyadd_table(path.clone(), "outputs"), i); + prelude_output(path, e); + } + for (i, e) in data.as_object().unwrap().get("outputs_data").unwrap().as_array().unwrap().iter().enumerate() { + let path = keyadd_index(keyadd_table(path.clone(), "outputs_data"), i); + prelude_hex(path, e); + } + for (i, e) in data.as_object().unwrap().get("witnesses").unwrap().as_array().unwrap().iter().enumerate() { + let path = keyadd_index(keyadd_table(path.clone(), "witnesses"), i); + prelude_hex(path, e); + } +} + +pub fn prelude_u128(path: Vec, data: &serde_json::Value) { + if !data.is_string() || !data.as_str().unwrap().starts_with("0x") { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal u128"); + std::process::exit(1); + } + if u128::from_str_radix(&data.as_str().unwrap()[2..], 16).is_err() { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal u128"); + std::process::exit(1); + } +} + +pub fn prelude_u32(path: Vec, data: &serde_json::Value) { + if !data.is_string() || !data.as_str().unwrap().starts_with("0x") { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal u32"); + std::process::exit(1); + } + if u32::from_str_radix(&data.as_str().unwrap()[2..], 16).is_err() { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal u32"); + std::process::exit(1); + } +} + +pub fn prelude_u64(path: Vec, data: &serde_json::Value) { + if !data.is_string() || !data.as_str().unwrap().starts_with("0x") { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal u64"); + std::process::exit(1); + } + if u64::from_str_radix(&data.as_str().unwrap()[2..], 16).is_err() { + println!("Check Fail: {} {}", keyfmt(path), "is not a legal u64"); + std::process::exit(1); + } +} + +#[derive(Clone, Debug)] +pub enum Key { + Table(String), + Index(usize), +} + +pub fn keyfmt(key: Vec) -> String { + let mut s = String::from("json"); + for e in key { + match e { + Key::Table(k) => { + s.push_str(&format!("[\"{}\"]", k)); + } + Key::Index(i) => { + s.push_str(&format!("[{}]", i)); + } + } + } + s +} + +pub fn keyadd_index(mut key: Vec, add: usize) -> Vec { + key.push(Key::Index(add)); + key +} + +pub fn keyadd_table(mut key: Vec, add: &str) -> Vec { + key.push(Key::Table(String::from(add))); + key +} diff --git a/ckb-debugger/src/lib.rs b/ckb-debugger/src/lib.rs index 8c9a255..bf98ad8 100644 --- a/ckb-debugger/src/lib.rs +++ b/ckb-debugger/src/lib.rs @@ -1,3 +1,4 @@ +mod analyzer; mod api; mod machine_analyzer; mod machine_assign; @@ -8,11 +9,12 @@ mod syscall_elf_dumper; #[cfg(target_family = "unix")] mod syscall_stdio; +pub use analyzer::analyze; pub use api::{run, run_json}; pub use machine_analyzer::{MachineAnalyzer, MachineOverlap, MachineProfile, MachineStepLog}; pub use machine_assign::MachineAssign; pub use machine_gdb::{GdbStubHandler, GdbStubHandlerEventLoop}; -pub use misc::{get_script_hash_by_index, pre_check, DummyResourceLoader, Embed, HumanReadableCycles}; +pub use misc::{get_script_hash_by_index, DummyResourceLoader, Embed, HumanReadableCycles}; pub use syscall_all::{FileOperation, FileStream, Random, TimeNow}; pub use syscall_elf_dumper::ElfDumper; #[cfg(target_family = "unix")] diff --git a/ckb-debugger/src/main.rs b/ckb-debugger/src/main.rs index 6993fa5..d1ead76 100644 --- a/ckb-debugger/src/main.rs +++ b/ckb-debugger/src/main.rs @@ -2,7 +2,7 @@ use ckb_chain_spec::consensus::{ConsensusBuilder, TYPE_ID_CODE_HASH}; #[cfg(target_family = "unix")] use ckb_debugger::Stdio; use ckb_debugger::{ - get_script_hash_by_index, pre_check, ElfDumper, FileOperation, FileStream, HumanReadableCycles, MachineAnalyzer, + analyze, get_script_hash_by_index, ElfDumper, FileOperation, FileStream, HumanReadableCycles, MachineAnalyzer, MachineAssign, MachineOverlap, MachineProfile, MachineStepLog, Random, TimeNow, }; use ckb_debugger::{Embed, GdbStubHandler, GdbStubHandlerEventLoop}; @@ -214,20 +214,16 @@ fn main() -> Result<(), Box> { Some("-") => { let mut buf = String::new(); std::io::stdin().read_to_string(&mut buf)?; + analyze(&buf); let repr_mock_tx: ReprMockTransaction = serde_json::from_str(&buf)?; - if let Err(msg) = pre_check(&repr_mock_tx) { - println!("Potential format error found: {}", msg); - } repr_mock_tx.into() } Some(doc) => { let buf = std::fs::read_to_string(doc)?; let mut mock_tx_embed = Embed::new(PathBuf::from(doc.to_string()), buf.clone()); let buf = mock_tx_embed.replace_all(); + analyze(&buf); let repr_mock_tx: ReprMockTransaction = serde_json::from_str(&buf)?; - if let Err(msg) = pre_check(&repr_mock_tx) { - println!("Potential format error found: {}", msg); - } repr_mock_tx.into() } None => { diff --git a/ckb-debugger/src/misc.rs b/ckb-debugger/src/misc.rs index 8d6d3b6..b30576a 100644 --- a/ckb-debugger/src/misc.rs +++ b/ckb-debugger/src/misc.rs @@ -1,9 +1,9 @@ use ckb_chain_spec::consensus::TYPE_ID_CODE_HASH; use ckb_hash::blake2b_256; -use ckb_mock_tx_types::{MockResourceLoader, MockTransaction, ReprMockTransaction}; +use ckb_mock_tx_types::{MockResourceLoader, MockTransaction}; use ckb_script::ScriptGroupType; use ckb_types::core::{HeaderView, ScriptHashType}; -use ckb_types::packed::{Byte32, CellOutput, OutPoint, OutPointVec, Script}; +use ckb_types::packed::{Byte32, CellOutput, OutPoint, Script}; use ckb_types::prelude::{Builder, Entity, Pack}; use ckb_types::H256; use ckb_vm::Bytes; @@ -168,41 +168,3 @@ pub fn get_script_hash_by_index( _ => panic!("Invalid specified script: {:?} {} {}", script_group_type, cell_type, cell_index), } } - -// Check transactions before executing them to avoid obvious mistakes. -pub fn pre_check(tx: &ReprMockTransaction) -> Result<(), String> { - let mut mock_cell_deps: Vec<_> = tx.mock_info.cell_deps.iter().map(|c| c.cell_dep.clone()).collect(); - let mut real_cell_deps: Vec<_> = tx.tx.cell_deps.iter().map(|c| c.clone()).collect(); - for dep in &tx.mock_info.cell_deps { - if dep.cell_dep.dep_type == ckb_jsonrpc_types::DepType::DepGroup { - let outpoints = OutPointVec::from_slice(dep.data.as_bytes()).unwrap(); - let outpoints: Vec = outpoints.into_iter().collect(); - let resolved_cell_deps: Vec<_> = outpoints - .into_iter() - .map(|o| ckb_jsonrpc_types::CellDep { out_point: o.into(), dep_type: ckb_jsonrpc_types::DepType::Code }) - .collect(); - real_cell_deps.extend(resolved_cell_deps); - } - } - let compare = |a: &ckb_jsonrpc_types::CellDep, b: &ckb_jsonrpc_types::CellDep| { - let l = serde_json::to_string(a).unwrap(); - let r = serde_json::to_string(b).unwrap(); - l.cmp(&r) - }; - mock_cell_deps.sort_by(compare); - real_cell_deps.sort_by(compare); - if mock_cell_deps != real_cell_deps { - return Err(String::from("Precheck: celldeps is mismatched")); - } - let mock_inputs: Vec<_> = tx.mock_info.inputs.iter().map(|i| i.input.clone()).collect(); - let real_inputs: Vec<_> = tx.tx.inputs.clone(); - if mock_inputs != real_inputs { - return Err(String::from("Precheck: inputs is mismatched")); - } - let mock_header_deps: Vec<_> = tx.mock_info.header_deps.iter().map(|h| h.hash.clone()).collect(); - let read_header_deps: Vec<_> = tx.tx.header_deps.clone(); - if mock_header_deps != read_header_deps { - return Err(String::from("Precheck: header deps is mismatched")); - } - Ok(()) -} From 845a8429674a24fdca92dcc338db34f0a1dd77de Mon Sep 17 00:00:00 2001 From: mohanson Date: Mon, 10 Feb 2025 17:41:44 +0800 Subject: [PATCH 2/4] Return errors instead of exit --- ckb-debugger/src/analyzer.rs | 341 +++++++++++++++------------ ckb-debugger/src/main.rs | 4 +- ckb-vm-signal-profiler/src/frames.rs | 14 +- 3 files changed, 192 insertions(+), 167 deletions(-) diff --git a/ckb-debugger/src/analyzer.rs b/ckb-debugger/src/analyzer.rs index fb1a6e2..8348caa 100644 --- a/ckb-debugger/src/analyzer.rs +++ b/ckb-debugger/src/analyzer.rs @@ -1,7 +1,9 @@ +use std::fmt::Debug; + use ckb_types::prelude::Entity; -pub fn analyze(data: &str) { - prelude(data); +pub fn analyze(data: &str) -> Result<(), CheckError> { + prelude(data)?; let mock: ckb_mock_tx_types::ReprMockTransaction = serde_json::from_str(&data).unwrap(); for (i, e) in mock.mock_info.cell_deps.iter().enumerate() { @@ -22,305 +24,334 @@ pub fn analyze(data: &str) { path, &mock, &ckb_jsonrpc_types::CellDep { out_point: f.clone().into(), dep_type: ckb_jsonrpc_types::DepType::Code }, - ); + )?; } } for (i, e) in mock.tx.cell_deps.iter().enumerate() { let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("cell_deps")), Key::Index(i)]; - analyze_cell_dep(path, &mock, &e); + analyze_cell_dep(path, &mock, &e)?; } for (i, e) in mock.tx.header_deps.iter().enumerate() { let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("header_deps")), Key::Index(i)]; - analyze_header_dep(path, &mock, e); + analyze_header_dep(path, &mock, e)?; } for (i, e) in mock.tx.inputs.iter().enumerate() { let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("inputs")), Key::Index(i)]; - analyze_input(path, &mock, &e); + analyze_input(path, &mock, &e)?; } - analyze_output(vec![Key::Table(String::from("tx")), Key::Table(String::from("outputs"))], &mock); + analyze_output(vec![Key::Table(String::from("tx")), Key::Table(String::from("outputs"))], &mock)?; + Ok(()) } pub fn analyze_cell_dep( path: Vec, data: &ckb_mock_tx_types::ReprMockTransaction, cell_dep: &ckb_jsonrpc_types::CellDep, -) { +) -> Result<(), CheckError> { let cset: Vec = data.mock_info.cell_deps.iter().map(|e| e.cell_dep.clone()).collect(); if !cset.contains(&cell_dep) { - println!("Check Fail: {} used unprovided cell dep", keyfmt(path)); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} used unprovided cell dep", keyfmt(&path)))); } + Ok(()) } -pub fn analyze_header_dep(path: Vec, data: &ckb_mock_tx_types::ReprMockTransaction, header_dep: &ckb_types::H256) { +pub fn analyze_header_dep( + path: Vec, + data: &ckb_mock_tx_types::ReprMockTransaction, + header_dep: &ckb_types::H256, +) -> Result<(), CheckError> { let hset: Vec = data.mock_info.header_deps.iter().map(|e| e.hash.clone()).collect(); if !hset.contains(&header_dep) { - println!("Check Fail: {} used unprovided header dep", keyfmt(path)); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} used unprovided header dep", keyfmt(&path)))); } + Ok(()) } pub fn analyze_input( path: Vec, data: &ckb_mock_tx_types::ReprMockTransaction, input: &ckb_jsonrpc_types::CellInput, -) { +) -> Result<(), CheckError> { let iset: Vec = data.mock_info.inputs.iter().map(|e| e.input.clone()).collect(); if !iset.contains(&input) { - println!("Check Fail: {} used unprovided input", keyfmt(path)); + return Err(CheckError(format!("Check Fail: {} used unprovided input", keyfmt(&path)))); } + Ok(()) } -pub fn analyze_output(path: Vec, data: &ckb_mock_tx_types::ReprMockTransaction) { +pub fn analyze_output(path: Vec, data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> { if data.tx.outputs.len() != data.tx.outputs_data.len() { - println!("Check Fail: {} outputs and outputs_data are not one-to-one correspondence", keyfmt(path)); - std::process::exit(1); + return Err(CheckError(format!( + "Check Fail: {} outputs and outputs_data are not one-to-one correspondence", + keyfmt(&path) + ))); } + Ok(()) } -pub fn prelude(data: &str) { - let j: serde_json::Value = serde_json::from_str(data).unwrap(); - prelude_contains_key(vec![], &j, "mock_info"); - prelude_contains_key(vec![], &j, "tx"); - prelude_mock_info(keyadd_table(vec![], "mock_info"), j.as_object().unwrap().get("mock_info").unwrap()); - prelude_tx(keyadd_table(vec![], "tx"), j.as_object().unwrap().get("tx").unwrap()); +pub fn prelude(data: &str) -> Result<(), CheckError> { + let j: serde_json::Value = serde_json::from_str(data).map_err(|e| CheckError(e.to_string()))?; + prelude_contains_key(vec![], &j, "mock_info")?; + prelude_contains_key(vec![], &j, "tx")?; + prelude_mock_info(keyadd_table(vec![], "mock_info"), j.as_object().unwrap().get("mock_info").unwrap())?; + prelude_tx(keyadd_table(vec![], "tx"), j.as_object().unwrap().get("tx").unwrap())?; + Ok(()) } -pub fn prelude_cell_dep(path: Vec, data: &serde_json::Value) { - prelude_contains_key(path.clone(), &data, "out_point"); - prelude_contains_key(path.clone(), &data, "dep_type"); - prelude_out_point(keyadd_table(path.clone(), "out_point"), data.as_object().unwrap().get("out_point").unwrap()); - prelude_dep_type(keyadd_table(path.clone(), "dep_type"), data.as_object().unwrap().get("dep_type").unwrap()); +pub fn prelude_cell_dep(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + prelude_contains_key(path.clone(), &data, "out_point")?; + prelude_contains_key(path.clone(), &data, "dep_type")?; + prelude_out_point(keyadd_table(path.clone(), "out_point"), data.as_object().unwrap().get("out_point").unwrap())?; + prelude_dep_type(keyadd_table(path.clone(), "dep_type"), data.as_object().unwrap().get("dep_type").unwrap())?; + Ok(()) } -pub fn prelude_contains_key(path: Vec, data: &serde_json::Value, key: &str) { +pub fn prelude_contains_key(path: Vec, data: &serde_json::Value, key: &str) -> Result<(), CheckError> { + if !data.is_object() { + return Err(CheckError(format!("Check Fail: {} is not an object", keyfmt(&path)))); + } if !data.as_object().unwrap().contains_key(key) { - println!("Check Fail: {} missing members: {}", keyfmt(path), key); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} missing members: {}", keyfmt(&path), key))); } + Ok(()) } -pub fn prelude_dep_type(path: Vec, data: &serde_json::Value) { - if data.as_str().is_none() { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal dep type"); - std::process::exit(1); +pub fn prelude_dep_type(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + if !data.is_string() { + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal dep type"))); } if serde_json::from_str::(&format!("{:?}", &data.as_str().unwrap())).is_err() { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal dep type"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal dep type"))); } + Ok(()) } -pub fn prelude_hash(path: Vec, data: &serde_json::Value) { - prelude_hex(path.clone(), data); +pub fn prelude_hash(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + prelude_hex(path.clone(), data)?; if data.as_str().unwrap().len() != 66 { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal hash"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hash"))); } + Ok(()) } -pub fn prelude_hash_type(path: Vec, data: &serde_json::Value) { - if data.as_str().is_none() { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal hash type"); - std::process::exit(1); +pub fn prelude_hash_type(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + if !data.is_string() { + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hash type"))); } if serde_json::from_str::(&format!("{:?}", &data.as_str().unwrap())).is_err() { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal hash type"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hash type"))); } + Ok(()) } -pub fn prelude_hex(path: Vec, data: &serde_json::Value) { - if data.as_str().is_none() { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal hex string"); - std::process::exit(1); +pub fn prelude_hex(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + if !data.is_string() { + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hex string"))); } if !data.as_str().unwrap().starts_with("0x") { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal hex string"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hex string"))); } if hex::decode(&data.as_str().unwrap()[2..]).is_err() { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal hex string"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal hex string"))); } + Ok(()) } -pub fn prelude_input(path: Vec, data: &serde_json::Value) { - prelude_contains_key(path.clone(), &data, "since"); - prelude_contains_key(path.clone(), &data, "previous_output"); - prelude_u64(keyadd_table(path.clone(), "since"), data.as_object().unwrap().get("since").unwrap()); +pub fn prelude_input(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + prelude_contains_key(path.clone(), &data, "since")?; + prelude_contains_key(path.clone(), &data, "previous_output")?; + prelude_u64(keyadd_table(path.clone(), "since"), data.as_object().unwrap().get("since").unwrap())?; prelude_out_point( keyadd_table(path.clone(), "previous_output"), data.as_object().unwrap().get("previous_output").unwrap(), - ); + )?; + Ok(()) } -pub fn prelude_mock_info(path: Vec, data: &serde_json::Value) { - prelude_contains_key(path.clone(), &data, "inputs"); - prelude_contains_key(path.clone(), &data, "cell_deps"); - prelude_contains_key(path.clone(), &data, "header_deps"); +pub fn prelude_mock_info(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + prelude_contains_key(path.clone(), &data, "inputs")?; + prelude_contains_key(path.clone(), &data, "cell_deps")?; + prelude_contains_key(path.clone(), &data, "header_deps")?; for (i, e) in data.as_object().unwrap().get("inputs").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "inputs"), i); - prelude_contains_key(path.clone(), e, "input"); - prelude_contains_key(path.clone(), e, "output"); - prelude_contains_key(path.clone(), e, "data"); - prelude_contains_key(path.clone(), e, "header"); - prelude_input(keyadd_table(path.clone(), "input"), e.as_object().unwrap().get("input").unwrap()); - prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap()); - prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap()); - prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap()); + prelude_contains_key(path.clone(), e, "input")?; + prelude_contains_key(path.clone(), e, "output")?; + prelude_contains_key(path.clone(), e, "data")?; + prelude_contains_key(path.clone(), e, "header")?; + prelude_input(keyadd_table(path.clone(), "input"), e.as_object().unwrap().get("input").unwrap())?; + prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap())?; + prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap())?; + prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap())?; } for (i, e) in data.as_object().unwrap().get("cell_deps").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "cell_deps"), i); - prelude_contains_key(path.clone(), e, "cell_dep"); - prelude_contains_key(path.clone(), e, "output"); - prelude_contains_key(path.clone(), e, "data"); - prelude_contains_key(path.clone(), e, "header"); - prelude_cell_dep(keyadd_table(path.clone(), "cell_dep"), e.as_object().unwrap().get("cell_dep").unwrap()); - prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap()); - prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap()); - prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap()); + prelude_contains_key(path.clone(), e, "cell_dep")?; + prelude_contains_key(path.clone(), e, "output")?; + prelude_contains_key(path.clone(), e, "data")?; + prelude_contains_key(path.clone(), e, "header")?; + prelude_cell_dep(keyadd_table(path.clone(), "cell_dep"), e.as_object().unwrap().get("cell_dep").unwrap())?; + prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap())?; + prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap())?; + prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap())?; } for (i, e) in data.as_object().unwrap().get("header_deps").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "header_deps"), i); - prelude_contains_key(path.clone(), e, "compact_target"); - prelude_contains_key(path.clone(), e, "dao"); - prelude_contains_key(path.clone(), e, "epoch"); - prelude_contains_key(path.clone(), e, "extra_hash"); - prelude_contains_key(path.clone(), e, "hash"); - prelude_contains_key(path.clone(), e, "nonce"); - prelude_contains_key(path.clone(), e, "number"); - prelude_contains_key(path.clone(), e, "parent_hash"); - prelude_contains_key(path.clone(), e, "proposals_hash"); - prelude_contains_key(path.clone(), e, "timestamp"); - prelude_contains_key(path.clone(), e, "transactions_root"); - prelude_contains_key(path.clone(), e, "version"); + prelude_contains_key(path.clone(), e, "compact_target")?; + prelude_contains_key(path.clone(), e, "dao")?; + prelude_contains_key(path.clone(), e, "epoch")?; + prelude_contains_key(path.clone(), e, "extra_hash")?; + prelude_contains_key(path.clone(), e, "hash")?; + prelude_contains_key(path.clone(), e, "nonce")?; + prelude_contains_key(path.clone(), e, "number")?; + prelude_contains_key(path.clone(), e, "parent_hash")?; + prelude_contains_key(path.clone(), e, "proposals_hash")?; + prelude_contains_key(path.clone(), e, "timestamp")?; + prelude_contains_key(path.clone(), e, "transactions_root")?; + prelude_contains_key(path.clone(), e, "version")?; prelude_u32( keyadd_table(path.clone(), "compact_target"), e.as_object().unwrap().get("compact_target").unwrap(), - ); - prelude_hash(keyadd_table(path.clone(), "dao"), e.as_object().unwrap().get("dao").unwrap()); - prelude_u64(keyadd_table(path.clone(), "epoch"), e.as_object().unwrap().get("epoch").unwrap()); - prelude_hash(keyadd_table(path.clone(), "extra_hash"), e.as_object().unwrap().get("extra_hash").unwrap()); - prelude_hash(keyadd_table(path.clone(), "hash"), e.as_object().unwrap().get("hash").unwrap()); - prelude_u128(keyadd_table(path.clone(), "nonce"), e.as_object().unwrap().get("nonce").unwrap()); - prelude_u64(keyadd_table(path.clone(), "number"), e.as_object().unwrap().get("number").unwrap()); - prelude_hash(keyadd_table(path.clone(), "parent_hash"), e.as_object().unwrap().get("parent_hash").unwrap()); + )?; + prelude_hash(keyadd_table(path.clone(), "dao"), e.as_object().unwrap().get("dao").unwrap())?; + prelude_u64(keyadd_table(path.clone(), "epoch"), e.as_object().unwrap().get("epoch").unwrap())?; + prelude_hash(keyadd_table(path.clone(), "extra_hash"), e.as_object().unwrap().get("extra_hash").unwrap())?; + prelude_hash(keyadd_table(path.clone(), "hash"), e.as_object().unwrap().get("hash").unwrap())?; + prelude_u128(keyadd_table(path.clone(), "nonce"), e.as_object().unwrap().get("nonce").unwrap())?; + prelude_u64(keyadd_table(path.clone(), "number"), e.as_object().unwrap().get("number").unwrap())?; + prelude_hash(keyadd_table(path.clone(), "parent_hash"), e.as_object().unwrap().get("parent_hash").unwrap())?; prelude_hash( keyadd_table(path.clone(), "proposals_hash"), e.as_object().unwrap().get("proposals_hash").unwrap(), - ); - prelude_u64(keyadd_table(path.clone(), "timestamp"), e.as_object().unwrap().get("timestamp").unwrap()); + )?; + prelude_u64(keyadd_table(path.clone(), "timestamp"), e.as_object().unwrap().get("timestamp").unwrap())?; prelude_hash( keyadd_table(path.clone(), "transactions_root"), e.as_object().unwrap().get("transactions_root").unwrap(), - ); - prelude_u32(keyadd_table(path.clone(), "version"), e.as_object().unwrap().get("version").unwrap()); + )?; + prelude_u32(keyadd_table(path.clone(), "version"), e.as_object().unwrap().get("version").unwrap())?; } + Ok(()) } -pub fn prelude_out_point(path: Vec, data: &serde_json::Value) { - prelude_contains_key(path.clone(), data, "tx_hash"); - prelude_contains_key(path.clone(), data, "index"); - prelude_hash(keyadd_table(path.clone(), "tx_hash"), data.as_object().unwrap().get("tx_hash").unwrap()); - prelude_u32(keyadd_table(path.clone(), "index"), data.as_object().unwrap().get("index").unwrap()); +pub fn prelude_out_point(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + prelude_contains_key(path.clone(), data, "tx_hash")?; + prelude_contains_key(path.clone(), data, "index")?; + prelude_hash(keyadd_table(path.clone(), "tx_hash"), data.as_object().unwrap().get("tx_hash").unwrap())?; + prelude_u32(keyadd_table(path.clone(), "index"), data.as_object().unwrap().get("index").unwrap())?; + Ok(()) } -pub fn prelude_output(path: Vec, data: &serde_json::Value) { - prelude_contains_key(path.clone(), data, "capacity"); - prelude_contains_key(path.clone(), data, "lock"); - prelude_contains_key(path.clone(), data, "type"); - prelude_u64(keyadd_table(path.clone(), "capacity"), data.as_object().unwrap().get("capacity").unwrap()); - prelude_script(keyadd_table(path.clone(), "lock"), data.as_object().unwrap().get("lock").unwrap()); +pub fn prelude_output(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + prelude_contains_key(path.clone(), data, "capacity")?; + prelude_contains_key(path.clone(), data, "lock")?; + prelude_contains_key(path.clone(), data, "type")?; + prelude_u64(keyadd_table(path.clone(), "capacity"), data.as_object().unwrap().get("capacity").unwrap())?; + prelude_script(keyadd_table(path.clone(), "lock"), data.as_object().unwrap().get("lock").unwrap())?; if !data.as_object().unwrap().get("type").unwrap().is_null() { - prelude_script(keyadd_table(path.clone(), "type"), data.as_object().unwrap().get("type").unwrap()); + prelude_script(keyadd_table(path.clone(), "type"), data.as_object().unwrap().get("type").unwrap())?; } + Ok(()) } -pub fn prelude_script(path: Vec, data: &serde_json::Value) { - prelude_contains_key(path.clone(), data, "code_hash"); - prelude_contains_key(path.clone(), data, "hash_type"); - prelude_contains_key(path.clone(), data, "args"); - prelude_hash(keyadd_table(path.clone(), "code_hash"), data.as_object().unwrap().get("code_hash").unwrap()); - prelude_hash_type(keyadd_table(path.clone(), "hash_type"), data.as_object().unwrap().get("hash_type").unwrap()); - prelude_hex(keyadd_table(path.clone(), "args"), data.as_object().unwrap().get("args").unwrap()); +pub fn prelude_script(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + prelude_contains_key(path.clone(), data, "code_hash")?; + prelude_contains_key(path.clone(), data, "hash_type")?; + prelude_contains_key(path.clone(), data, "args")?; + prelude_hash(keyadd_table(path.clone(), "code_hash"), data.as_object().unwrap().get("code_hash").unwrap())?; + prelude_hash_type(keyadd_table(path.clone(), "hash_type"), data.as_object().unwrap().get("hash_type").unwrap())?; + prelude_hex(keyadd_table(path.clone(), "args"), data.as_object().unwrap().get("args").unwrap())?; + Ok(()) } -pub fn prelude_tx(path: Vec, data: &serde_json::Value) { - prelude_contains_key(path.clone(), &data, "version"); - prelude_contains_key(path.clone(), &data, "cell_deps"); - prelude_contains_key(path.clone(), &data, "header_deps"); - prelude_contains_key(path.clone(), &data, "inputs"); - prelude_contains_key(path.clone(), &data, "outputs"); - prelude_contains_key(path.clone(), &data, "outputs_data"); - prelude_contains_key(path.clone(), &data, "witnesses"); - prelude_u32(keyadd_table(path.clone(), "version"), data.as_object().unwrap().get("version").unwrap()); +pub fn prelude_tx(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { + prelude_contains_key(path.clone(), &data, "version")?; + prelude_contains_key(path.clone(), &data, "cell_deps")?; + prelude_contains_key(path.clone(), &data, "header_deps")?; + prelude_contains_key(path.clone(), &data, "inputs")?; + prelude_contains_key(path.clone(), &data, "outputs")?; + prelude_contains_key(path.clone(), &data, "outputs_data")?; + prelude_contains_key(path.clone(), &data, "witnesses")?; + prelude_u32(keyadd_table(path.clone(), "version"), data.as_object().unwrap().get("version").unwrap())?; for (i, e) in data.as_object().unwrap().get("cell_deps").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "cell_deps"), i); - prelude_cell_dep(path, e); + prelude_cell_dep(path, e)?; } for (i, e) in data.as_object().unwrap().get("header_deps").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "header_deps"), i); - prelude_hash(path, e); + prelude_hash(path, e)?; } for (i, e) in data.as_object().unwrap().get("inputs").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "inputs"), i); - prelude_input(path, e); + prelude_input(path, e)?; } for (i, e) in data.as_object().unwrap().get("outputs").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "outputs"), i); - prelude_output(path, e); + prelude_output(path, e)?; } for (i, e) in data.as_object().unwrap().get("outputs_data").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "outputs_data"), i); - prelude_hex(path, e); + prelude_hex(path, e)?; } for (i, e) in data.as_object().unwrap().get("witnesses").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "witnesses"), i); - prelude_hex(path, e); + prelude_hex(path, e)?; } + Ok(()) } -pub fn prelude_u128(path: Vec, data: &serde_json::Value) { +pub fn prelude_u128(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { if !data.is_string() || !data.as_str().unwrap().starts_with("0x") { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal u128"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u128"))); } if u128::from_str_radix(&data.as_str().unwrap()[2..], 16).is_err() { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal u128"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u128"))); } + Ok(()) } -pub fn prelude_u32(path: Vec, data: &serde_json::Value) { +pub fn prelude_u32(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { if !data.is_string() || !data.as_str().unwrap().starts_with("0x") { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal u32"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u32"))); } if u32::from_str_radix(&data.as_str().unwrap()[2..], 16).is_err() { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal u32"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u32"))); } + Ok(()) } -pub fn prelude_u64(path: Vec, data: &serde_json::Value) { +pub fn prelude_u64(path: Vec, data: &serde_json::Value) -> Result<(), CheckError> { if !data.is_string() || !data.as_str().unwrap().starts_with("0x") { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal u64"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u64"))); } if u64::from_str_radix(&data.as_str().unwrap()[2..], 16).is_err() { - println!("Check Fail: {} {}", keyfmt(path), "is not a legal u64"); - std::process::exit(1); + return Err(CheckError(format!("Check Fail: {} {}", keyfmt(&path), "is not a legal u64"))); + } + Ok(()) +} + +pub struct CheckError(String); + +impl std::fmt::Debug for CheckError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0) + } +} + +impl std::fmt::Display for CheckError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0) } } +impl std::error::Error for CheckError {} + #[derive(Clone, Debug)] pub enum Key { Table(String), Index(usize), } -pub fn keyfmt(key: Vec) -> String { +pub fn keyfmt(key: &[Key]) -> String { let mut s = String::from("json"); for e in key { match e { diff --git a/ckb-debugger/src/main.rs b/ckb-debugger/src/main.rs index d1ead76..8b3862b 100644 --- a/ckb-debugger/src/main.rs +++ b/ckb-debugger/src/main.rs @@ -214,7 +214,7 @@ fn main() -> Result<(), Box> { Some("-") => { let mut buf = String::new(); std::io::stdin().read_to_string(&mut buf)?; - analyze(&buf); + analyze(&buf)?; let repr_mock_tx: ReprMockTransaction = serde_json::from_str(&buf)?; repr_mock_tx.into() } @@ -222,7 +222,7 @@ fn main() -> Result<(), Box> { let buf = std::fs::read_to_string(doc)?; let mut mock_tx_embed = Embed::new(PathBuf::from(doc.to_string()), buf.clone()); let buf = mock_tx_embed.replace_all(); - analyze(&buf); + analyze(&buf)?; let repr_mock_tx: ReprMockTransaction = serde_json::from_str(&buf)?; repr_mock_tx.into() } diff --git a/ckb-vm-signal-profiler/src/frames.rs b/ckb-vm-signal-profiler/src/frames.rs index 77f2248..20a4f0d 100644 --- a/ckb-vm-signal-profiler/src/frames.rs +++ b/ckb-vm-signal-profiler/src/frames.rs @@ -110,16 +110,10 @@ impl Report { }; samples.push(sample); } - let samples_value = protos::ValueType { - type_: strings[SAMPLES] as i64, - unit: strings[COUNT] as i64, - ..Default::default() - }; - let time_value = protos::ValueType { - type_: strings[CPU] as i64, - unit: strings[NANOSECONDS] as i64, - ..Default::default() - }; + let samples_value = + protos::ValueType { type_: strings[SAMPLES] as i64, unit: strings[COUNT] as i64, ..Default::default() }; + let time_value = + protos::ValueType { type_: strings[CPU] as i64, unit: strings[NANOSECONDS] as i64, ..Default::default() }; let profile = protos::Profile { sample_type: vec![samples_value, time_value.clone()].into(), sample: samples.into(), From 56d1d4ebd9b5bada48552f02aaec989453a761dc Mon Sep 17 00:00:00 2001 From: mohanson Date: Wed, 12 Feb 2025 10:36:31 +0800 Subject: [PATCH 3/4] Make header optional --- ckb-debugger/examples/exec.json | 9 +++------ ckb-debugger/examples/spawn.json | 9 +++------ ckb-debugger/src/analyzer.rs | 10 ++++++---- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/ckb-debugger/examples/exec.json b/ckb-debugger/examples/exec.json index bc3f369..4bf6d9f 100644 --- a/ckb-debugger/examples/exec.json +++ b/ckb-debugger/examples/exec.json @@ -18,8 +18,7 @@ }, "type": null }, - "data": "0x", - "header": "0x0000000000000000000000000000000000000000000000000000000000000000" + "data": "0x" } ], "cell_deps": [ @@ -40,8 +39,7 @@ }, "type": null }, - "data": "0x{{ data exec_caller }}", - "header": "0x0000000000000000000000000000000000000000000000000000000000000000" + "data": "0x{{ data exec_caller }}" }, { "cell_dep": { @@ -60,8 +58,7 @@ }, "type": null }, - "data": "0x{{ data exec_callee }}", - "header": "0x0000000000000000000000000000000000000000000000000000000000000000" + "data": "0x{{ data exec_callee }}" } ], "header_deps": [] diff --git a/ckb-debugger/examples/spawn.json b/ckb-debugger/examples/spawn.json index cad55c8..6b290f6 100644 --- a/ckb-debugger/examples/spawn.json +++ b/ckb-debugger/examples/spawn.json @@ -18,8 +18,7 @@ }, "type": null }, - "data": "0x", - "header": "0x0000000000000000000000000000000000000000000000000000000000000000" + "data": "0x" } ], "cell_deps": [ @@ -40,8 +39,7 @@ }, "type": null }, - "data": "0x{{ data spawn_caller_strcat }}", - "header": "0x0000000000000000000000000000000000000000000000000000000000000000" + "data": "0x{{ data spawn_caller_strcat }}" }, { "cell_dep": { @@ -60,8 +58,7 @@ }, "type": null }, - "data": "0x{{ data spawn_callee_strcat }}", - "header": "0x0000000000000000000000000000000000000000000000000000000000000000" + "data": "0x{{ data spawn_callee_strcat }}" } ], "header_deps": [] diff --git a/ckb-debugger/src/analyzer.rs b/ckb-debugger/src/analyzer.rs index 8348caa..dfba194 100644 --- a/ckb-debugger/src/analyzer.rs +++ b/ckb-debugger/src/analyzer.rs @@ -177,22 +177,24 @@ pub fn prelude_mock_info(path: Vec, data: &serde_json::Value) -> Result<(), prelude_contains_key(path.clone(), e, "input")?; prelude_contains_key(path.clone(), e, "output")?; prelude_contains_key(path.clone(), e, "data")?; - prelude_contains_key(path.clone(), e, "header")?; prelude_input(keyadd_table(path.clone(), "input"), e.as_object().unwrap().get("input").unwrap())?; prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap())?; prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap())?; - prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap())?; + if e.as_object().unwrap().contains_key("header") && !e.as_object().unwrap().get("header").unwrap().is_null() { + prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap())?; + } } for (i, e) in data.as_object().unwrap().get("cell_deps").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "cell_deps"), i); prelude_contains_key(path.clone(), e, "cell_dep")?; prelude_contains_key(path.clone(), e, "output")?; prelude_contains_key(path.clone(), e, "data")?; - prelude_contains_key(path.clone(), e, "header")?; prelude_cell_dep(keyadd_table(path.clone(), "cell_dep"), e.as_object().unwrap().get("cell_dep").unwrap())?; prelude_output(keyadd_table(path.clone(), "output"), e.as_object().unwrap().get("output").unwrap())?; prelude_hex(keyadd_table(path.clone(), "data"), e.as_object().unwrap().get("data").unwrap())?; - prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap())?; + if e.as_object().unwrap().contains_key("header") && !e.as_object().unwrap().get("header").unwrap().is_null() { + prelude_hash(keyadd_table(path.clone(), "header"), e.as_object().unwrap().get("header").unwrap())?; + } } for (i, e) in data.as_object().unwrap().get("header_deps").unwrap().as_array().unwrap().iter().enumerate() { let path = keyadd_index(keyadd_table(path.clone(), "header_deps"), i); From 3d4cf3fb89824f581ad8c8ebc5866cfdfc758b23 Mon Sep 17 00:00:00 2001 From: mohanson Date: Wed, 12 Feb 2025 12:16:21 +0800 Subject: [PATCH 4/4] Disable unused cell_dep, input and header --- ckb-debugger/src/analyzer.rs | 122 +++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 54 deletions(-) diff --git a/ckb-debugger/src/analyzer.rs b/ckb-debugger/src/analyzer.rs index dfba194..63147c7 100644 --- a/ckb-debugger/src/analyzer.rs +++ b/ckb-debugger/src/analyzer.rs @@ -5,82 +5,96 @@ use ckb_types::prelude::Entity; pub fn analyze(data: &str) -> Result<(), CheckError> { prelude(data)?; let mock: ckb_mock_tx_types::ReprMockTransaction = serde_json::from_str(&data).unwrap(); + analyze_cell_dep(&mock)?; + analyze_header_dep(&mock)?; + analyze_input(&mock)?; + analyze_output(&mock)?; + Ok(()) +} - for (i, e) in mock.mock_info.cell_deps.iter().enumerate() { - if e.cell_dep.dep_type == ckb_jsonrpc_types::DepType::Code { +pub fn analyze_cell_dep(data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> { + let cset: Vec = data.mock_info.cell_deps.iter().map(|e| e.cell_dep.clone()).collect(); + for (i, e) in data.tx.cell_deps.iter().enumerate() { + if !cset.contains(&e) { + let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("cell_deps")), Key::Index(i)]; + return Err(CheckError(format!("Check Fail: {} used unprovided cell dep", keyfmt(&path)))); + } + } + let mut ccnt = vec![0u8; cset.len()]; + for (_, e) in data.tx.cell_deps.iter().enumerate() { + let i = cset.iter().position(|r| r == e).unwrap(); + ccnt[i] += 1; + if data.mock_info.cell_deps[i].cell_dep.dep_type == ckb_jsonrpc_types::DepType::Code { continue; } - let outpoints = ckb_types::packed::OutPointVec::from_slice(e.data.as_bytes()).unwrap(); + let outpoints = + ckb_types::packed::OutPointVec::from_slice(data.mock_info.cell_deps[i].data.as_bytes()).unwrap(); let outpoints: Vec = outpoints.into_iter().collect(); for (j, f) in outpoints.iter().enumerate() { - let path = vec![ - Key::Table(String::from("mock_info")), - Key::Table(String::from("cell_deps")), - Key::Index(i), - Key::Table(String::from("data")), - Key::Index(j), - ]; - analyze_cell_dep( - path, - &mock, - &ckb_jsonrpc_types::CellDep { out_point: f.clone().into(), dep_type: ckb_jsonrpc_types::DepType::Code }, - )?; + let cdep = + ckb_jsonrpc_types::CellDep { out_point: f.clone().into(), dep_type: ckb_jsonrpc_types::DepType::Code }; + if !cset.contains(&cdep) { + let path = vec![ + Key::Table(String::from("mock_info")), + Key::Table(String::from("cell_deps")), + Key::Index(i), + Key::Table(String::from("data")), + Key::Index(j), + ]; + return Err(CheckError(format!("Check Fail: {} used unprovided cell dep", keyfmt(&path)))); + } + let k = cset.iter().position(|r| r == &cdep).unwrap(); + ccnt[k] += 1; } } - for (i, e) in mock.tx.cell_deps.iter().enumerate() { - let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("cell_deps")), Key::Index(i)]; - analyze_cell_dep(path, &mock, &e)?; - } - for (i, e) in mock.tx.header_deps.iter().enumerate() { - let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("header_deps")), Key::Index(i)]; - analyze_header_dep(path, &mock, e)?; - } - for (i, e) in mock.tx.inputs.iter().enumerate() { - let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("inputs")), Key::Index(i)]; - analyze_input(path, &mock, &e)?; - } - analyze_output(vec![Key::Table(String::from("tx")), Key::Table(String::from("outputs"))], &mock)?; - Ok(()) -} - -pub fn analyze_cell_dep( - path: Vec, - data: &ckb_mock_tx_types::ReprMockTransaction, - cell_dep: &ckb_jsonrpc_types::CellDep, -) -> Result<(), CheckError> { - let cset: Vec = data.mock_info.cell_deps.iter().map(|e| e.cell_dep.clone()).collect(); - if !cset.contains(&cell_dep) { - return Err(CheckError(format!("Check Fail: {} used unprovided cell dep", keyfmt(&path)))); + for (i, e) in ccnt.iter().enumerate() { + if *e != 0 { + continue; + } + let path = vec![Key::Table(String::from("mock_info")), Key::Table(String::from("cell_deps")), Key::Index(i)]; + return Err(CheckError(format!("Check Fail: {} unused", keyfmt(&path)))); } Ok(()) } -pub fn analyze_header_dep( - path: Vec, - data: &ckb_mock_tx_types::ReprMockTransaction, - header_dep: &ckb_types::H256, -) -> Result<(), CheckError> { +pub fn analyze_header_dep(data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> { let hset: Vec = data.mock_info.header_deps.iter().map(|e| e.hash.clone()).collect(); - if !hset.contains(&header_dep) { - return Err(CheckError(format!("Check Fail: {} used unprovided header dep", keyfmt(&path)))); + for (i, e) in data.tx.header_deps.iter().enumerate() { + if !hset.contains(&e) { + let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("header_deps")), Key::Index(i)]; + return Err(CheckError(format!("Check Fail: {} used unprovided header dep", keyfmt(&path)))); + } + } + for (i, e) in hset.iter().enumerate() { + if !data.tx.header_deps.contains(&e) { + let path = + vec![Key::Table(String::from("mock_info")), Key::Table(String::from("header_deps")), Key::Index(i)]; + return Err(CheckError(format!("Check Fail: {} unused", keyfmt(&path)))); + } } Ok(()) } -pub fn analyze_input( - path: Vec, - data: &ckb_mock_tx_types::ReprMockTransaction, - input: &ckb_jsonrpc_types::CellInput, -) -> Result<(), CheckError> { +pub fn analyze_input(data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> { let iset: Vec = data.mock_info.inputs.iter().map(|e| e.input.clone()).collect(); - if !iset.contains(&input) { - return Err(CheckError(format!("Check Fail: {} used unprovided input", keyfmt(&path)))); + for (i, e) in data.tx.inputs.iter().enumerate() { + if !iset.contains(&e) { + let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("inputs")), Key::Index(i)]; + return Err(CheckError(format!("Check Fail: {} used unprovided input", keyfmt(&path)))); + } + } + for (i, e) in iset.iter().enumerate() { + if !data.tx.inputs.contains(&e) { + let path = vec![Key::Table(String::from("mock_info")), Key::Table(String::from("inputs")), Key::Index(i)]; + return Err(CheckError(format!("Check Fail: {} unused", keyfmt(&path)))); + } } Ok(()) } -pub fn analyze_output(path: Vec, data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> { +pub fn analyze_output(data: &ckb_mock_tx_types::ReprMockTransaction) -> Result<(), CheckError> { if data.tx.outputs.len() != data.tx.outputs_data.len() { + let path = vec![Key::Table(String::from("tx")), Key::Table(String::from("outputs"))]; return Err(CheckError(format!( "Check Fail: {} outputs and outputs_data are not one-to-one correspondence", keyfmt(&path)