Skip to content

Commit 765ea25

Browse files
xxuejiedoitian
authored andcommitted
feat: add a new VM syscall to allow printing debug infos from contract
This could be very handy when debugging a contract
1 parent 024177d commit 765ea25

3 files changed

Lines changed: 51 additions & 4 deletions

File tree

script/src/syscalls/debugger.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use syscalls::DEBUG_PRINT_SYSCALL_NUMBER;
2+
use vm::{CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A7};
3+
4+
pub struct Debugger<'a> {
5+
prefix: &'a str,
6+
}
7+
8+
impl<'a> Debugger<'a> {
9+
pub fn new(prefix: &'a str) -> Debugger<'a> {
10+
Debugger { prefix }
11+
}
12+
}
13+
14+
impl<'a, R: Register, M: Memory> Syscalls<R, M> for Debugger<'a> {
15+
fn initialize(&mut self, _machine: &mut CoreMachine<R, M>) -> Result<(), VMError> {
16+
Ok(())
17+
}
18+
19+
fn ecall(&mut self, machine: &mut CoreMachine<R, M>) -> Result<bool, VMError> {
20+
let number = machine.registers()[A7].to_u64();
21+
if number != DEBUG_PRINT_SYSCALL_NUMBER {
22+
return Ok(false);
23+
}
24+
25+
let mut addr = machine.registers()[A0].to_usize();
26+
let mut buffer = Vec::new();
27+
28+
loop {
29+
let byte = machine.memory_mut().load8(addr)?;
30+
if byte == 0 {
31+
break;
32+
}
33+
buffer.push(byte);
34+
addr += 1;
35+
}
36+
37+
let s = String::from_utf8(buffer).map_err(|_| VMError::ParseError)?;
38+
debug!(target: "script", "{} DEBUG OUTPUT: {}", self.prefix, s);
39+
Ok(true)
40+
}
41+
}

script/src/syscalls/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
mod builder;
2+
mod debugger;
23
mod fetch_script_hash;
34
mod mmap_cell;
45
mod mmap_tx;
56

67
pub use self::builder::build_tx;
8+
pub use self::debugger::Debugger;
79
pub use self::fetch_script_hash::FetchScriptHash;
810
pub use self::mmap_cell::MmapCell;
911
pub use self::mmap_tx::MmapTx;
@@ -17,6 +19,7 @@ pub const ITEM_MISSING: u8 = 2;
1719
pub const MMAP_TX_SYSCALL_NUMBER: u64 = 2049;
1820
pub const MMAP_CELL_SYSCALL_NUMBER: u64 = 2050;
1921
pub const FETCH_SCRIPT_HASH_SYSCALL_NUMBER: u64 = 2051;
22+
pub const DEBUG_PRINT_SYSCALL_NUMBER: u64 = 2177;
2023

2124
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
2225
pub enum Mode {

script/src/verify.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use core::script::Script;
55
use core::transaction::{CellInput, CellOutput};
66
use flatbuffers::FlatBufferBuilder;
77
use fnv::FnvHashMap;
8-
use syscalls::{build_tx, FetchScriptHash, MmapCell, MmapTx};
8+
use syscalls::{build_tx, Debugger, FetchScriptHash, MmapCell, MmapTx};
99
use vm::{DefaultMachine, SparseMemory};
1010

1111
// This struct leverages CKB VM to verify transaction inputs.
@@ -85,7 +85,7 @@ impl<'a> TransactionScriptsVerifier<'a> {
8585
Err(ScriptError::NoScript)
8686
}
8787

88-
pub fn verify_script(&self, script: &Script) -> Result<(), ScriptError> {
88+
pub fn verify_script(&self, script: &Script, prefix: &str) -> Result<(), ScriptError> {
8989
self.extract_script(script).and_then(|script_binary| {
9090
let mut args = vec![b"verify".to_vec()];
9191
args.extend_from_slice(&script.signed_args.as_slice());
@@ -95,6 +95,7 @@ impl<'a> TransactionScriptsVerifier<'a> {
9595
machine.add_syscall_module(Box::new(self.build_mmap_tx()));
9696
machine.add_syscall_module(Box::new(self.build_mmap_cell()));
9797
machine.add_syscall_module(Box::new(self.build_fetch_script_hash()));
98+
machine.add_syscall_module(Box::new(Debugger::new(prefix)));
9899
machine
99100
.run(script_binary, &args)
100101
.map_err(ScriptError::VMError)
@@ -110,14 +111,16 @@ impl<'a> TransactionScriptsVerifier<'a> {
110111

111112
pub fn verify(&self) -> Result<(), ScriptError> {
112113
for (i, input) in self.inputs.iter().enumerate() {
113-
self.verify_script(&input.unlock).map_err(|e| {
114+
let prefix = format!("Transaction {}, input {}", self.hash, i);
115+
self.verify_script(&input.unlock, &prefix).map_err(|e| {
114116
info!(target: "script", "Error validating input {} of transaction {}: {:?}", i, self.hash, e);
115117
e
116118
})?;
117119
}
118120
for (i, output) in self.outputs.iter().enumerate() {
119121
if let Some(ref contract) = output.contract {
120-
self.verify_script(contract).map_err(|e| {
122+
let prefix = format!("Transaction {}, output {}", self.hash, i);
123+
self.verify_script(contract, &prefix).map_err(|e| {
121124
info!(target: "script", "Error validating output {} of transaction {}: {:?}", i, self.hash, e);
122125
e
123126
})?;

0 commit comments

Comments
 (0)