Skip to content

Commit

Permalink
feat: add a new VM syscall to allow printing debug infos from contract
Browse files Browse the repository at this point in the history
This could be very handy when debugging a contract
  • Loading branch information
xxuejie authored and doitian committed Nov 19, 2018
1 parent 024177d commit 765ea25
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
41 changes: 41 additions & 0 deletions script/src/syscalls/debugger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use syscalls::DEBUG_PRINT_SYSCALL_NUMBER;
use vm::{CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A7};

pub struct Debugger<'a> {
prefix: &'a str,
}

impl<'a> Debugger<'a> {
pub fn new(prefix: &'a str) -> Debugger<'a> {
Debugger { prefix }
}
}

impl<'a, R: Register, M: Memory> Syscalls<R, M> for Debugger<'a> {
fn initialize(&mut self, _machine: &mut CoreMachine<R, M>) -> Result<(), VMError> {
Ok(())
}

fn ecall(&mut self, machine: &mut CoreMachine<R, M>) -> Result<bool, VMError> {
let number = machine.registers()[A7].to_u64();
if number != DEBUG_PRINT_SYSCALL_NUMBER {
return Ok(false);
}

let mut addr = machine.registers()[A0].to_usize();
let mut buffer = Vec::new();

loop {
let byte = machine.memory_mut().load8(addr)?;
if byte == 0 {
break;
}
buffer.push(byte);
addr += 1;
}

let s = String::from_utf8(buffer).map_err(|_| VMError::ParseError)?;
debug!(target: "script", "{} DEBUG OUTPUT: {}", self.prefix, s);
Ok(true)
}
}
3 changes: 3 additions & 0 deletions script/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod builder;
mod debugger;
mod fetch_script_hash;
mod mmap_cell;
mod mmap_tx;

pub use self::builder::build_tx;
pub use self::debugger::Debugger;
pub use self::fetch_script_hash::FetchScriptHash;
pub use self::mmap_cell::MmapCell;
pub use self::mmap_tx::MmapTx;
Expand All @@ -17,6 +19,7 @@ pub const ITEM_MISSING: u8 = 2;
pub const MMAP_TX_SYSCALL_NUMBER: u64 = 2049;
pub const MMAP_CELL_SYSCALL_NUMBER: u64 = 2050;
pub const FETCH_SCRIPT_HASH_SYSCALL_NUMBER: u64 = 2051;
pub const DEBUG_PRINT_SYSCALL_NUMBER: u64 = 2177;

#[derive(Debug, PartialEq, Clone, Copy, Eq)]
pub enum Mode {
Expand Down
11 changes: 7 additions & 4 deletions script/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::script::Script;
use core::transaction::{CellInput, CellOutput};
use flatbuffers::FlatBufferBuilder;
use fnv::FnvHashMap;
use syscalls::{build_tx, FetchScriptHash, MmapCell, MmapTx};
use syscalls::{build_tx, Debugger, FetchScriptHash, MmapCell, MmapTx};
use vm::{DefaultMachine, SparseMemory};

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

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

pub fn verify(&self) -> Result<(), ScriptError> {
for (i, input) in self.inputs.iter().enumerate() {
self.verify_script(&input.unlock).map_err(|e| {
let prefix = format!("Transaction {}, input {}", self.hash, i);
self.verify_script(&input.unlock, &prefix).map_err(|e| {
info!(target: "script", "Error validating input {} of transaction {}: {:?}", i, self.hash, e);
e
})?;
}
for (i, output) in self.outputs.iter().enumerate() {
if let Some(ref contract) = output.contract {
self.verify_script(contract).map_err(|e| {
let prefix = format!("Transaction {}, output {}", self.hash, i);
self.verify_script(contract, &prefix).map_err(|e| {
info!(target: "script", "Error validating output {} of transaction {}: {:?}", i, self.hash, e);
e
})?;
Expand Down

0 comments on commit 765ea25

Please sign in to comment.