diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index da6a828..dc26199 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -14,5 +14,13 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Install llvm 18 + run: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 18 && rm llvm.sh + - name: Install riscv64 target + run: rustup target add riscv64imac-unknown-none-elf - name: Run Cargo Test run: cargo test + - name: Run Cargo clippy + run: cargo clippy --all + - name: Run Test + run: cd tests && make default diff --git a/src/constants.rs b/src/constants.rs index 5ebba57..0f84419 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -17,9 +17,15 @@ pub const SYS_LOAD_CELL_DATA_AS_CODE: u64 = 2091; pub const SYS_LOAD_CELL_DATA: u64 = 2092; pub const SYS_DEBUG: u64 = 2177; +// https://github.com/nervosnetwork/ckb-c-stdlib/blob/744c62e5259a5ab826e1a02ca36a811c9905f010/ckb_consts.h#L32 pub const CKB_SUCCESS: i32 = 0; pub const CKB_INDEX_OUT_OF_BOUND: i32 = 1; pub const CKB_ITEM_MISSING: i32 = 2; +pub const CKB_WAIT_FAILURE: i32 = 5; +pub const CKB_INVALID_FD: i32 = 6; +pub const CKB_OTHER_END_CLOSED: i32 = 7; +pub const CKB_MAX_VMS_SPAWNED: i32 = 8; +pub const CKB_MAX_FDS_CREATED: i32 = 9; pub const SOURCE_INPUT: u64 = 1; pub const SOURCE_OUTPUT: u64 = 2; diff --git a/src/global_data.rs b/src/global_data.rs new file mode 100644 index 0000000..6327189 --- /dev/null +++ b/src/global_data.rs @@ -0,0 +1,90 @@ +use crate::{simulator_context::SimContext, utils::SimID}; +use std::{ + collections::HashMap, + ffi::c_void, + pin::Pin, + sync::{Mutex, MutexGuard}, +}; + +lazy_static! { + static ref GLOBAL_DATA: Pin>> = Pin::new(Box::default()); +} +static mut GLOBAL_DATA_PTR: *mut Mutex = std::ptr::null_mut(); + +pub struct GlobalData { + tx_ctx: HashMap, + tx_ctx_id_count: SimID, +} +impl Default for GlobalData { + fn default() -> Self { + SimContext::update_ctx_id(0.into(), None); + Self { + tx_ctx: [(0.into(), SimContext::default())].into(), + tx_ctx_id_count: 1.into(), + } + } +} + +impl GlobalData { + pub fn get() -> &'static Mutex { + if unsafe { GLOBAL_DATA_PTR.is_null() } { + &GLOBAL_DATA + } else { + unsafe { &mut *GLOBAL_DATA_PTR as &mut Mutex } + } + } + pub fn locked() -> MutexGuard<'static, Self> { + Self::get().lock().unwrap() + } + pub fn get_ptr() -> *const c_void { + if unsafe { GLOBAL_DATA_PTR.is_null() } { + let infos_ref: &Mutex = &GLOBAL_DATA; + infos_ref as *const Mutex as *const c_void + } else { + unsafe { GLOBAL_DATA_PTR as *const c_void } + } + } + pub fn set_ptr(ptr: *const c_void) { + unsafe { + GLOBAL_DATA_PTR = ptr as *mut Mutex; + } + } + + pub fn clean() { + unsafe { + GLOBAL_DATA_PTR = std::ptr::null_mut(); + } + let mut data = Self::locked(); + *data = Self::default(); + SimContext::clean(); + } + + pub fn set_tx(&mut self, ctx: SimContext) -> SimID { + self.tx_ctx.insert(self.tx_ctx_id_count.next(), ctx); + self.tx_ctx_id_count.clone() + } + pub fn get_tx(&self, id: &SimID) -> &SimContext { + self.tx_ctx + .get(id) + .unwrap_or_else(|| panic!("unknow tx context: {:?}", id)) + } + pub fn get_tx_mut(&mut self, id: &SimID) -> &mut SimContext { + self.tx_ctx + .get_mut(id) + .unwrap_or_else(|| panic!("unknow mut tx context: {:?}", id)) + } +} + +#[macro_export] +macro_rules! get_cur_tx { + () => { + GlobalData::locked().get_tx(&SimContext::ctx_id()) + }; +} + +#[macro_export] +macro_rules! get_cur_tx_mut { + () => { + GlobalData::locked().get_tx_mut(&SimContext::ctx_id()) + }; +} diff --git a/src/lib.rs b/src/lib.rs index f57ad51..97af556 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,15 @@ pub mod constants; +pub mod spawn; +pub use spawn::*; + +mod global_data; +mod simulator_context; +mod utils; + +use global_data::GlobalData; +use simulator_context::SimContext; + #[macro_use] extern crate lazy_static; @@ -20,7 +30,7 @@ use constants::{ }; use serde_derive::{Deserialize, Serialize}; use std::collections::HashMap; -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::os::raw::{c_char, c_int, c_void}; #[derive(Clone, Serialize, Deserialize)] @@ -56,7 +66,7 @@ lazy_static! { } fn assert_vm_version() { - if SETUP.vm_version != 1 { + if SETUP.vm_version == 0 { panic!( "Currently running setup vm_version({}) not support this syscall", SETUP.vm_version @@ -94,45 +104,37 @@ pub extern "C" fn ckb_exec_cell( ) -> c_int { assert_vm_version(); - let mut filename = None; - for ht in [hash_type, 0xFF] { - let code_hash = unsafe { std::slice::from_raw_parts(code_hash, 32) }; - let mut buffer = vec![]; - buffer.extend_from_slice(code_hash); - buffer.push(ht); - buffer.extend_from_slice(&offset.to_be_bytes()[..]); - buffer.extend_from_slice(&length.to_be_bytes()[..]); - let key = format!("0x{}", faster_hex::hex_string(&buffer)); - filename = SETUP.native_binaries.get(&key); - if filename.is_some() { - break; - } - } - let filename = filename.expect("cannot locate native binary for ckb_exec syscall!"); + let sim_path = + utils::get_simulator_path(utils::to_array(code_hash, 32), hash_type, offset, length); + let sim_path = sim_path.expect("cannot locate native binary for ckb_exec syscall!"); match SETUP.run_type.as_ref().unwrap_or(&RunningType::Executable) { RunningType::Executable => { - let filename_cstring = CString::new(filename.as_bytes().to_vec()).unwrap(); + let filename_cstring = CString::new(sim_path.as_bytes().to_vec()).unwrap(); unsafe { let args = argv as *const *const i8; libc::execvp(filename_cstring.as_ptr(), args) } } RunningType::DynamicLib => { - use core::ffi::c_int; - type CkbMainFunc<'a> = libloading::Symbol< - 'a, - unsafe extern "C" fn(argc: c_int, argv: *const *const i8) -> i8, - >; - - unsafe { - let lib = libloading::Library::new(filename).expect("Load library"); - let func: CkbMainFunc = lib - .get(b"__ckb_std_main") - .expect("load function : __ckb_std_main"); - let args = argv as *const *const i8; - std::process::exit(func(argc, args).into()) - } + use utils::CkbNativeSimulator; + + let tx_ctx_id = GlobalData::locked().set_tx(simulator_context::SimContext::default()); + SimContext::update_ctx_id(tx_ctx_id.clone(), None); + + let sim = CkbNativeSimulator::new_by_hash(code_hash, hash_type, offset, length); + let args = utils::to_vec_args(argc, argv as *const *const i8); + + let join_handle = { + let mut global_data = GlobalData::locked(); + let sim_ctx = global_data.get_tx_mut(&tx_ctx_id); + let child_pid: utils::ProcID = sim_ctx.start_process(&[], move |sim_id, pid| { + sim.update_script_info(sim_id, pid); + sim.ckb_std_main(args) + }); + sim_ctx.exit(&child_pid).unwrap() + }; + join_handle.join().expect("exec dylib") as c_int } } } @@ -165,8 +167,9 @@ pub extern "C" fn ckb_load_script(ptr: *mut c_void, len: *mut u64, offset: u64) #[no_mangle] pub extern "C" fn ckb_debug(s: *const c_char) { - let message = unsafe { CStr::from_ptr(s) }.to_str().expect("UTF8 error!"); - println!("Debug message: {}", message); + let message = utils::to_c_str(s).to_str().expect("UTF8 error!"); + // println!("Debug message: {}", message); + println!("[contract debug] {}", message); } #[no_mangle] @@ -378,6 +381,29 @@ extern "C" { ) -> c_int; } +// TO fix clippy error: clippy::not_unsafe_ptr_arg_deref +fn rs_simulator_internal_dlopen2( + native_library_path: *const u8, + code: *const u8, + length: u64, + aligned_addr: *mut u8, + aligned_size: u64, + handle: *mut *mut c_void, + consumed_size: *mut u64, +) -> c_int { + unsafe { + simulator_internal_dlopen2( + native_library_path, + code, + length, + aligned_addr, + aligned_size, + handle, + consumed_size, + ) + } +} + #[no_mangle] pub extern "C" fn ckb_dlopen2( dep_cell_hash: *const u8, @@ -387,7 +413,7 @@ pub extern "C" fn ckb_dlopen2( handle: *mut *mut c_void, consumed_size: *mut u64, ) -> c_int { - let dep_cell_hash = unsafe { std::slice::from_raw_parts(dep_cell_hash, 32) }; + let dep_cell_hash = utils::to_array(dep_cell_hash, 32); let mut buffer = vec![]; buffer.extend_from_slice(dep_cell_hash); buffer.push(hash_type); @@ -414,16 +440,24 @@ pub extern "C" fn ckb_dlopen2( }) .expect("cannot locate cell dep"); let cell_data = cell_dep.data.as_ref(); - unsafe { - simulator_internal_dlopen2( - filename.as_str().as_ptr(), - cell_data.as_ptr(), - cell_data.len() as u64, - aligned_addr, - aligned_size, - handle, - consumed_size, - ) + rs_simulator_internal_dlopen2( + filename.as_str().as_ptr(), + cell_data.as_ptr(), + cell_data.len() as u64, + aligned_addr, + aligned_size, + handle, + consumed_size, + ) +} + +#[no_mangle] +pub extern "C" fn set_script_info(ptr: *const std::ffi::c_void, tx_ctx_id: u64, proc_ctx_id: u64) { + if ptr.is_null() && tx_ctx_id == 0 && proc_ctx_id == 0 { + GlobalData::clean(); + } else { + GlobalData::set_ptr(ptr); + SimContext::update_ctx_id(tx_ctx_id.into(), Some(proc_ctx_id.into())); } } diff --git a/src/simulator_context.rs b/src/simulator_context.rs new file mode 100644 index 0000000..3afc726 --- /dev/null +++ b/src/simulator_context.rs @@ -0,0 +1,508 @@ +use crate::{ + global_data::GlobalData, + utils::{Event, Fd, ProcID, SimID}, +}; +use std::{cell::RefCell, collections::HashMap, thread::JoinHandle}; + +thread_local! { + static TX_CONTEXT_ID: RefCell = RefCell::new(SimID::default()); + static PROC_CONTEXT_ID: RefCell = RefCell::new(ProcID::default()); +} + +const MAX_PROCESSES_COUNT: u64 = 16; + +#[derive(PartialEq, Eq, Clone)] +pub enum ProcStatus { + Default(ProcID), + WaitSpawn(ProcID, bool), // PID, is_release + ReadWait(ProcID, Fd, usize, Vec, u64), + WriteWait(ProcID, Fd, Vec, u64), + CloseWait(ProcID, Fd), + Terminated(ProcID), +} +impl Default for ProcStatus { + fn default() -> Self { + Self::Default(ProcInfo::id()) + } +} +impl std::fmt::Debug for ProcStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Default(pid) => write!(f, "Loaded({})", u64::from(pid.clone())), + Self::WaitSpawn(pid, is_rel) => { + if *is_rel { + write!(f, "WaitSpawn N({})", u64::from(pid.clone())) + } else { + write!(f, "WaitSpawn({})", u64::from(pid.clone())) + } + } + Self::ReadWait(pid, fd, len, buf, dbg_id) => { + if len == &0 { + write!( + f, + "ReadWait N(id: {}, pid:{}, fd: {}, bl: {})", + dbg_id, + u64::from(pid.clone()), + u64::from(fd.clone()), + buf.len() + ) + } else { + write!( + f, + "ReadWait(id: {}, pid:{}, fd: {}, nl: {}, bl: {})", + dbg_id, + u64::from(pid.clone()), + u64::from(fd.clone()), + len, + buf.len() + ) + } + } + Self::WriteWait(pid, fd, buf, dbg_id) => { + if buf.is_empty() { + write!( + f, + "WriteWait N(id: {}, pid: {}, fd: {})", + dbg_id, + u64::from(pid.clone()), + u64::from(fd.clone()), + ) + } else { + write!( + f, + "WriteWait(id: {}, pid: {}, fd: {}, l: {})", + dbg_id, + u64::from(pid.clone()), + u64::from(fd.clone()), + buf.len() + ) + } + } + Self::CloseWait(pid, fd) => { + write!( + f, + "Close(pid: {}, fd: {})", + u64::from(pid.clone()), + u64::from(fd.clone()) + ) + } + Self::Terminated(pid) => write!(f, "Terminated({})", u64::from(pid.clone())), + } + } +} +impl ProcStatus { + fn read_wait(&self) -> Option<(&ProcID, &Fd, &usize, &[u8])> { + if let Self::ReadWait(pid, fd, len, buf, _) = self { + Some((pid, fd, len, buf)) + } else { + None + } + } + fn read_wait_mut(&mut self) -> Option<(&ProcID, &mut Fd, &mut usize, &mut Vec)> { + if let Self::ReadWait(pid, fd, len, buf, _) = self { + Some((pid, fd, len, buf)) + } else { + None + } + } + fn write_wait(&self) -> Option<(&ProcID, &Fd, &[u8])> { + if let Self::WriteWait(pid, fd, buf, _) = self { + Some((pid, fd, buf)) + } else { + None + } + } + fn write_wait_mut(&mut self) -> Option<(&ProcID, &mut Fd, &mut Vec)> { + if let Self::WriteWait(pid, fd, buf, _) = self { + Some((pid, fd, buf)) + } else { + None + } + } +} + +#[derive(Default)] +struct ProcInfo { + parent_id: ProcID, + + inherited_fds: Vec, + + scheduler_event: Event, + join_handle: Option>, +} +impl ProcInfo { + fn set_pid(id: ProcID) { + PROC_CONTEXT_ID.with(|f| *f.borrow_mut() = id); + } + pub fn id() -> ProcID { + PROC_CONTEXT_ID.with(|f| f.borrow().clone()) + } +} + +pub struct SimContext { + fd_count: u64, + process_id_count: ProcID, + + processes: HashMap, + process_status: Vec, + readed_cache: HashMap>, + + fds: HashMap, + + dbg_status_count: u64, +} +impl Default for SimContext { + fn default() -> Self { + ProcInfo::set_pid(0.into()); + Self { + fd_count: 2, + process_id_count: 1.into(), + processes: [(0.into(), ProcInfo::default())].into(), + process_status: Default::default(), + readed_cache: Default::default(), + fds: Default::default(), + + dbg_status_count: 0, + } + } +} +impl SimContext { + pub fn update_ctx_id(id: SimID, pid: Option) { + TX_CONTEXT_ID.with(|f| *f.borrow_mut() = id); + if let Some(pid) = pid { + ProcInfo::set_pid(pid); + } + } + pub fn ctx_id() -> SimID { + TX_CONTEXT_ID.with(|f| f.borrow().clone()) + } + pub fn clean() { + TX_CONTEXT_ID.with(|f| *f.borrow_mut() = 0.into()); + PROC_CONTEXT_ID.with(|f| *f.borrow_mut() = 0.into()); + } + + pub fn start_process i8>( + &mut self, + fds: &[Fd], + func: F, + ) -> ProcID { + let parent_id = ProcInfo::id(); + let id = self.process_id_count.next(); + let process = ProcInfo { + parent_id: parent_id.clone(), + inherited_fds: fds.to_vec(), + ..Default::default() + }; + + self.processes.insert(id.clone(), process); + let ctx_id = SimContext::ctx_id(); + + fds.iter().all(|fd| { + self.move_pipe(fd, id.clone()); + true + }); + self.process_status + .push(ProcStatus::WaitSpawn(id.clone(), false)); + + let id2 = id.clone(); + let join_handle = std::thread::spawn(move || { + SimContext::update_ctx_id(ctx_id.clone(), Some(id.clone())); + let code = func(ctx_id.clone(), id.clone()); + + let mut gd = GlobalData::locked(); + let cur_sim = gd.get_tx_mut(&SimContext::ctx_id()); + cur_sim.close_all(&id); + cur_sim.process_io(None); + + code + }); + + self.process_mut(&id2).join_handle = Some(join_handle); + + id2 + } + pub fn pid() -> ProcID { + ProcInfo::id() + } + pub fn inherited_fds(&self) -> Vec { + let process = self.process(&ProcInfo::id()); + process.inherited_fds.clone() + } + fn process(&self, id: &ProcID) -> &ProcInfo { + self.processes + .get(id) + .unwrap_or_else(|| panic!("unknow process id: {:?}", id)) + } + fn process_mut(&mut self, id: &ProcID) -> &mut ProcInfo { + self.processes + .get_mut(id) + .unwrap_or_else(|| panic!("unknow process id: {:?}", id)) + } + pub fn max_proc_spawned(&self) -> bool { + u64::from(self.process_id_count.clone()) > MAX_PROCESSES_COUNT + } + pub fn has_proc(&self, id: &ProcID) -> bool { + self.processes.contains_key(id) + } + pub fn get_event(&self) -> Event { + self.process(&ProcInfo::id()).scheduler_event.clone() + } + pub fn exit(&mut self, id: &ProcID) -> Option> { + self.process_io(None); + + let process = self.process_mut(id); + + process.join_handle.take() + } + + fn process_io(&mut self, fd: Option<&Fd>) { + // println!("==status 1: {:?}", self.process_status); + + let mut update_rw: Vec<(usize, usize, bool)> = Vec::<(usize, usize, bool)>::new(); // Vec<(Read, Write)> + for i in 0..self.process_status.len() { + if let Some((_pid, rfd, rlen, _rbuf)) = self.process_status[i].read_wait() { + if rlen != &0 { + assert!(rfd.is_read()); + let write_fd = rfd.other_fd(); + + let mut is_close = false; + if let Some(w_pos) = + self.process_status.iter().position(|status| match status { + ProcStatus::WriteWait(_wpid, wfd, _wbuf, _) => wfd == &write_fd, + ProcStatus::CloseWait(_wpid, cfd) => { + is_close = true; + cfd == &write_fd + } + _ => false, + }) + { + update_rw.push((i, w_pos, is_close)); + } + } + } + } + update_rw.iter().for_each(|(r_pos, w_pos, is_close)| { + if *is_close { + let (_, _rfd, rlen, _rbuf) = self.process_status[*r_pos] + .read_wait_mut() + .expect("Unknow error"); + *rlen = 0; + } else { + let wbuf = self.process_status[*w_pos] + .write_wait() + .map(|(_, _, buf)| buf.to_vec()) + .expect("unknow error"); + + // Update Read Status + let (_, _rfd, rlen, rbuf) = self.process_status[*r_pos] + .read_wait_mut() + .expect("Unknow error"); + let copy_len = (*rlen).min(wbuf.len()); + rbuf.extend_from_slice(&wbuf[..copy_len]); + *rlen -= copy_len; + + // Update Write Status + let (_, _wfd, wbuf) = self.process_status[*w_pos] + .write_wait_mut() + .expect("unknow error"); + *wbuf = wbuf[copy_len..].to_vec(); + } + }); + + self.process_status.iter_mut().any(|status| { + if let ProcStatus::WaitSpawn(pid, is_rel) = status { + if pid == &ProcInfo::id() && !*is_rel { + *is_rel = true; + } + } + false + }); + + // println!("==status 2: {:?}", self.process_status); + self.notify_status(fd); + // println!("==status 3: {:?}", self.process_status); + } + fn notify_status(&mut self, fd: Option<&Fd>) { + if let Some(pos) = self.process_status.iter().position(|s| { + if let Some((_pid, rfd, len, _)) = s.read_wait() { + len == &0 && (fd == Some(rfd) || fd == Some(&rfd.other_fd())) + } else { + false + } + }) { + if let Some((pid, fd, _len, buf)) = self.process_status.remove(pos).read_wait() { + assert_eq!(pid, self.fds.get(fd).unwrap()); + self.process(pid).scheduler_event.notify(); + self.readed_cache.insert(fd.clone(), buf.to_vec()); + } else { + panic!("unknow error"); + } + return; + } + + let mut notify_items = std::collections::BTreeMap::::new(); + for i in 0..self.process_status.len() { + match &self.process_status[i] { + ProcStatus::Default(_) => (), + ProcStatus::WaitSpawn(pid, is_rel) => { + if *is_rel { + notify_items.insert(pid.clone(), i); + } + } + ProcStatus::ReadWait(pid, _fd, len, _buf, _) => { + if len == &0 { + notify_items.insert(pid.clone(), i); + } + } + ProcStatus::WriteWait(pid, _fd, buf, _) => { + if buf.is_empty() { + notify_items.insert(pid.clone(), i); + } + } + ProcStatus::CloseWait(pid, _fd) => { + notify_items.insert(pid.clone(), i); + } + ProcStatus::Terminated(pid) => { + notify_items.insert(pid.clone(), i); + } + }; + } + if let Some((_pid, index)) = notify_items.pop_first() { + match &self.process_status[index] { + ProcStatus::Default(_) => (), + ProcStatus::WaitSpawn(pid, _) => { + self.process(&self.process(pid).parent_id) + .scheduler_event + .notify(); + } + ProcStatus::ReadWait(_, fd, _len, buf, _) => { + self.readed_cache.insert(fd.clone(), buf.clone()); + let pid = self.fds.get(fd).expect("unknow error"); + self.process(pid).scheduler_event.notify(); + } + ProcStatus::WriteWait(_, fd, _buf, _) => { + let pid = self.fds.get(fd).expect("unknow error"); + self.process(pid).scheduler_event.notify(); + } + ProcStatus::CloseWait(pid, _) => { + self.process(&self.process(pid).parent_id) + .scheduler_event + .notify(); + } + ProcStatus::Terminated(pid) => { + self.process(&self.process(pid).parent_id) + .scheduler_event + .notify(); + } + }; + self.process_status.remove(index); + } + } + pub fn wait_read(&mut self, fd: Fd, len: usize) -> Event { + let id = ProcInfo::id(); + let dbg_id = self.dbg_status_count; + self.dbg_status_count += 1; + + self.process_status.push(ProcStatus::ReadWait( + id, + fd.clone(), + len, + Vec::new(), + dbg_id, + )); + self.process_io(Some(&fd)); + self.get_event() + } + pub fn wait_write(&mut self, fd: Fd, buf: &[u8]) -> Event { + let id = ProcInfo::id(); + let dbg_id = self.dbg_status_count; + self.dbg_status_count += 1; + self.process_status + .push(ProcStatus::WriteWait(id, fd.clone(), buf.to_vec(), dbg_id)); + + self.process_io(Some(&fd)); + self.get_event() + } + pub fn read_cache(&mut self, fd: &Fd) -> Vec { + if let Some(buf) = self.readed_cache.remove(fd) { + buf + } else { + let mut r_buf = Vec::new(); + self.process_status.iter_mut().any(|status| { + if let Some((_pid, rfd, _len, buf)) = status.read_wait_mut() { + if fd == rfd { + r_buf = buf.clone(); + buf.clear(); + true + } else { + false + } + } else { + false + } + }); + r_buf + } + } + + pub fn new_pipe(&mut self) -> (Fd, Fd) { + let pid = ProcInfo::id(); + let fds = Fd::create(self.fd_count); + + self.fds.insert(fds.0.clone(), pid.clone()); + self.fds.insert(fds.1.clone(), pid.clone()); + self.fd_count = fds.2; + + (fds.0, fds.1) + } + pub fn close_pipe(&mut self, fd: Fd) -> Result { + if !self.has_fd(&fd) { + Err(()) + } else { + self.process_status + .push(ProcStatus::CloseWait(ProcInfo::id(), fd.clone())); + self.process_io(Some(&fd)); + + if self.fds.remove(&fd).is_some() { + Ok(self.process(&ProcInfo::id()).scheduler_event.clone()) + } else { + // + Err(()) + } + } + } + pub fn len_pipe(&self) -> usize { + self.fds.len() + } + fn move_pipe(&mut self, fd: &Fd, pid: ProcID) { + let f = self + .fds + .get_mut(fd) + .unwrap_or_else(|| panic!("unknow fd: {:?}", fd)); + *f = pid; + } + fn close_all(&mut self, id: &ProcID) { + let keys_to_rm: Vec = self + .fds + .iter() + .filter(|(_k, v)| v == &id) + .map(|(k, _v)| k.clone()) + .collect(); + for k in keys_to_rm { + self.fds.remove(&k); + } + + self.process_status.push(ProcStatus::Terminated(id.clone())); + } + + pub fn has_fd(&self, fd: &Fd) -> bool { + if let Some(pid) = self.fds.get(fd) { + &ProcInfo::id() == pid + } else { + false + } + } + pub fn chech_other_fd(&self, fd: &Fd) -> bool { + self.fds.contains_key(&fd.other_fd()) + } +} diff --git a/src/spawn.rs b/src/spawn.rs new file mode 100644 index 0000000..5628afd --- /dev/null +++ b/src/spawn.rs @@ -0,0 +1,228 @@ +use crate::{ + constants::{ + CKB_INVALID_FD, CKB_MAX_FDS_CREATED, CKB_MAX_VMS_SPAWNED, CKB_OTHER_END_CLOSED, + CKB_SUCCESS, CKB_WAIT_FAILURE, + }, + get_cur_tx, get_cur_tx_mut, + global_data::GlobalData, + simulator_context::SimContext, + utils, + utils::{Fd, ProcID}, +}; +use std::os::raw::{c_int, c_void}; + +const MAX_FDS: usize = 64; + +#[repr(C)] +#[derive(Clone)] +pub struct SpawnArgs { + /// argc contains the number of arguments passed to the program. + pub argc: u64, + /// argv is a one-dimensional array of strings. + pub argv: *const *const i8, + /// a pointer used to save the process_id of the child process. + pub process_id: *mut u64, + /// an array representing the file descriptors passed to the child process. It must end with zero. + pub inherited_fds: *const u64, +} + +#[no_mangle] +pub extern "C" fn ckb_spawn_cell( + code_hash: *const u8, + hash_type: u8, + offset: u32, + length: u32, + argc: i32, + argv: *const *const u8, + inherited_fds: *const u64, + pid: *mut u64, +) -> c_int { + // check fd: + let inherited_fds = get_fds(inherited_fds); + for it in &inherited_fds { + if let Err(err) = CheckSpawn::Def.check(it) { + return err; + } + } + if get_cur_tx!().max_proc_spawned() { + return CKB_MAX_VMS_SPAWNED; + } + + let ckb_sim = utils::CkbNativeSimulator::new_by_hash(code_hash, hash_type, offset, length); + let args = utils::to_vec_args(argc, argv as *const *const i8); + let new_id = get_cur_tx_mut!().start_process(&inherited_fds, move |sim_id, pid| { + ckb_sim.update_script_info(sim_id, pid); + ckb_sim.ckb_std_main(args) + }); + + let event = get_cur_tx!().get_event(); + event.wait(); + + unsafe { *({ pid }) = new_id.into() }; + CKB_SUCCESS +} + +#[no_mangle] +pub extern "C" fn ckb_wait(pid: u64, code: *mut i8) -> c_int { + let pid: ProcID = pid.into(); + if !get_cur_tx!().has_proc(&pid) { + return CKB_WAIT_FAILURE; + } + let join_handle = get_cur_tx_mut!().exit(&pid); + + let c = if let Some(j) = join_handle { + j.join().unwrap() + } else { + return CKB_WAIT_FAILURE; + }; + unsafe { *({ code }) = c }; + CKB_SUCCESS +} + +#[no_mangle] +pub extern "C" fn ckb_process_id() -> u64 { + SimContext::pid().into() +} + +#[no_mangle] +pub extern "C" fn ckb_pipe(fds: *mut u64) -> c_int { + if get_cur_tx!().len_pipe() >= MAX_FDS { + return CKB_MAX_FDS_CREATED; + } + + let out = get_cur_tx_mut!().new_pipe(); + copy_fds(&[out.0, out.1], fds); + CKB_SUCCESS +} + +#[no_mangle] +pub extern "C" fn ckb_read(fd: u64, buf: *mut c_void, length: *mut usize) -> c_int { + let fd: Fd = fd.into(); + + // Check + if let Err(e) = CheckSpawn::Read.check(&fd) { + return e; + } + + // wait read + let event = get_cur_tx_mut!().wait_read(fd.clone(), unsafe { *({ length }) }); + event.wait(); + + let data = get_cur_tx_mut!().read_cache(&fd); + + if !data.is_empty() { + unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), buf as *mut u8, data.len()) }; + } + unsafe { + *({ length }) = data.len(); + } + + CKB_SUCCESS +} + +#[no_mangle] +pub extern "C" fn ckb_write(fd: u64, buf: *const c_void, length: *mut usize) -> c_int { + let fd: Fd = fd.into(); + + if let Err(e) = CheckSpawn::Write.check(&fd) { + return e; + } + + let buf = unsafe { + let length = utils::to_usize(length); + std::slice::from_raw_parts(buf as *const u8, length) + } + .to_vec(); + let event = get_cur_tx_mut!().wait_write(fd, &buf); + event.wait(); + + CKB_SUCCESS +} + +#[no_mangle] +pub extern "C" fn ckb_inherited_fds(fds: *mut u64, length: *mut usize) -> c_int { + let out_fds = get_cur_tx!().inherited_fds(); + let len = out_fds.len().min(utils::to_usize(length)); + + copy_fds(&out_fds[0..len], fds); + unsafe { *({ length }) = len }; + CKB_SUCCESS +} + +#[no_mangle] +pub extern "C" fn ckb_close(fd: u64) -> c_int { + let fd = fd.into(); + let event = get_cur_tx_mut!().close_pipe(fd); + if let Ok(event) = event { + event.wait(); + CKB_SUCCESS + } else { + CKB_INVALID_FD + } +} + +#[no_mangle] +pub extern "C" fn ckb_load_block_extension( + _addr: *mut c_void, + _len: *mut u64, + _offset: usize, + _index: usize, + _source: usize, +) -> c_int { + panic!("unsupport"); +} + +fn copy_fds(in_fd: &[Fd], out_fd: *mut u64) { + let mut out_fd = out_fd; + for fd in in_fd { + unsafe { + *out_fd = fd.clone().into(); + out_fd = out_fd.add(1); + } + } +} + +fn get_fds(fds: *const u64) -> Vec { + unsafe { + let mut buf = Vec::new(); + let mut fds_ptr = fds; + while *fds_ptr != 0 { + buf.push((*fds_ptr).into()); + fds_ptr = fds_ptr.add(1); + } + buf + } +} + +enum CheckSpawn { + Def, + Read, + Write, +} +impl CheckSpawn { + fn check(self, fd: &Fd) -> Result<(), c_int> { + match self { + Self::Def => (), + Self::Read => { + if !fd.is_read() { + return Err(CKB_INVALID_FD); + } + } + Self::Write => { + if fd.is_read() { + return Err(CKB_INVALID_FD); + } + } + } + + let g = GlobalData::locked(); + let tx_ctx = g.get_tx(&SimContext::ctx_id()); + if !tx_ctx.has_fd(fd) { + return Err(CKB_INVALID_FD); + } + if !tx_ctx.chech_other_fd(fd) { + return Err(CKB_OTHER_END_CLOSED); + } + Ok(()) + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..a0a3bc1 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,221 @@ +use crate::global_data::GlobalData; +use std::{ + ffi::{c_int, c_void}, + path::PathBuf, + sync::{Arc, Condvar, Mutex}, +}; + +pub fn get_simulator_path( + code_hash: &[u8], + hash_type: u8, + offset: u32, + length: u32, +) -> Option { + let mut filename = None; + for ht in [hash_type, 0xFF] { + let mut buffer = vec![]; + buffer.extend_from_slice(code_hash); + buffer.push(ht); + buffer.extend_from_slice(&offset.to_be_bytes()[..]); + buffer.extend_from_slice(&length.to_be_bytes()[..]); + let key = format!("0x{}", faster_hex::hex_string(&buffer)); + filename = crate::SETUP.native_binaries.get(&key); + if filename.is_some() { + break; + } + } + filename.cloned() +} + +pub struct CkbNativeSimulator { + lib: libloading::Library, +} +impl CkbNativeSimulator { + pub fn new_by_hash(code_hash: *const u8, hash_type: u8, offset: u32, length: u32) -> Self { + let sim_path = get_simulator_path( + unsafe { std::slice::from_raw_parts(code_hash, 32) }, + hash_type, + offset, + length, + ); + let sim_path = sim_path.expect("cannot locate native binary for ckb_spawn syscall!"); + Self::new(&sim_path.into()) + } + fn new(path: &PathBuf) -> Self { + unsafe { + let lib = libloading::Library::new(path).expect("Load library"); + Self { lib } + } + } + + pub fn ckb_std_main(self, args: Vec) -> i8 { + type CkbMainFunc<'a> = + libloading::Symbol<'a, unsafe extern "C" fn(argc: i32, argv: *const *const i8) -> i8>; + + let argc = args.len() as u64; + let mut argv: Vec<*const i8> = Vec::with_capacity(argc as usize + 1); + for s in args { + let c_string = std::ffi::CString::new(s.clone()).expect("CString::new failed"); + argv.push(c_string.into_raw()); + } + argv.push(std::ptr::null_mut()); + + unsafe { + let func: CkbMainFunc = self + .lib + .get(b"__ckb_std_main") + .expect("load function : __ckb_std_main"); + func(argc as i32, argv.as_ptr()) + } + } + + pub fn update_script_info(&self, tx_ctx_id: SimID, pid: ProcID) { + type SetScriptInfo<'a> = libloading::Symbol< + 'a, + unsafe extern "C" fn(ptr: *const c_void, tx_ctx_id: u64, pid: u64), + >; + + unsafe { + let func: SetScriptInfo = self + .lib + .get(b"__set_script_info") + .expect("load function : __update_spawn_info"); + func(GlobalData::get_ptr(), tx_ctx_id.into(), pid.into()) + } + } +} + +pub fn to_vec_args(argc: c_int, argv: *const *const i8) -> Vec { + let mut args = Vec::with_capacity(argc as usize); + for i in 0..argc { + let c_str = unsafe { std::ffi::CStr::from_ptr(*argv.add(i as usize)) }; + let str_slice = c_str + .to_str() + .expect("Failed to convert C string to Rust string"); + args.push(str_slice.to_owned()); + } + args +} + +pub fn to_array(ptr: *const u8, len: usize) -> &'static [u8] { + unsafe { std::slice::from_raw_parts(ptr, len) } +} + +pub fn to_c_str(ptr: *const std::ffi::c_char) -> &'static core::ffi::CStr { + unsafe { core::ffi::CStr::from_ptr(ptr) } +} + +pub fn to_usize(ptr: *mut usize) -> usize { + unsafe { *ptr } +} + +#[derive(Default, Debug)] +pub struct Event { + data: Arc<(Mutex, Condvar)>, +} +impl Clone for Event { + fn clone(&self) -> Self { + Self { + data: Arc::clone(&self.data), + } + } +} +impl Event { + pub fn notify(&self) { + let (lock, cvar) = &*self.data; + let mut started = lock.lock().unwrap(); + *started = true; + cvar.notify_one(); + } + + pub fn wait(&self) { + let (lock, cvar) = &*self.data; + let mut started = lock.lock().unwrap(); + + loop { + if *started { + *started = false; + break; + } + started = cvar.wait(started).unwrap(); + } + } +} + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Fd(pub u64); +impl From for Fd { + fn from(value: u64) -> Self { + Self(value) + } +} +impl From for u64 { + fn from(value: Fd) -> Self { + value.0 + } +} +impl std::fmt::Debug for Fd { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "FD:{}", self.0) + } +} +impl Fd { + pub fn create(slot: u64) -> (Fd, Fd, u64) { + (Fd(slot), Fd(slot + 1), slot + 2) + } + pub fn other_fd(&self) -> Fd { + Fd(self.0 ^ 0x1) + } + pub fn is_read(&self) -> bool { + self.0 % 2 == 0 + } +} + +#[derive(Default, PartialEq, Eq, Clone, Hash)] +pub struct SimID(u64); +impl From for SimID { + fn from(value: u64) -> Self { + Self(value) + } +} +impl From for u64 { + fn from(value: SimID) -> Self { + value.0 + } +} +impl std::fmt::Debug for SimID { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "SID:{}", self.0) + } +} +impl SimID { + pub fn next(&mut self) -> Self { + self.0 += 1; + self.clone() + } +} + +#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] +pub struct ProcID(u64); +impl From for ProcID { + fn from(value: u64) -> Self { + Self(value) + } +} +impl From for u64 { + fn from(value: ProcID) -> Self { + value.0 + } +} +impl std::fmt::Debug for ProcID { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "PID:{}", self.0) + } +} +impl ProcID { + pub fn next(&mut self) -> Self { + let id = self.clone(); + self.0 += 1; + id + } +} diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..4a7da40 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,4 @@ +/build +/target +/tests/failed_txs +/.vscode diff --git a/tests/Cargo.lock b/tests/Cargo.lock new file mode 100644 index 0000000..741514b --- /dev/null +++ b/tests/Cargo.lock @@ -0,0 +1,2050 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "version_check", +] + +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "blake2b-ref" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "294d17c72e0ba59fad763caa112368d0672083779cdebbb97164f4bb4c1e339a" + +[[package]] +name = "blake2b-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89a8565807f21b913288968e391819e7f9b2f0f46c7b89549c051cccf3a2771" +dependencies = [ + "cc", + "cty", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "buddy-alloc" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f0d2da64a6a895d5a7e0724882825d50f83c13396b1b9f1878e19a024bab395" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +dependencies = [ + "serde", +] + +[[package]] +name = "cacache" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142316461ed3a3dfcba10417317472da5bfd0461e4d276bf7c07b330766d9490" +dependencies = [ + "digest", + "either", + "futures", + "hex", + "libc", + "memmap2", + "miette", + "reflink-copy", + "serde", + "serde_derive", + "serde_json", + "sha1", + "sha2", + "ssri", + "tempfile", + "thiserror", + "tokio", + "tokio-stream", + "walkdir", +] + +[[package]] +name = "cc" +version = "1.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ckb-always-success-script" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b3b72a38c9920a29990df12002c4d069a147c8782f0c211f8a01b2df8f42bfd" + +[[package]] +name = "ckb-chain-spec" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38fa470ac81179a066b3dd3e0d56e8fb9988aaddf5c2ae921cbc2ee6c60bbc56" +dependencies = [ + "cacache", + "ckb-constant", + "ckb-crypto", + "ckb-dao-utils", + "ckb-error", + "ckb-hash", + "ckb-jsonrpc-types", + "ckb-logger", + "ckb-pow", + "ckb-rational", + "ckb-resource", + "ckb-traits", + "ckb-types", + "serde", + "toml", +] + +[[package]] +name = "ckb-channel" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081350579a7d6cee3c7d3b82b3667860517e785269f8cd72b27ae472775d9c04" +dependencies = [ + "crossbeam-channel", +] + +[[package]] +name = "ckb-constant" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce0d46bfe7d555b60e7e1b589643bcd2d6a40e4cc0a04c5e9412dbb30c1f206" + +[[package]] +name = "ckb-crypto" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b268e177104ec7089656562adac06dd8209298873cb806fff76c1d3df16566" +dependencies = [ + "ckb-fixed-hash", + "faster-hex", + "lazy_static", + "rand 0.8.5", + "secp256k1", + "thiserror", +] + +[[package]] +name = "ckb-dao" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98374c485145b35b27be4cdb64ef958c25903698fb5b18e72107a90f06e8e408" +dependencies = [ + "byteorder", + "ckb-chain-spec", + "ckb-dao-utils", + "ckb-traits", + "ckb-types", +] + +[[package]] +name = "ckb-dao-utils" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df36dfa384153423e777f3f40beeb5fbb42d5ba223d411d7850bf72e190428e" +dependencies = [ + "byteorder", + "ckb-error", + "ckb-types", +] + +[[package]] +name = "ckb-error" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6726194aed3b38485270e64d3823b3fb5e9d7ce6ea2ea117106f97619272de5" +dependencies = [ + "anyhow", + "ckb-occupied-capacity", + "derive_more", + "thiserror", +] + +[[package]] +name = "ckb-fixed-hash" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e497f03441f4622bc6d1d44db95ff47c37bec03ef2c8132ca19ac8005c70995f" +dependencies = [ + "ckb-fixed-hash-core", + "ckb-fixed-hash-macros", +] + +[[package]] +name = "ckb-fixed-hash-core" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb5f80c82b9b0498272f085b86824cfe0b76a2c04ee653a91f9ff362fd9b6f6c" +dependencies = [ + "ckb_schemars", + "faster-hex", + "serde", + "thiserror", +] + +[[package]] +name = "ckb-fixed-hash-macros" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00ede2291016d17450e9d117fac6fb515629779e31ea452d5146a117cd12bf0f" +dependencies = [ + "ckb-fixed-hash-core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ckb-gen-types" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7891f62f4ae4f018bfde91a46178c78491a1dfee740a20b994003a23f10af" +dependencies = [ + "cfg-if", + "ckb-error", + "ckb-fixed-hash", + "ckb-hash", + "ckb-occupied-capacity", + "molecule", + "numext-fixed-uint", +] + +[[package]] +name = "ckb-hash" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96b7aec74956ae5e79d0fc68e5903dc2b133c2c64644514485bbc9feb5367eb" +dependencies = [ + "blake2b-ref", + "blake2b-rs", +] + +[[package]] +name = "ckb-jsonrpc-types" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44e622a66404770b52da9dfbc9b994f2b711ea2368ef23cc9b1c96ca38491ecf" +dependencies = [ + "ckb-types", + "ckb_schemars", + "faster-hex", + "serde", + "serde_json", +] + +[[package]] +name = "ckb-logger" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c76af0252cd14e57fafac7c67282eedb9c7bbf40a529ed4eb1bb85067b767e7a" +dependencies = [ + "log", +] + +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ckb-mock-tx-types" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd785084e7c3903cb49f0d2abb12c56880827cae14723cd4fed1bf3fa26a74db" +dependencies = [ + "ckb-jsonrpc-types", + "ckb-traits", + "ckb-types", + "serde", +] + +[[package]] +name = "ckb-occupied-capacity" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e01910f17fcdb1850df67a5b340bbb98d18948eacb476fc85d9a7699294d7ab" +dependencies = [ + "ckb-occupied-capacity-core", + "ckb-occupied-capacity-macros", +] + +[[package]] +name = "ckb-occupied-capacity-core" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ead5905cedba4acf082f88723c8f42a508bd28fd9c00e1dbf170049ef778b4" +dependencies = [ + "serde", +] + +[[package]] +name = "ckb-occupied-capacity-macros" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "893198354933ba8fa0a1d99c013c819a295f6ae30b1af89fc14e0b62d7afa024" +dependencies = [ + "ckb-occupied-capacity-core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ckb-pow" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069c3db99adb9d350d186de8e02a43e77dff7be2a8b9b7cf61ba80a280e2dd00" +dependencies = [ + "byteorder", + "ckb-hash", + "ckb-types", + "eaglesong", + "log", + "serde", +] + +[[package]] +name = "ckb-rational" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be813511d1c17a6bab8a7dcfbee6e086aa2bae3bb77cfd4a570abfb67af2c16" +dependencies = [ + "numext-fixed-uint", + "serde", +] + +[[package]] +name = "ckb-resource" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1e74f9bc4039a4cf6d579dc8b444b3d76780e51aab94daccdd5e6e58d01cc32" +dependencies = [ + "ckb-system-scripts", + "ckb-types", + "includedir", + "includedir_codegen", + "phf", + "serde", + "walkdir", +] + +[[package]] +name = "ckb-script" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b92a1f4f059f4680b08817dfb739a40356dcf4798b3cb8ae1f1d67218eb0fb" +dependencies = [ + "byteorder", + "ckb-chain-spec", + "ckb-error", + "ckb-hash", + "ckb-logger", + "ckb-traits", + "ckb-types", + "ckb-vm", + "faster-hex", + "serde", + "tokio", +] + +[[package]] +name = "ckb-std" +version = "0.16.0" +dependencies = [ + "buddy-alloc", + "cc", + "ckb-gen-types", + "ckb-x64-simulator 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gcd", +] + +[[package]] +name = "ckb-std-wrapper" +version = "0.1.0" +dependencies = [ + "ckb-std", +] + +[[package]] +name = "ckb-system-scripts" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa5c59063142de7a68cfad4449c6b3863563856219a2925dfb8c5f019ec2aa47" +dependencies = [ + "blake2b-rs", + "faster-hex", + "includedir", + "includedir_codegen", + "phf", +] + +[[package]] +name = "ckb-systemtime" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758eadf691a5e85efcc41927c947f52d924e61837874a8e103d285f4e81e775d" + +[[package]] +name = "ckb-testtool" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f39aec2471d611cee13f2e39ef1f5419150ba94bc8fd3a3db520876a6fe0d5d2" +replace = "ckb-testtool 0.13.1 (git+https://github.com/joii2020/ckb-testtool.git?rev=281c4ec)" + +[[package]] +name = "ckb-testtool" +version = "0.13.1" +source = "git+https://github.com/joii2020/ckb-testtool.git?rev=281c4ec#281c4ec6e8db9c6e86d9196b734f3494539165b2" +dependencies = [ + "ckb-always-success-script", + "ckb-chain-spec", + "ckb-crypto", + "ckb-error", + "ckb-hash", + "ckb-jsonrpc-types", + "ckb-mock-tx-types", + "ckb-resource", + "ckb-script", + "ckb-traits", + "ckb-types", + "ckb-verification", + "faster-hex", + "lazy_static", + "libloading", + "rand 0.8.5", + "serde_json", +] + +[[package]] +name = "ckb-traits" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6500281355bbf7a235fb386b2883e8ffb8cb5bab8447bd86dd229148ec52704" +dependencies = [ + "ckb-types", +] + +[[package]] +name = "ckb-types" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f4ef54b7665440d1984489c580e8d540888496d7b9ff8d57262ac583ff97a4" +dependencies = [ + "bit-vec", + "bytes", + "ckb-channel", + "ckb-constant", + "ckb-error", + "ckb-fixed-hash", + "ckb-gen-types", + "ckb-hash", + "ckb-merkle-mountain-range", + "ckb-occupied-capacity", + "ckb-rational", + "derive_more", + "golomb-coded-set", + "merkle-cbt", + "molecule", + "numext-fixed-uint", + "once_cell", + "paste", +] + +[[package]] +name = "ckb-verification" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15031c4c15946db946b79f2a18e0281396f2f9ae0d71a3a70882f5f7059a11d9" +dependencies = [ + "ckb-chain-spec", + "ckb-dao", + "ckb-dao-utils", + "ckb-error", + "ckb-pow", + "ckb-script", + "ckb-systemtime", + "ckb-traits", + "ckb-types", + "ckb-verification-traits", + "derive_more", + "lru", + "tokio", +] + +[[package]] +name = "ckb-verification-traits" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528617394f801d37aaff93da32bda8502ad61ed4706f9073c4a6761a9895df47" +dependencies = [ + "bitflags 1.3.2", + "ckb-error", +] + +[[package]] +name = "ckb-vm" +version = "0.24.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddff96029d3298cb630e95f29d4b9a93384e938a0b75758684aa8794b53bdd1a" +dependencies = [ + "byteorder", + "bytes", + "cc", + "ckb-vm-definitions", + "derive_more", + "goblin 0.2.3", + "goblin 0.4.0", + "rand 0.7.3", + "scroll", + "serde", +] + +[[package]] +name = "ckb-vm-definitions" +version = "0.24.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c280bf1d589d23ab0358f58601c2187fc6be86a131644583ef72ea96a0a13ddd" +dependencies = [ + "paste", +] + +[[package]] +name = "ckb-x64-simulator" +version = "0.9.2" +dependencies = [ + "cc", + "ckb-mock-tx-types", + "ckb-types", + "faster-hex", + "lazy_static", + "libc", + "libloading", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "ckb-x64-simulator" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "febfea480f2e1e78d620e243447929fabcc200c647fb1073c692d632af202745" +replace = "ckb-x64-simulator 0.9.2" + +[[package]] +name = "ckb_schemars" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21f99fca82a4eb8708e406e99246987b087ecc1e1babeece1a0b1d5238b1750" +dependencies = [ + "ckb_schemars_derive", + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "ckb_schemars_derive" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c813b4fadbdd9f33b1cf02a1ddfa9537d955c8d2fbe150d1fc1684dbf78e73" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.77", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "eaglesong" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d978bd5d343e8ab9b5c0fc8d93ff9c602fdc96616ffff9c05ac7a155419b824" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "exec-child" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", +] + +[[package]] +name = "exec-child-sim" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", + "exec-child", +] + +[[package]] +name = "exec-parent" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", +] + +[[package]] +name = "exec-parent-sim" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", + "exec-parent", +] + +[[package]] +name = "faster-hex" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e2ce894d53b295cf97b05685aa077950ff3e8541af83217fc720a6437169f8" + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "flate2" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "goblin" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d20fd25aa456527ce4f544271ae4fea65d2eda4a6561ea56f39fb3ee4f7e3884" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "goblin" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532a09cd3df2c6bbfc795fb0434bff8f22255d1d07328180e918a2e6ce122d4d" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "golomb-coded-set" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f314a99fb5b7f0f9d0a8388539578f83f3aca6a65f588b8dbeefb731e2f98" +dependencies = [ + "siphasher", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heapsize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" +dependencies = [ + "winapi", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "includedir" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afd126bd778c00c43a9dc76d1609a0894bf4222088088b2217ccc0ce9e816db7" +dependencies = [ + "flate2", + "phf", +] + +[[package]] +name = "includedir_codegen" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac1500c9780957c9808c4ec3b94002f35aab01483833f5a8bce7dfb243e3148" +dependencies = [ + "flate2", + "phf_codegen", + "walkdir", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "merkle-cbt" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171d2f700835121c3b04ccf0880882987a050fd5c7ae88148abf537d33dd3a56" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "miette" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" +dependencies = [ + "miette-derive", + "once_cell", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "molecule" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6efe1c7efcd0bdf4ca590e104bcb13087d9968956ae4ae98e92fb8c1da0f3730" +dependencies = [ + "bytes", + "cfg-if", + "faster-hex", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "numext-constructor" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "621fe0f044729f810c6815cdd77e8f5e0cd803ce4f6a38380ebfc1322af98661" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "numext-fixed-uint" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c68c76f96d589d1009a666c5072f37f3114d682696505f2cf445f27766c7d70" +dependencies = [ + "numext-fixed-uint-core", + "numext-fixed-uint-hack", +] + +[[package]] +name = "numext-fixed-uint-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aab1d6457b97b49482f22a92f0f58a2f39bdd7f3b2f977eae67e8bc206aa980" +dependencies = [ + "heapsize", + "numext-constructor", + "rand 0.7.3", + "serde", + "thiserror", +] + +[[package]] +name = "numext-fixed-uint-hack" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200f8d55c36ec1b6a8cf810115be85d4814f045e0097dfd50033ba25adb4c9e" +dependencies = [ + "numext-fixed-uint-core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared", + "rand 0.7.3", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "reflink-copy" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc31414597d1cd7fdd2422798b7652a6329dda0fe0219e6335a13d5bcaa9aeb6" +dependencies = [ + "cfg-if", + "rustix", + "windows", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scroll" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "spawn-cases" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", + "spawn_cmd", +] + +[[package]] +name = "spawn-cases-sim" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", + "spawn-cases", +] + +[[package]] +name = "spawn-child" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", + "spawn_cmd", +] + +[[package]] +name = "spawn-child-sim" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", + "spawn-child", +] + +[[package]] +name = "spawn-parent" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", + "spawn_cmd", +] + +[[package]] +name = "spawn-parent-sim" +version = "0.1.0" +dependencies = [ + "ckb-std-wrapper", + "spawn-parent", +] + +[[package]] +name = "spawn_cmd" +version = "0.1.0" +dependencies = [ + "num-derive", + "num-traits", +] + +[[package]] +name = "ssri" +version = "9.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7a2b3c2bc9693bcb40870c4e9b5bf0d79f9cb46273321bf855ec513e919082" +dependencies = [ + "base64", + "digest", + "hex", + "miette", + "serde", + "sha-1", + "sha2", + "thiserror", + "xxhash-rust", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "tests" +version = "0.1.0" +dependencies = [ + "ckb-testtool 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json", + "spawn_cmd", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "xxhash-rust" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 0000000..28f3e1c --- /dev/null +++ b/tests/Cargo.toml @@ -0,0 +1,35 @@ +[workspace] +resolver = "2" + +members = [ + # Please don't remove the following line, we use it to automatically + # detect insertion point for newly generated crates. + # @@INSERTION_POINT@@ + "native-simulators/spawn-cases-sim", + "native-simulators/spawn-child-sim", + "native-simulators/spawn-parent-sim", + "native-simulators/exec-child-sim", + "native-simulators/exec-parent-sim", + "contracts/spawn-cases", + "contracts/spawn-child", + "contracts/spawn-parent", + "contracts/exec-child", + "contracts/exec-parent", + "contracts/exec-parent", + "libs/spawn_cmd", + "libs/ckb-std-wrapper", + "tests", +] + +[profile.release] +overflow-checks = true +strip = false +codegen-units = 1 +debug = true + +[replace] +"ckb-x64-simulator:0.9.2" = { path = "../" } + +# TODO: Will be deleted after release +"ckb-testtool:0.13.1" = { git = "https://github.com/joii2020/ckb-testtool.git", rev = "281c4ec" } +# "ckb-testtool:0.13.1" = { path = "../../ckb-testtool" } diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..c9cb6cc --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,150 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +# When this is set, a single contract will be built instead of all contracts +CONTRACT := +# By default, we would clean build/{release,debug} folder first, in case old +# contracts are mixed together with new ones, if for some reason you want to +# revert this behavior, you can change this to anything other than true +CLEAN_BUILD_DIR_FIRST := true +BUILD_DIR := build/$(MODE) + +# Pass setups to child make processes +export CUSTOM_RUSTFLAGS +export TOP +export CARGO_ARGS +export MODE +export CLANG +export BUILD_DIR + +default: check clippy build test test-contracts + +build: + @if [ "x$(CLEAN_BUILD_DIR_FIRST)" = "xtrue" ]; then \ + echo "Cleaning $(BUILD_DIR) directory..."; \ + rm -rf $(BUILD_DIR); \ + fi + mkdir -p $(BUILD_DIR) + @set -eu; \ + if [ "x$(CONTRACT)" = "x" ]; then \ + for contract in $(wildcard contracts/*); do \ + $(MAKE) -e -C $$contract build; \ + done; \ + else \ + $(MAKE) -e -C contracts/$(CONTRACT) build; \ + fi; \ + for sim in $(wildcard native-simulators/*); do \ + cd $$sim && cargo build && cd ../..; \ + done; \ + +# Run a single make task for a specific contract. For example: +# +# make run CONTRACT=stack-reorder TASK=adjust_stack_size STACK_SIZE=0x200000 +TASK := +run: + $(MAKE) -e -C contracts/$(CONTRACT) $(TASK) + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: build + cargo test $(CARGO_ARGS) -- --test-threads=1 + +test-contracts: build + cargo test --no-default-features $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + rm -rf build + cargo clean + +TEMPLATE_TYPE := --git +TEMPLATE_REPO := https://github.com/cryptape/ckb-script-templates +CRATE := +TEMPLATE := contract +DESTINATION := contracts +generate: + @set -eu; \ + if [ "x$(CRATE)" = "x" ]; then \ + cargo generate $(TEMPLATE_TYPE) $(TEMPLATE_REPO) $(TEMPLATE) \ + --destination $(DESTINATION); \ + GENERATED_DIR=$$(ls -dt $(DESTINATION)/* | head -n 1); \ + if [ -f "$$GENERATED_DIR/.cargo-generate/tests.rs" ]; then \ + cat $$GENERATED_DIR/.cargo-generate/tests.rs >> tests/src/tests.rs; \ + rm -rf $$GENERATED_DIR/.cargo-generate/; \ + fi; \ + sed "s,@@INSERTION_POINT@@,@@INSERTION_POINT@@\n \"$$GENERATED_DIR\"\,," Cargo.toml > Cargo.toml.new; \ + mv Cargo.toml.new Cargo.toml; \ + else \ + cargo generate $(TEMPLATE_TYPE) $(TEMPLATE_REPO) $(TEMPLATE) \ + --destination $(DESTINATION) \ + --name $(CRATE); \ + if [ -f "$(DESTINATION)/$(CRATE)/.cargo-generate/tests.rs" ]; then \ + cat $(DESTINATION)/$(CRATE)/.cargo-generate/tests.rs >> tests/src/tests.rs; \ + rm -rf $(DESTINATION)/$(CRATE)/.cargo-generate/; \ + fi; \ + sed '/@@INSERTION_POINT@@/s/$$/\n "$(DESTINATION)\/$(CRATE)",/' Cargo.toml > Cargo.toml.new; \ + mv Cargo.toml.new Cargo.toml; \ + fi; + +generate-native-simulator: + @set -eu; \ + cargo generate $(TEMPLATE_TYPE) $(TEMPLATE_REPO) native-simulator \ + -n $(CRATE)-sim \ + --destination native-simulators \ + -d contract_name=$(CRATE) \ + -d contract_crate_name=`echo "$(CRATE)" | tr '-' '_'`; \ + mv native-simulators/$(CRATE)-sim/src/contract-lib.rs contracts/$(CRATE)/src/lib.rs; \ + FILE=contracts/$(CRATE)/Cargo.toml; \ + if grep -q "\\[features\\]" "$$FILE"; then \ + sed -i '/\[features\]/a\\native-simulator = \[\"ckb-std/native-simulator\"\]' $$FILE; \ + else \ + echo "\\n[features]\\nnative-simulator = [\"ckb-std/native-simulator\"]\\n" >> $$FILE ; \ + fi; \ + FILE=contracts/$(CRATE)/src/main.rs; \ + sed -i 's/#!\[no_std\]/#!\[cfg_attr(not(feature = "native-simulator"), no_std)\]/' $$FILE; \ + sed -i 's/#\[cfg(test)\]/#\[cfg(any(feature = "native-simulator", test))\]/' $$FILE; \ + sed -i 's/#\[cfg(not(test))\]/#\[cfg(not(any(feature = "native-simulator", test)))\]/' $$FILE; \ + sed '/@@INSERTION_POINT@@/s/$$/\n "native-simulators\/$(CRATE)-sim",/' Cargo.toml > Cargo.toml.new; \ + mv Cargo.toml.new Cargo.toml; + +prepare: + rustup target add riscv64imac-unknown-none-elf + +# Generate checksum info for reproducible build +CHECKSUM_FILE := build/checksums-$(MODE).txt +checksum: build + shasum -a 256 build/$(MODE)/* > $(CHECKSUM_FILE) + +.PHONY: build test check clippy fmt cargo clean prepare checksum diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..4aaaff0 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,12 @@ +# test-workspace + +This project is generated using [ckb-script-templates](https://github.com/cryptape/ckb-script-templates) and is mainly intended to demonstrate how to debug contracts using the native simulator. + +## Contracts +* exec-parent\exec-child +* spawn-parent\spawn-child : Support will be added once new spawn is completed + +## Native Simulator Debugging +First, compile the project by running `make build-simulator`. Then, enable the `simulator` feature in `tests`. For convenience, you can also set `default = [ "simulator" ]` directly in `tests/Cargo.toml`. + +A test case called `test_exec` is provided in `tests/src/tests.rs`. After enabling the `simulator`, you can use native simulator debugging and set breakpoints in the IDE to debug the contract. diff --git a/tests/contracts/exec-child/.gitignore b/tests/contracts/exec-child/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/contracts/exec-child/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/contracts/exec-child/Cargo.toml b/tests/contracts/exec-child/Cargo.toml new file mode 100644 index 0000000..951a9e2 --- /dev/null +++ b/tests/contracts/exec-child/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "exec-child" +version = "0.1.0" +edition = "2021" + +[dependencies] +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } + +[features] +native-simulator = ["ckb-std-wrapper/native-simulator"] + diff --git a/tests/contracts/exec-child/Makefile b/tests/contracts/exec-child/Makefile new file mode 100644 index 0000000..e4ee221 --- /dev/null +++ b/tests/contracts/exec-child/Makefile @@ -0,0 +1,80 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +OBJCOPY := $(subst clang,llvm-objcopy,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := $(notdir $(shell pwd)) + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + cp $(TOP)/$(BUILD_DIR)/$$binary $(TOP)/$(BUILD_DIR)/$$binary.debug; \ + $(OBJCOPY) --strip-debug --strip-all $(TOP)/$(BUILD_DIR)/$$binary; \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/tests/contracts/exec-child/README.md b/tests/contracts/exec-child/README.md new file mode 100644 index 0000000..4af325b --- /dev/null +++ b/tests/contracts/exec-child/README.md @@ -0,0 +1,7 @@ +# exec-child + +TODO: Write this readme + +*This contract was bootstrapped with [ckb-script-templates].* + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/contracts/exec-child/src/lib.rs b/tests/contracts/exec-child/src/lib.rs new file mode 100644 index 0000000..7b58725 --- /dev/null +++ b/tests/contracts/exec-child/src/lib.rs @@ -0,0 +1,7 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![allow(special_module_name)] +#![allow(unused_attributes)] +#[cfg(feature = "native-simulator")] +mod main; +#[cfg(feature = "native-simulator")] +pub use main::program_entry; diff --git a/tests/contracts/exec-child/src/main.rs b/tests/contracts/exec-child/src/main.rs new file mode 100644 index 0000000..e1437c0 --- /dev/null +++ b/tests/contracts/exec-child/src/main.rs @@ -0,0 +1,25 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![cfg_attr(not(test), no_main)] + +#[cfg(any(feature = "native-simulator", test))] +extern crate alloc; + +#[cfg(not(any(feature = "native-simulator", test)))] +use ckb_std::default_alloc; +#[cfg(not(any(feature = "native-simulator", test)))] +ckb_std::entry!(program_entry); +#[cfg(not(any(feature = "native-simulator", test)))] +default_alloc!(); +use ckb_std_wrapper::ckb_std; + +pub fn program_entry() -> i8 { + ckb_std::debug!("This is a sample contract exec-child!"); + + let argv = ckb_std::env::argv(); + ckb_std::debug!("argv: {:?}", argv); + assert_eq!(argv.len(), 2); + assert_eq!(argv[0].to_bytes(), b"Hello World"); + assert_eq!(argv[1].to_bytes(), "你好".as_bytes()); + + 0 +} diff --git a/tests/contracts/exec-parent/.gitignore b/tests/contracts/exec-parent/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/contracts/exec-parent/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/contracts/exec-parent/Cargo.toml b/tests/contracts/exec-parent/Cargo.toml new file mode 100644 index 0000000..efe3442 --- /dev/null +++ b/tests/contracts/exec-parent/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "exec-parent" +version = "0.1.0" +edition = "2021" + +[dependencies] +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } + +[features] +native-simulator = ["ckb-std-wrapper/native-simulator"] + diff --git a/tests/contracts/exec-parent/Makefile b/tests/contracts/exec-parent/Makefile new file mode 100644 index 0000000..e4ee221 --- /dev/null +++ b/tests/contracts/exec-parent/Makefile @@ -0,0 +1,80 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +OBJCOPY := $(subst clang,llvm-objcopy,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := $(notdir $(shell pwd)) + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + cp $(TOP)/$(BUILD_DIR)/$$binary $(TOP)/$(BUILD_DIR)/$$binary.debug; \ + $(OBJCOPY) --strip-debug --strip-all $(TOP)/$(BUILD_DIR)/$$binary; \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/tests/contracts/exec-parent/README.md b/tests/contracts/exec-parent/README.md new file mode 100644 index 0000000..3958b57 --- /dev/null +++ b/tests/contracts/exec-parent/README.md @@ -0,0 +1,7 @@ +# exec-parent + +TODO: Write this readme + +*This contract was bootstrapped with [ckb-script-templates].* + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/contracts/exec-parent/src/lib.rs b/tests/contracts/exec-parent/src/lib.rs new file mode 100644 index 0000000..7b58725 --- /dev/null +++ b/tests/contracts/exec-parent/src/lib.rs @@ -0,0 +1,7 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![allow(special_module_name)] +#![allow(unused_attributes)] +#[cfg(feature = "native-simulator")] +mod main; +#[cfg(feature = "native-simulator")] +pub use main::program_entry; diff --git a/tests/contracts/exec-parent/src/main.rs b/tests/contracts/exec-parent/src/main.rs new file mode 100644 index 0000000..506c230 --- /dev/null +++ b/tests/contracts/exec-parent/src/main.rs @@ -0,0 +1,52 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![cfg_attr(not(test), no_main)] + +#[cfg(any(feature = "native-simulator", test))] +extern crate alloc; + +#[cfg(not(any(feature = "native-simulator", test)))] +use ckb_std::default_alloc; +#[cfg(not(any(feature = "native-simulator", test)))] +ckb_std::entry!(program_entry); +#[cfg(not(any(feature = "native-simulator", test)))] +default_alloc!(); +use ckb_std_wrapper::ckb_std; + +use ckb_std::ckb_types::bytes::Bytes; +use ckb_std::ckb_types::core::ScriptHashType; +use ckb_std::ckb_types::prelude::Unpack; +use ckb_std::debug; +use core::ffi::CStr; + +pub fn program_entry() -> i8 { + debug!("This is a sample contract exec-parent!"); + + if let Ok(script) = ckb_std::high_level::load_script() { + let args: Bytes = script.args().unpack(); + let args = args.to_vec(); + + if args.len() < 33 { + debug!("args len loss 33: {}", args.len()); + return 1; + } + let hash_type = match args[32] { + 0 => ScriptHashType::Data, + 1 => ScriptHashType::Type, + 2 => ScriptHashType::Data1, + 4 => ScriptHashType::Data2, + _ => { + debug!("unknow hash type : {}", args[32]); + return 2; + } + }; + let arg1 = CStr::from_bytes_with_nul(b"Hello World\0").unwrap(); + let arg2 = CStr::from_bytes_with_nul("你好\0".as_bytes()).unwrap(); + + let rc = ckb_std::high_level::exec_cell(&args[..32], hash_type, &[arg1, arg2]).unwrap_err(); + debug!("exec_cell faield: {:?}", rc); + 3 + } else { + debug!("load script failed"); + 4 + } +} diff --git a/tests/contracts/spawn-cases/.gitignore b/tests/contracts/spawn-cases/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/contracts/spawn-cases/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/contracts/spawn-cases/Cargo.toml b/tests/contracts/spawn-cases/Cargo.toml new file mode 100644 index 0000000..8941a3d --- /dev/null +++ b/tests/contracts/spawn-cases/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "spawn-cases" +version = "0.1.0" +edition = "2021" + +[dependencies] +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } +spawn_cmd = { path = "../../libs/spawn_cmd" } + +[features] +native-simulator = ["ckb-std-wrapper/native-simulator"] + diff --git a/tests/contracts/spawn-cases/Makefile b/tests/contracts/spawn-cases/Makefile new file mode 100644 index 0000000..e4ee221 --- /dev/null +++ b/tests/contracts/spawn-cases/Makefile @@ -0,0 +1,80 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +OBJCOPY := $(subst clang,llvm-objcopy,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := $(notdir $(shell pwd)) + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + cp $(TOP)/$(BUILD_DIR)/$$binary $(TOP)/$(BUILD_DIR)/$$binary.debug; \ + $(OBJCOPY) --strip-debug --strip-all $(TOP)/$(BUILD_DIR)/$$binary; \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/tests/contracts/spawn-cases/README.md b/tests/contracts/spawn-cases/README.md new file mode 100644 index 0000000..0d5ef66 --- /dev/null +++ b/tests/contracts/spawn-cases/README.md @@ -0,0 +1,7 @@ +# spawn-cases + +TODO: Write this readme + +*This contract was bootstrapped with [ckb-script-templates].* + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/contracts/spawn-cases/src/lib.rs b/tests/contracts/spawn-cases/src/lib.rs new file mode 100644 index 0000000..7b58725 --- /dev/null +++ b/tests/contracts/spawn-cases/src/lib.rs @@ -0,0 +1,7 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![allow(special_module_name)] +#![allow(unused_attributes)] +#[cfg(feature = "native-simulator")] +mod main; +#[cfg(feature = "native-simulator")] +pub use main::program_entry; diff --git a/tests/contracts/spawn-cases/src/main.rs b/tests/contracts/spawn-cases/src/main.rs new file mode 100644 index 0000000..ba047d4 --- /dev/null +++ b/tests/contracts/spawn-cases/src/main.rs @@ -0,0 +1,574 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![cfg_attr(not(test), no_main)] + +#[cfg(not(any(feature = "native-simulator", test)))] +ckb_std::entry!(program_entry); +#[cfg(not(any(feature = "native-simulator", test)))] +ckb_std::default_alloc!(); + +#[cfg(any(feature = "native-simulator", test))] +extern crate alloc; +use alloc::vec::Vec; +use ckb_std::ckb_constants::Source; +use ckb_std::ckb_types::bytes::Bytes; +use ckb_std::ckb_types::core::ScriptHashType; +use ckb_std::ckb_types::prelude::Unpack; +use ckb_std::syscalls::SysError; +use ckb_std::{debug, syscalls}; +use ckb_std_wrapper::ckb_std; +use core::ffi::CStr; +use spawn_cmd::SpawnCasesCmd; + +const CKB_STDIN: usize = 0; +const CKB_STDOUT: usize = 1; + +fn error_to_code(err: SysError) -> u64 { + match err { + SysError::IndexOutOfBound => 1, + SysError::ItemMissing => 2, + SysError::LengthNotEnough(_usize) => 3, + SysError::Encoding => 4, + SysError::WaitFailure => 5, + SysError::InvalidFd => 6, + SysError::OtherEndClosed => 7, + SysError::MaxVmsSpawned => 8, + SysError::MaxFdsCreated => 9, + SysError::Unknown(code) => code, + } +} + +fn create_std_fds() -> Result<([u64; 2], [u64; 3]), SysError> { + let (r0, w0) = syscalls::pipe()?; + let (r1, w1) = syscalls::pipe()?; + Ok(([r0, w1], [r1, w0, 0])) +} +fn new_spawn(args: &[&str], inherited_fds: &[u64]) -> Result { + let args_buf: Vec> = args.iter().map(|f| [f.as_bytes(), &[0]].concat()).collect(); + let c_args: Vec<&CStr> = args_buf + .iter() + .map(|f| unsafe { CStr::from_bytes_with_nul_unchecked(f) }) + .collect(); + let pid = ckb_std::high_level::spawn_cell( + &ckb_std::high_level::load_cell_lock(0, Source::GroupInput)? + .code_hash() + .raw_data(), + ScriptHashType::Data2, + &c_args, + inherited_fds, + )?; + Ok(pid) +} +fn full_spawn(args: &[&str]) -> Result<(u64, [u64; 2]), SysError> { + let (fds, inherited_fds) = create_std_fds()?; + let pid = new_spawn(args, &inherited_fds)?; + Ok((pid, fds)) +} +fn write_exact(fd: u64, buf: &[u8], actual_length: &mut usize) -> Result<(), SysError> { + let mut w_buf = buf; + *actual_length = 0; + while !w_buf.is_empty() { + let n = syscalls::write(fd, w_buf)?; + *actual_length += n; + w_buf = &w_buf[n..]; + } + Ok(()) +} +fn read_exact(fd: u64, buf: &mut [u8], actual_length: &mut usize) -> Result<(), SysError> { + let mut r_buf = buf; + *actual_length = 0; + while !r_buf.is_empty() { + let n = syscalls::read(fd, r_buf)?; + *actual_length += n; + r_buf = &mut r_buf[n..]; + } + + Ok(()) +} + +fn parent_simple_read_write() -> Result, SysError> { + let (pid, fds) = full_spawn(&[""])?; + + let block = [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ]; + // write + for _i in 0..7 { + let mut actual_length = 0; + debug!("-P- {} WBegin len: {}", _i, block.len()); + write_exact(fds[CKB_STDOUT], &block, &mut actual_length)?; + if actual_length != block.len() { + return Err(SysError::Unknown(-2i64 as u64)); + } + debug!("-P- {} WEnd, actual_length: {}", _i, actual_length); + } + + debug!("-P- --------"); + // read + for _i in 0..7 { + let mut actual_length = 0; + let mut block = [0u8; 11]; + debug!("-P- {} RBegin len: {}", _i, block.len()); + read_exact(fds[CKB_STDIN], &mut block, &mut actual_length)?; + + if actual_length != block.len() { + return Err(SysError::Unknown(-2i64 as u64)); + } + if block.iter().any(|v| v != &0xff) { + return Err(SysError::Unknown(-2i64 as u64)); + } + debug!("-P- {} REnd actual_length: {}", _i, actual_length); + } + + Ok(Some(pid)) +} +fn child_simple_read_write() -> Result<(), SysError> { + let mut inherited_fds = [0u64; 2]; + syscalls::inherited_fds(&mut inherited_fds); + + for _i in 0..11 { + let mut block = [0u8; 7]; + let mut actual_length = 0; + debug!("-C- {} RBegin len: {}", _i, block.len()); + read_exact(inherited_fds[CKB_STDIN], &mut block, &mut actual_length)?; + if actual_length != block.len() { + return Err(SysError::Unknown(-2i64 as u64)); + } + if block.iter().any(|v| v != &0xff) { + return Err(SysError::Unknown(-3i64 as u64)); + } + debug!("-C- {} REnd, actual_length: {}", _i, actual_length); + } + + debug!("-C- --------"); + let block = [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ]; + for _i in 0..7 { + debug!("-C- {} WBegin len: {}", _i, block.len()); + let mut actual_length = 0; + write_exact(inherited_fds[CKB_STDOUT], &block, &mut actual_length)?; + if actual_length != block.len() { + return Err(SysError::Unknown(-2i64 as u64)); + } + debug!("-C- {} WEnd actual_length: {}", _i, actual_length); + } + + Ok(()) +} + +fn parent_write_dead_lock() -> Result, SysError> { + let (pid, fds) = full_spawn(&[""])?; + + let data = [0u8; 10]; + syscalls::write(fds[CKB_STDOUT], &data)?; + + Ok(Some(pid)) +} +fn child_write_dead_lock() -> Result<(), SysError> { + let mut inherited_fds = [0u64; 2]; + syscalls::inherited_fds(&mut inherited_fds); + + let data = [0u8; 10]; + syscalls::write(inherited_fds[CKB_STDOUT], &data)?; + + Ok(()) +} + +fn parent_invalid_fd() -> Result, SysError> { + let mut data = [0u8; 4]; + + let invalid_fd = 0xff; + let err = syscalls::read(invalid_fd, &mut data).unwrap_err(); + if err != SysError::InvalidFd { + return Err(SysError::Unknown(-2i64 as u64)); + } + + let err = syscalls::write(invalid_fd, &data).unwrap_err(); + if err != SysError::InvalidFd { + return Err(SysError::Unknown(-2i64 as u64)); + } + + let (r, w) = syscalls::pipe()?; + let err = syscalls::read(w, &mut data).unwrap_err(); + if err != SysError::InvalidFd { + return Err(SysError::Unknown(-2i64 as u64)); + } + let err = syscalls::write(r, &data).unwrap_err(); + if err != SysError::InvalidFd { + return Err(SysError::Unknown(-2i64 as u64)); + } + + let inherited_fds = [r, 0]; + let args = [""]; + let args_buf: Vec> = args.iter().map(|f| [f.as_bytes(), &[0]].concat()).collect(); + let c_args: Vec<&CStr> = args_buf + .iter() + .map(|f| unsafe { CStr::from_bytes_with_nul_unchecked(f) }) + .collect(); + let pid = ckb_std::high_level::spawn_cell( + &ckb_std::high_level::load_cell_lock(0, Source::GroupInput)? + .code_hash() + .raw_data(), + ScriptHashType::Data2, + &c_args, + &inherited_fds, + )?; + + let mut buf = [0u8; 4]; + let err = syscalls::read(r, &mut buf).unwrap_err(); + assert_eq!(err, SysError::InvalidFd); + + let (r1, w1) = syscalls::pipe()?; + syscalls::close(r1)?; + let buf = [0xfu8; 4]; + let err = syscalls::write(w1, &buf).unwrap_err(); + assert_eq!(err, SysError::OtherEndClosed); + + let (r1, w1) = syscalls::pipe()?; + syscalls::close(w1)?; + let mut buf = [0xfu8; 4]; + let err = syscalls::read(r1, &mut buf).unwrap_err(); + assert_eq!(err, SysError::OtherEndClosed); + + syscalls::wait(pid)?; + + Ok(None) +} + +fn parent_wait_dead_lock() -> Result, SysError> { + let (pid, _) = full_spawn(&[""])?; + Ok(Some(pid)) +} +fn child_wait_dead_lock() -> Result<(), SysError> { + let pid = 0; + syscalls::wait(pid)?; + Ok(()) +} + +fn parent_read_write_with_close() -> Result, SysError> { + let (pid, fds) = full_spawn(&[""])?; + let block = [0xFFu8; 100]; + let mut actual_length = 0; + write_exact(fds[CKB_STDOUT], &block, &mut actual_length)?; + + if actual_length != block.len() { + return Err(SysError::Unknown(-2i64 as u64)); + } + + Ok(Some(pid)) +} +fn child_read_write_with_close() -> Result<(), SysError> { + let mut inherited_fds = [0u64; 2]; + syscalls::inherited_fds(&mut inherited_fds); + + let mut block = [0u8; 100]; + let mut actual_length = 0; + read_exact(inherited_fds[CKB_STDIN], &mut block, &mut actual_length)?; + if actual_length != block.len() { + return Err(SysError::Unknown(-2i64 as u64)); + } + + if block.iter().any(|v| v != &0xFF) { + return Err(SysError::Unknown(-3i64 as u64)); + } + + syscalls::close(inherited_fds[CKB_STDIN])?; + + Ok(()) +} + +fn parent_wait_multiple() -> Result, SysError> { + let (pid, _fds) = full_spawn(&[""])?; + + let exit_code = syscalls::wait(pid)?; + assert_eq!(exit_code, 0); + + let err = syscalls::wait(pid).unwrap_err(); + assert_eq!(err, SysError::WaitFailure); + + let (pid, _fds) = full_spawn(&[""])?; + + Ok(Some(pid)) +} + +fn parent_inherited_fds() -> Result, SysError> { + let mut inherited_fds = [0u64; 11]; + for i in 0..5 { + let (r, w) = syscalls::pipe()?; + inherited_fds[i * 2] = r; + inherited_fds[i * 2 + 1] = w; + } + + let pid = new_spawn(&[""], &inherited_fds)?; + Ok(Some(pid)) +} +fn child_inherited_fds() -> Result<(), SysError> { + let mut inherited_fds = [0u64; 10]; + syscalls::inherited_fds(&mut inherited_fds); + + for i in 0u64..10u64 { + if inherited_fds[i as usize] != i + 2 { + return Err(SysError::Unknown(-2i64 as u64)); + } + } + + Ok(()) +} + +fn parent_inherited_fds_without_owner() -> Result, SysError> { + let fds = [0xFF, 0xFF, 0]; + let err = new_spawn(&[""], &fds).unwrap_err(); + assert_eq!(err, SysError::InvalidFd); + + let (r, w) = syscalls::pipe()?; + + let pid = new_spawn(&[""], &[r, w, 0])?; + let err = new_spawn(&[""], &[r, w, 0]).unwrap_err(); + assert_eq!(err, SysError::InvalidFd); + + Ok(Some(pid)) +} + +fn parent_read_then_close() -> Result, SysError> { + let (pid, fds) = full_spawn(&[""])?; + debug!("-P- Spawn end, fds: {:?}", fds); + syscalls::close(fds[CKB_STDOUT])?; + debug!("-P- Close fd: {}", fds[CKB_STDOUT]); + Ok(Some(pid)) +} +fn child_read_then_close() -> Result<(), SysError> { + let mut fds = [0u64; 2]; + syscalls::inherited_fds(&mut fds); + debug!("-C- inherited fds {:?}", fds); + + let mut data = [0u8; 8]; + debug!("-C- Read begin"); + let data_len = syscalls::read(fds[CKB_STDIN], &mut data)?; + debug!("-C- data len : {}", data_len); + + let err = syscalls::read(fds[CKB_STDIN], &mut data).unwrap_err(); + if err != SysError::OtherEndClosed { + return Err(SysError::Unknown(-2i64 as u64)); + } + + Ok(()) +} + +fn parent_max_vms_count() -> Result, SysError> { + let pid = new_spawn(&[""], &[0])?; + debug!("-P- pid: {}", pid); + Ok(Some(pid)) +} +fn child_max_vms_count() -> Result<(), SysError> { + match new_spawn(&[""], &[0]) { + Ok(_pid) => { + debug!("-C- pid: {}", _pid); + Ok(()) + } + Err(e) => { + if e == SysError::MaxVmsSpawned { + Ok(()) + } else { + Err(e) + } + } + } +} + +fn parent_max_fds_limit() -> Result, SysError> { + for _ in 0..16 { + let _ = syscalls::pipe()?; + } + + new_spawn(&[""], &[0])?; + Ok(None) +} +fn child_max_fds_limit() -> Result<(), SysError> { + for _ in 0..16 { + let _ = syscalls::pipe()?; + } + + let err = syscalls::pipe().unwrap_err(); + assert_eq!(err, SysError::MaxFdsCreated); + + Ok(()) +} + +fn parent_close_invalid_fd() -> Result, SysError> { + let fds = syscalls::pipe()?; + + let err = syscalls::close(fds.0 + 32).unwrap_err(); + assert_eq!(err, SysError::InvalidFd); + + syscalls::close(fds.0)?; + syscalls::close(fds.1)?; + + let err = syscalls::close(fds.0).unwrap_err(); + assert_eq!(err, SysError::InvalidFd); + let err = syscalls::close(fds.1).unwrap_err(); + assert_eq!(err, SysError::InvalidFd); + + Ok(None) +} + +fn parent_write_closed_fd() -> Result, SysError> { + let (pid, fds) = full_spawn(&[""])?; + + let mut block = [0u8; 7]; + let mut actual_length = 0; + debug!("-P- Read begin"); + read_exact(fds[CKB_STDIN], &mut block, &mut actual_length)?; + debug!("-P- Read end"); + + assert_eq!(actual_length, block.len()); + + debug!("-P- Close1 begin"); + syscalls::close(fds[CKB_STDIN])?; + debug!("-P- Close2 begin"); + syscalls::close(fds[CKB_STDOUT])?; + debug!("-P- Close end"); + + Ok(Some(pid)) +} +fn child_write_closed_fd() -> Result<(), SysError> { + let mut fds = [0, 2]; + syscalls::inherited_fds(&mut fds); + + debug!("-C- Write1 begin"); + let block = [0u8; 7]; + let mut actual_length = 0; + write_exact(fds[CKB_STDOUT], &block, &mut actual_length)?; + debug!("-C- Write2 begin"); + let err = write_exact(fds[CKB_STDOUT], &block, &mut actual_length).unwrap_err(); + assert_eq!(err, SysError::OtherEndClosed); + debug!("-C- Write end"); + + debug!("-C- Close1 begin"); + syscalls::close(fds[CKB_STDIN])?; + debug!("-C- Close2 begin"); + syscalls::close(fds[CKB_STDOUT])?; + debug!("-C- Close end"); + + Ok(()) +} + +fn parent_spawn_pid() -> Result, SysError> { + let cur_pid = syscalls::process_id(); + assert_eq!(cur_pid, 0); + + let (pid_1, fds_1) = full_spawn(&[""])?; + assert_eq!(pid_1, 1); + + let (pid_2, fds_2) = full_spawn(&[""])?; + assert_eq!(pid_2, 2); + + let mut buf = [0u8; 8]; + let mut actual_length = 0; + read_exact(fds_1[CKB_STDIN], &mut buf, &mut actual_length)?; + assert_eq!(pid_1, u64::from_le_bytes(buf)); + + let mut buf = [0u8; 8]; + let mut actual_length = 0; + read_exact(fds_2[CKB_STDIN], &mut buf, &mut actual_length)?; + assert_eq!(pid_2, u64::from_le_bytes(buf)); + + Ok(None) +} +fn child_spawn_pid() -> Result<(), SysError> { + let pid = syscalls::process_id(); + + let mut fds = [0; 2]; + syscalls::inherited_fds(&mut fds); + + let mut actual_length = 0; + write_exact(fds[CKB_STDOUT], &pid.to_le_bytes(), &mut actual_length)?; + + Ok(()) +} + +fn parent_entry(cmd: SpawnCasesCmd) -> i8 { + debug!("-P- Begin cmd: {:?}, pid: {}", cmd, syscalls::process_id()); + + let ret = match cmd { + SpawnCasesCmd::Unknow => panic!("pass"), + SpawnCasesCmd::ReadWrite => parent_simple_read_write(), + SpawnCasesCmd::WriteDeadLock => parent_write_dead_lock(), + SpawnCasesCmd::InvalidFd => parent_invalid_fd(), + SpawnCasesCmd::WaitDeadLock => parent_wait_dead_lock(), + SpawnCasesCmd::ReadWriteWithClose => parent_read_write_with_close(), + SpawnCasesCmd::WaitMultiple => parent_wait_multiple(), + SpawnCasesCmd::InheritedFds => parent_inherited_fds(), + SpawnCasesCmd::InheritedFdsWithoutOwner => parent_inherited_fds_without_owner(), + SpawnCasesCmd::ReadThenClose => parent_read_then_close(), + SpawnCasesCmd::MaxVmsCount => parent_max_vms_count(), + SpawnCasesCmd::MaxFdsLimit => parent_max_fds_limit(), + SpawnCasesCmd::CloseInvalidFd => parent_close_invalid_fd(), + SpawnCasesCmd::WriteClosedFd => parent_write_closed_fd(), + SpawnCasesCmd::CheckPID => parent_spawn_pid(), + }; + + let code = match ret { + Ok(pid) => { + if let Some(pid) = pid { + debug!("-P- Wait Child"); + let exit_code: i8 = syscalls::wait(pid).unwrap(); + exit_code + } else { + 0 + } + } + Err(e) => error_to_code(e) as i8, + }; + + debug!("-P- End, code: {}", code); + code +} +fn child_entry(cmd: SpawnCasesCmd) -> i8 { + debug!("-C- Begin cmd: {:?}, pid: {}", cmd, syscalls::process_id()); + let ret = match cmd { + SpawnCasesCmd::Unknow => panic!("unsupport"), + SpawnCasesCmd::ReadWrite => child_simple_read_write(), + SpawnCasesCmd::WriteDeadLock => child_write_dead_lock(), + SpawnCasesCmd::InvalidFd => Ok(()), + SpawnCasesCmd::WaitDeadLock => child_wait_dead_lock(), + SpawnCasesCmd::ReadWriteWithClose => child_read_write_with_close(), + SpawnCasesCmd::WaitMultiple => Ok(()), + SpawnCasesCmd::InheritedFds => child_inherited_fds(), + SpawnCasesCmd::InheritedFdsWithoutOwner => Ok(()), + SpawnCasesCmd::ReadThenClose => child_read_then_close(), + SpawnCasesCmd::MaxVmsCount => child_max_vms_count(), + SpawnCasesCmd::MaxFdsLimit => child_max_fds_limit(), + SpawnCasesCmd::CloseInvalidFd => Ok(()), + SpawnCasesCmd::WriteClosedFd => child_write_closed_fd(), + SpawnCasesCmd::CheckPID => child_spawn_pid(), + }; + + let code = match ret { + Ok(_) => 0, + Err(e) => error_to_code(e) as i8, + }; + debug!("-C- End code: {:?}", code); + code +} + +pub fn program_entry() -> i8 { + match ckb_std::high_level::load_script() { + Ok(script) => { + let script_args: Bytes = script.args().unpack(); + let script_args = script_args.to_vec(); + + let cmd: SpawnCasesCmd = script_args[0].into(); + + let argv = ckb_std::env::argv(); + + if argv.is_empty() { + parent_entry(cmd) + } else { + child_entry(cmd) + } + } + Err(e) => { + panic!("load script error: {:?}", e) + } + } +} diff --git a/tests/contracts/spawn-child/.gitignore b/tests/contracts/spawn-child/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/contracts/spawn-child/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/contracts/spawn-child/Cargo.toml b/tests/contracts/spawn-child/Cargo.toml new file mode 100644 index 0000000..32a7d26 --- /dev/null +++ b/tests/contracts/spawn-child/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "spawn-child" +version = "0.1.0" +edition = "2021" + +[dependencies] +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } +spawn_cmd = { path = "../../libs/spawn_cmd" } + +[features] +native-simulator = ["ckb-std-wrapper/native-simulator"] + diff --git a/tests/contracts/spawn-child/Makefile b/tests/contracts/spawn-child/Makefile new file mode 100644 index 0000000..e4ee221 --- /dev/null +++ b/tests/contracts/spawn-child/Makefile @@ -0,0 +1,80 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +OBJCOPY := $(subst clang,llvm-objcopy,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := $(notdir $(shell pwd)) + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + cp $(TOP)/$(BUILD_DIR)/$$binary $(TOP)/$(BUILD_DIR)/$$binary.debug; \ + $(OBJCOPY) --strip-debug --strip-all $(TOP)/$(BUILD_DIR)/$$binary; \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/tests/contracts/spawn-child/README.md b/tests/contracts/spawn-child/README.md new file mode 100644 index 0000000..5298130 --- /dev/null +++ b/tests/contracts/spawn-child/README.md @@ -0,0 +1,7 @@ +# spawn-child + +TODO: Write this readme + +*This contract was bootstrapped with [ckb-script-templates].* + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/contracts/spawn-child/src/lib.rs b/tests/contracts/spawn-child/src/lib.rs new file mode 100644 index 0000000..7b58725 --- /dev/null +++ b/tests/contracts/spawn-child/src/lib.rs @@ -0,0 +1,7 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![allow(special_module_name)] +#![allow(unused_attributes)] +#[cfg(feature = "native-simulator")] +mod main; +#[cfg(feature = "native-simulator")] +pub use main::program_entry; diff --git a/tests/contracts/spawn-child/src/main.rs b/tests/contracts/spawn-child/src/main.rs new file mode 100644 index 0000000..61c4fea --- /dev/null +++ b/tests/contracts/spawn-child/src/main.rs @@ -0,0 +1,230 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![cfg_attr(not(test), no_main)] + +#[cfg(not(any(feature = "native-simulator", test)))] +ckb_std::entry!(program_entry); +#[cfg(not(any(feature = "native-simulator", test)))] +ckb_std::default_alloc!(); + +#[cfg(any(feature = "native-simulator", test))] +extern crate alloc; +use ckb_std_wrapper::ckb_std; + +use alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; +use ckb_std::{debug, syscalls}; +use spawn_cmd::SpawnCmd; + +pub fn program_entry() -> i8 { + debug!("-B- Spawn-Child(pid:{}) Begin --", syscalls::process_id()); + + let argv = ckb_std::env::argv(); + assert!(!argv.is_empty(), "child args is failed: {}", argv.len()); + + let argv: Vec = argv + .iter() + .map(|f| f.to_str().unwrap().to_string()) + .collect(); + + let cmd: SpawnCmd = argv[0].as_str().into(); + let argv = argv[1..].to_vec(); + + let rc = cmd_routing(cmd, &argv); + + debug!("-B- Spawn-Child(pid:{}) End --", syscalls::process_id()); + rc +} + +fn cmd_routing(cmd: SpawnCmd, argv: &[String]) -> i8 { + debug!("-B- cmd: {:?}", cmd); + match cmd { + SpawnCmd::Base => spawn_base(argv), + SpawnCmd::SpawnRetNot0 => spawn_ret_not0(argv), + SpawnCmd::WaitRetNot0 => wait_ret_not0(argv), + SpawnCmd::WaitInvalidPid => panic!("pass"), + SpawnCmd::EmptyPipe => panic!("unsupport EmptyPipe"), + SpawnCmd::SpawnInvalidFd => panic!("unsupport SpawnInvalidFd"), + SpawnCmd::SpawnMaxVms => spawn_max_vms(argv), + SpawnCmd::PipeMaxFds => panic!("pass"), + SpawnCmd::BaseIO1 => spawn_base_io1(argv), + SpawnCmd::BaseIO2 => spawn_base_io2(argv), + SpawnCmd::BaseIO3 => spawn_base_io3(argv), + SpawnCmd::BaseIO4 => spawn_base_io4(argv), + SpawnCmd::IOReadMore => io_read_more(argv), + SpawnCmd::IOWriteMore => io_write_more(argv), + } +} + +fn spawn_base(_argv: &[String]) -> i8 { + let mut std_fds = [0u64; 2]; + syscalls::inherited_fds(&mut std_fds); + assert_eq!(std_fds[0], 4); + assert_eq!(std_fds[1], 3); + + let mut std_fds2 = [0u64; 3]; + syscalls::inherited_fds(&mut std_fds2); + assert_eq!(std_fds2[0], 4); + assert_eq!(std_fds2[1], 3); + assert_eq!(std_fds2[2], 0); + + 0 +} + +fn spawn_ret_not0(_argv: &[String]) -> i8 { + 3 +} + +fn wait_ret_not0(_argv: &[String]) -> i8 { + let mut std_fds: [u64; 1] = [0; 1]; + syscalls::inherited_fds(&mut std_fds); + + let mut buf = [0u8; 32]; + let _ = syscalls::read(std_fds[0], &mut buf); + + 2 +} + +fn spawn_max_vms(_argv: &[String]) -> i8 { + let mut std_fds: [u64; 1] = [0; 1]; + syscalls::inherited_fds(&mut std_fds); + + let mut buf = [0u8; 32]; + syscalls::read(std_fds[0], &mut buf).expect("child write"); + + 0 +} + +fn spawn_base_io1(argv: &[String]) -> i8 { + let mut std_fds: [u64; 2] = [0; 2]; + debug!("-B- InheritedFds --"); + syscalls::inherited_fds(&mut std_fds); + debug!("-B- InheritedFds {} {} End --", std_fds[0], std_fds[1]); + + let mut buffer = vec![]; + for arg in argv { + buffer.extend_from_slice(arg.as_bytes()); + } + + debug!( + "-B- Write, fd: {}, buf_len({}) --", + std_fds[1], + buffer.len() + ); + let len = syscalls::write(std_fds[1], &buffer).expect("child write"); + debug!("-B- buf: {:02x?}", buffer); + debug!("-B- Write End --"); + assert_eq!(len, 10); + + debug!( + "-B- Write2, fd: {}, buf_len({}) --", + std_fds[1], + buffer.len() + ); + let bufff = [0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8]; + let len = syscalls::write(std_fds[1], &bufff).expect("child write"); + debug!("-B- Write2 End --"); + assert_eq!(len, 10); + + 0 +} + +fn spawn_base_io2(argv: &[String]) -> i8 { + let mut std_fds: [u64; 2] = [0; 2]; + debug!("-B- InheritedFds --"); + syscalls::inherited_fds(&mut std_fds); + debug!("-B- InheritedFds {} {} End --", std_fds[0], std_fds[1]); + + let mut out = vec![]; + for arg in argv { + out.extend_from_slice(arg.as_bytes()); + } + + debug!("-B- Read --"); + let mut buf: [u8; 256] = [0; 256]; + let len = syscalls::read(std_fds[0], &mut buf).expect("read 1"); + debug!("-B- Read End --"); + + assert_eq!(len, out.len()); + assert_eq!(out, buf[..out.len()]); + 0 +} + +fn spawn_base_io3(argv: &[String]) -> i8 { + let mut std_fds: [u64; 2] = [0; 2]; + debug!("-B- InheritedFds --"); + syscalls::inherited_fds(&mut std_fds); + debug!("-B- InheritedFds {} {} End --", std_fds[0], std_fds[1]); + + let mut out = vec![]; + for arg in argv { + out.extend_from_slice(arg.as_bytes()); + } + + debug!("-B- Read --"); + let mut buf: [u8; 256] = [0; 256]; + let len = syscalls::read(std_fds[0], &mut buf).expect("read 1"); + debug!("-B- Read End --"); + + assert_eq!(len, out.len()); + assert_eq!(out, buf[..out.len()]); + 0 +} + +fn spawn_base_io4(argv: &[String]) -> i8 { + let mut std_fds: [u64; 2] = [0; 2]; + syscalls::inherited_fds(&mut std_fds); + + let mut out = vec![]; + for arg in argv { + out.extend_from_slice(arg.as_bytes()); + } + + debug!("-B- write: {:02x?}", out); + let len = syscalls::write(std_fds[1], &out).expect("child write"); + assert_eq!(len, 10); + 0 +} + +fn io_read_more(_argv: &[String]) -> i8 { + let fd_w = { + let mut std_fds = [0; 1]; + syscalls::inherited_fds(&mut std_fds); + std_fds[0] + }; + + let mut buffer1 = [0u8; 32]; + let mut count = 0; + buffer1.iter_mut().all(|f| { + *f = count; + count += 1; + true + }); + syscalls::write(fd_w, &buffer1).unwrap(); + + 0 +} + +fn io_write_more(argv: &[String]) -> i8 { + let fd_r = { + let mut std_fds = [0; 1]; + syscalls::inherited_fds(&mut std_fds); + std_fds[0] + }; + + let mut out = vec![]; + for arg in argv { + out.extend_from_slice(arg.as_bytes()); + } + + debug!("-B- Read --"); + let mut buf: [u8; 256] = [0; 256]; + let len = syscalls::read(fd_r, &mut buf).expect("read 1"); + debug!("-B- Read End --"); + + assert_eq!(len, out.len()); + assert_eq!(out, buf[..out.len()]); + 0 +} diff --git a/tests/contracts/spawn-parent/.gitignore b/tests/contracts/spawn-parent/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/contracts/spawn-parent/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/contracts/spawn-parent/Cargo.toml b/tests/contracts/spawn-parent/Cargo.toml new file mode 100644 index 0000000..6a19a03 --- /dev/null +++ b/tests/contracts/spawn-parent/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "spawn-parent" +version = "0.1.0" +edition = "2021" + +[dependencies] +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } +spawn_cmd = { path = "../../libs/spawn_cmd" } + +[features] +native-simulator = ["ckb-std-wrapper/native-simulator"] + diff --git a/tests/contracts/spawn-parent/Makefile b/tests/contracts/spawn-parent/Makefile new file mode 100644 index 0000000..e4ee221 --- /dev/null +++ b/tests/contracts/spawn-parent/Makefile @@ -0,0 +1,80 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +OBJCOPY := $(subst clang,llvm-objcopy,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := $(notdir $(shell pwd)) + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + cp $(TOP)/$(BUILD_DIR)/$$binary $(TOP)/$(BUILD_DIR)/$$binary.debug; \ + $(OBJCOPY) --strip-debug --strip-all $(TOP)/$(BUILD_DIR)/$$binary; \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/tests/contracts/spawn-parent/README.md b/tests/contracts/spawn-parent/README.md new file mode 100644 index 0000000..ac4764e --- /dev/null +++ b/tests/contracts/spawn-parent/README.md @@ -0,0 +1,7 @@ +# spawn-parent + +TODO: Write this readme + +*This contract was bootstrapped with [ckb-script-templates].* + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/contracts/spawn-parent/src/lib.rs b/tests/contracts/spawn-parent/src/lib.rs new file mode 100644 index 0000000..7b58725 --- /dev/null +++ b/tests/contracts/spawn-parent/src/lib.rs @@ -0,0 +1,7 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![allow(special_module_name)] +#![allow(unused_attributes)] +#[cfg(feature = "native-simulator")] +mod main; +#[cfg(feature = "native-simulator")] +pub use main::program_entry; diff --git a/tests/contracts/spawn-parent/src/main.rs b/tests/contracts/spawn-parent/src/main.rs new file mode 100644 index 0000000..173ea93 --- /dev/null +++ b/tests/contracts/spawn-parent/src/main.rs @@ -0,0 +1,384 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +#![cfg_attr(not(test), no_main)] + +#[cfg(not(any(feature = "native-simulator", test)))] +ckb_std::entry!(program_entry); +#[cfg(not(any(feature = "native-simulator", test)))] +ckb_std::default_alloc!(); + +#[cfg(any(feature = "native-simulator", test))] +extern crate alloc; +use ckb_std_wrapper::ckb_std; + +use alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; +use ckb_std::{ + ckb_types::{bytes::Bytes, packed::Byte32, prelude::Unpack}, + debug, + error::SysError, + syscalls, +}; +use core::ffi::CStr; +use spawn_cmd::SpawnCmd; + +pub fn program_entry() -> i8 { + debug!("-A- SpawnParent(pid:{}) Begin --", syscalls::process_id()); + + let rc = SpawnArgs::default().cmd_routing(); + debug!("-A- Spawn-Parent(pid:{}) End --", syscalls::process_id()); + rc +} + +#[derive(Clone)] +struct SpawnArgs { + cmd: SpawnCmd, + code_hash: Byte32, + _args: Vec, +} +impl Default for SpawnArgs { + fn default() -> Self { + let args = { + let script = ckb_std::high_level::load_script().expect("Load script"); + let args: Bytes = script.args().unpack(); + args.to_vec() + }; + + let cmd = args[0].into(); + let code_hash = Byte32::new(args[1..33].to_vec().try_into().unwrap()); + let args = args[33..].to_vec(); + + Self { + cmd, + code_hash, + _args: args, + } + } +} +impl SpawnArgs { + fn cmd_routing(self) -> i8 { + debug!("-A- cmd: {:?}", self.cmd); + match self.cmd { + SpawnCmd::Base => spawn_base(self), + SpawnCmd::SpawnRetNot0 => spawn_ret_not0(self), + SpawnCmd::WaitRetNot0 => wait_ret_not0(self), + SpawnCmd::WaitInvalidPid => wait_invalid_pid(self), + SpawnCmd::EmptyPipe => spawn_empty_pipe(self), + SpawnCmd::SpawnInvalidFd => spawn_invalid_fd(self), + SpawnCmd::SpawnMaxVms => spawn_max_vms(self), + SpawnCmd::PipeMaxFds => pipe_max_fds(self), + SpawnCmd::BaseIO1 => spawn_base_io1(self), + SpawnCmd::BaseIO2 => spawn_base_io2(self), + SpawnCmd::BaseIO3 => spawn_base_io3(self), + SpawnCmd::BaseIO4 => spawn_base_io4(self), + SpawnCmd::IOReadMore => io_read_more(self), + SpawnCmd::IOWriteMore => io_write_more(self), + } + } + + fn new_spawn(self, args: &[String], fds: &[u64]) -> Result { + let cmd: u8 = self.cmd.into(); + let args = [&[cmd.to_string()], args].concat(); + let args: Vec> = args + .iter() + .map(|s| alloc::vec![s.as_bytes(), &[0u8]].concat()) + .collect(); + let argv: Vec<&CStr> = args + .iter() + .map(|s| CStr::from_bytes_until_nul(s).unwrap()) + .collect(); + + ckb_std::high_level::spawn_cell( + &self.code_hash.raw_data(), + ckb_std::ckb_types::core::ScriptHashType::Data2, + &argv, + fds, + ) + } +} + +fn new_pipe() -> ([u64; 2], [u64; 3]) { + let mut std_fds: [u64; 2] = [0, 0]; + let mut son_fds: [u64; 3] = [0, 0, 0]; + let (r0, w0) = syscalls::pipe().unwrap(); + std_fds[0] = r0; + son_fds[1] = w0; + let (r1, w1) = syscalls::pipe().unwrap(); + std_fds[1] = w1; + son_fds[0] = r1; + (std_fds, son_fds) +} + +fn spawn_base(args: SpawnArgs) -> i8 { + let (std_fds, son_fds) = new_pipe(); + let pid = args.new_spawn(&[], &son_fds).expect("run spawn base"); + assert_eq!(pid, 1); + + assert!(syscalls::close(std_fds[0]).is_ok()); + assert!(syscalls::close(std_fds[1]).is_ok()); + + assert_eq!(syscalls::close(son_fds[0]), Err(SysError::InvalidFd)); + assert_eq!(syscalls::close(son_fds[1]), Err(SysError::InvalidFd)); + + assert_eq!(syscalls::process_id(), 0); + + let rr = syscalls::close(pid); + assert_eq!(rr.unwrap_err(), SysError::InvalidFd); + + let code = syscalls::wait(pid).unwrap(); + assert_eq!(code, 0); + 0 +} + +fn spawn_ret_not0(args: SpawnArgs) -> i8 { + let (_std_fds, son_fds) = new_pipe(); + let pid = args.new_spawn(&[], &son_fds).expect("run spawn base"); + assert_eq!(pid, 1); + let code = syscalls::wait(pid).unwrap(); + assert_eq!(code, 3); + + 0 +} + +fn wait_ret_not0(args: SpawnArgs) -> i8 { + let (r0, _w0) = syscalls::pipe().unwrap(); + let pid = args.new_spawn(&[], &[r0, 0]).expect("run spawn base r"); + assert_eq!(pid, 1); + + let rc_code = syscalls::wait(pid).unwrap(); + + assert_eq!(rc_code, 2); + debug!("-A- rc code: {}", rc_code); + 0 +} + +fn wait_invalid_pid(args: SpawnArgs) -> i8 { + let mut args = args; + args.cmd = SpawnCmd::WaitRetNot0; + let (r0, w0) = syscalls::pipe().unwrap(); + let pid = args.new_spawn(&[], &[r0, 0]).unwrap(); + assert_eq!(pid, 1); + + let err = syscalls::wait(pid + 2).unwrap_err(); + assert_eq!(err, SysError::WaitFailure); + + debug!("-A- Unlock B"); + syscalls::write(w0, &[2u8; 8]).unwrap(); + + debug!("-A- Wait B"); + let rc_code = syscalls::wait(pid).unwrap(); + assert_eq!(rc_code, 2); + + 0 +} + +fn spawn_empty_pipe(_args: SpawnArgs) -> i8 { + let (std_fds, son_fds) = new_pipe(); + + assert_eq!(std_fds[0], 2); + assert_eq!(son_fds[1], 3); + assert_eq!(son_fds[0], 4); + assert_eq!(std_fds[1], 5); + + assert!(syscalls::close(std_fds[0]).is_ok()); + assert_eq!(syscalls::close(std_fds[0]), Err(SysError::InvalidFd)); + assert!(syscalls::close(std_fds[1]).is_ok()); + assert!(syscalls::close(son_fds[0]).is_ok()); + assert!(syscalls::close(son_fds[1]).is_ok()); + 0 +} + +fn spawn_invalid_fd(args: SpawnArgs) -> i8 { + let (_std_fds, son_fds) = new_pipe(); + let mut son_fds2 = son_fds; + son_fds2[0] += 20; + let err = args.new_spawn(&[], &son_fds2).unwrap_err(); + assert_eq!(err, ckb_std::error::SysError::InvalidFd); + 0 +} + +fn spawn_max_vms(args: SpawnArgs) -> i8 { + for _ in 0..16 { + let (r0, _w0) = syscalls::pipe().unwrap(); + let _pid = args.clone().new_spawn(&[], &[r0, 0]).unwrap(); + } + + let (r0, _w0) = syscalls::pipe().unwrap(); + let err = args.clone().new_spawn(&[], &[r0, 0]).unwrap_err(); + assert_eq!(err, SysError::MaxVmsSpawned); + + 0 +} + +fn pipe_max_fds(args: SpawnArgs) -> i8 { + // lock B + let mut args = args; + args.cmd = SpawnCmd::WaitRetNot0; + let (r0, w0) = syscalls::pipe().unwrap(); + let pid = args.clone().new_spawn(&[], &[r0, 0]).unwrap(); + + let mut fds = Vec::with_capacity(32); + for _ in 0..31 { + fds.push(syscalls::pipe().unwrap()); + } + + let err = syscalls::pipe().unwrap_err(); + assert_eq!(err, SysError::MaxFdsCreated); + + let (fd1, fd2) = fds.pop().unwrap(); + syscalls::close(fd1).unwrap(); + syscalls::close(fd2).unwrap(); + + fds.push(syscalls::pipe().unwrap()); + + while let Some((fd1, fd2)) = fds.pop() { + syscalls::close(fd1).unwrap(); + syscalls::close(fd2).unwrap(); + } + + syscalls::write(w0, &[2u8; 8]).unwrap(); + let code = syscalls::wait(pid).unwrap(); + assert_eq!(code, 2); + 0 +} + +fn spawn_base_io1(args: SpawnArgs) -> i8 { + let (std_fds, son_fds) = new_pipe(); + + let argv = ["hello".to_string(), "world".to_string()]; + debug!("-A- Spawn --"); + let pid = args.new_spawn(&argv, &son_fds).expect("run spawn base io"); + debug!("-A- Spawn End, pid: {} --", pid); + assert_eq!(pid, 1); + + let mut buf = [0; 10]; + let err = syscalls::read(std_fds[0] + 20, &mut buf).unwrap_err(); + assert_eq!(err, SysError::InvalidFd); + + debug!("-A- Read --"); + let mut buf = [0; 10]; + let len = syscalls::read(std_fds[0], &mut buf).expect("read 1"); + debug!("-A- Read {} End --", len); + + assert_eq!(len, 10); + let buf = [buf.as_slice(), &[0]].concat(); + assert_eq!( + CStr::from_bytes_until_nul(&buf).unwrap().to_str().unwrap(), + "helloworld" + ); + 0 +} + +fn spawn_base_io2(args: SpawnArgs) -> i8 { + let (std_fds, son_fds) = new_pipe(); + + let argv = ["hello".to_string(), "world".to_string()]; + let pid = args.new_spawn(&argv, &son_fds).expect("run spawn base io"); + assert_eq!(pid, 1); + + debug!("-A- Write --"); + let write_buf = alloc::vec![argv[0].as_bytes(), argv[1].as_bytes()].concat(); + let len = syscalls::write(std_fds[1], &write_buf).expect("write"); + debug!("-A- Write End --"); + assert_eq!(len, write_buf.len()); + + 0 +} + +fn spawn_base_io3(args: SpawnArgs) -> i8 { + let (std_fds, son_fds) = new_pipe(); + + let argv = ["hello".to_string(), "world".to_string()]; + let pid = args.new_spawn(&argv, &son_fds).expect("run spawn base io"); + assert_eq!(pid, 1); + + let write_buf = alloc::vec![argv[0].as_bytes(), argv[1].as_bytes()].concat(); + let len = syscalls::write(std_fds[1], &write_buf).expect("write"); + assert_eq!(len, write_buf.len()); + + 0 +} + +fn spawn_base_io4(args: SpawnArgs) -> i8 { + let (std_fds, son_fds) = new_pipe(); + + let argv = ["hello".to_string(), "world".to_string()]; + let _pid = args.new_spawn(&argv, &son_fds).expect("run spawn base io"); + + let mut buf1 = [0u8; 5]; + syscalls::read(std_fds[0], &mut buf1).unwrap(); + debug!("-A- buf1: {:02x?}", buf1); + assert_eq!( + CStr::from_bytes_until_nul(&[buf1.to_vec(), vec![0]].concat()) + .unwrap() + .to_str() + .unwrap(), + "hello" + ); + + let mut buf2 = [0u8; 5]; + syscalls::read(std_fds[0], &mut buf2).unwrap(); + debug!("-A- buf2: {:02x?}", buf2); + assert_eq!( + CStr::from_bytes_until_nul(&[buf2.to_vec(), vec![0]].concat()) + .unwrap() + .to_str() + .unwrap(), + "world" + ); + + 0 +} + +fn io_read_more(args: SpawnArgs) -> i8 { + let (fd_r, fd_w) = syscalls::pipe().unwrap(); + + let pid = args.new_spawn(&[], &[fd_w, 0]).expect("run spawn base io"); + + let mut buffer = [0u8; 128]; + + debug!("-A- Read Begin"); + let readed_len = syscalls::read(fd_r, &mut buffer).unwrap(); + debug!("-A- Readed len: {}", readed_len); + + for (count, it) in buffer.iter().take(readed_len).enumerate() { + assert_eq!(it, &(count as u8)); + } + + let code = syscalls::wait(pid).unwrap(); + assert_eq!(code, 0); + + let err = syscalls::read(fd_r, &mut buffer).unwrap_err(); + assert_eq!(err, SysError::OtherEndClosed); + + 0 +} + +fn io_write_more(args: SpawnArgs) -> i8 { + let (fd_r, fd_w) = syscalls::pipe().unwrap(); + + let argv = ["hello".to_string(), "world".to_string()]; + let pid = args + .new_spawn(&argv, &[fd_r, 0]) + .expect("run spawn base io"); + assert_eq!(pid, 1); + + let err = syscalls::write(fd_w + 20, &[0; 8]).unwrap_err(); + assert_eq!(err, SysError::InvalidFd); + + debug!("-A- Write --"); + let write_buf = alloc::vec![argv[0].as_bytes(), argv[1].as_bytes()].concat(); + let len = syscalls::write(fd_w, &write_buf).expect("write"); + debug!("-A- Write End --"); + assert_eq!(len, write_buf.len()); + + let code = syscalls::wait(pid).unwrap(); + assert_eq!(code, 0); + + let err = syscalls::write(fd_w, &[0; 8]).unwrap_err(); + assert_eq!(err, SysError::OtherEndClosed); + + 0 +} diff --git a/tests/libs/ckb-std-wrapper/Cargo.toml b/tests/libs/ckb-std-wrapper/Cargo.toml new file mode 100644 index 0000000..93a516c --- /dev/null +++ b/tests/libs/ckb-std-wrapper/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ckb-std-wrapper" +version = "0.1.0" +edition = "2021" + +[features] +native-simulator = ["ckb-std/native-simulator"] + +[dependencies] +ckb-std = { git = "https://github.com/joii2020/ckb-std.git", rev = "a4dfebc" } +# ckb-std = { path = "../../../../ckb-std" } diff --git a/tests/libs/ckb-std-wrapper/src/lib.rs b/tests/libs/ckb-std-wrapper/src/lib.rs new file mode 100644 index 0000000..2430514 --- /dev/null +++ b/tests/libs/ckb-std-wrapper/src/lib.rs @@ -0,0 +1,2 @@ +#![cfg_attr(not(feature = "native-simulator"), no_std)] +pub use ckb_std; \ No newline at end of file diff --git a/tests/libs/spawn_cmd/Cargo.toml b/tests/libs/spawn_cmd/Cargo.toml new file mode 100644 index 0000000..841a89e --- /dev/null +++ b/tests/libs/spawn_cmd/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "spawn_cmd" +version = "0.1.0" +edition = "2021" + +[dependencies] +num-traits = {version = "0.2.19", default-features = false} +num-derive = "0.4.2" \ No newline at end of file diff --git a/tests/libs/spawn_cmd/src/lib.rs b/tests/libs/spawn_cmd/src/lib.rs new file mode 100644 index 0000000..1f3e447 --- /dev/null +++ b/tests/libs/spawn_cmd/src/lib.rs @@ -0,0 +1,76 @@ +#![no_std] + +#[macro_use] +extern crate num_derive; + +use num_traits::{FromPrimitive, ToPrimitive}; + +#[repr(u8)] +#[derive(FromPrimitive, ToPrimitive, Clone, Debug)] +pub enum SpawnCmd { + Base = 1, + SpawnRetNot0, + WaitRetNot0, + WaitInvalidPid, + EmptyPipe, + SpawnInvalidFd, + SpawnMaxVms, + PipeMaxFds, + BaseIO1, + BaseIO2, + BaseIO3, + BaseIO4, + IOReadMore, + IOWriteMore, +} + +impl From for SpawnCmd { + fn from(value: u8) -> Self { + Self::from_u8(value).unwrap() + } +} +impl From<&str> for SpawnCmd { + fn from(value: &str) -> Self { + value.parse::().unwrap().into() + } +} +impl From for u8 { + fn from(value: SpawnCmd) -> Self { + value.to_u8().unwrap() + } +} + +#[repr(u8)] +#[derive(FromPrimitive, ToPrimitive, Clone, Debug)] +pub enum SpawnCasesCmd { + Unknow = 0, + ReadWrite, + WriteDeadLock, + InvalidFd, + WaitDeadLock, + ReadWriteWithClose, + WaitMultiple, + InheritedFds, + InheritedFdsWithoutOwner, + ReadThenClose, + MaxVmsCount, + MaxFdsLimit, + CloseInvalidFd, + WriteClosedFd, + CheckPID, +} +impl From for SpawnCasesCmd { + fn from(value: u8) -> Self { + Self::from_u8(value).unwrap() + } +} +impl From<&str> for SpawnCasesCmd { + fn from(value: &str) -> Self { + value.parse::().unwrap().into() + } +} +impl From for u8 { + fn from(value: SpawnCasesCmd) -> Self { + value.to_u8().unwrap() + } +} diff --git a/tests/native-simulators/exec-child-sim/.gitignore b/tests/native-simulators/exec-child-sim/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/native-simulators/exec-child-sim/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/native-simulators/exec-child-sim/Cargo.toml b/tests/native-simulators/exec-child-sim/Cargo.toml new file mode 100644 index 0000000..ba5479e --- /dev/null +++ b/tests/native-simulators/exec-child-sim/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "exec-child-sim" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +exec-child = { path = "../../contracts/exec-child", features = ["native-simulator"] } +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } + +[lib] +crate-type = ["cdylib"] diff --git a/tests/native-simulators/exec-child-sim/README.md b/tests/native-simulators/exec-child-sim/README.md new file mode 100644 index 0000000..0ad88d1 --- /dev/null +++ b/tests/native-simulators/exec-child-sim/README.md @@ -0,0 +1,10 @@ +# exec-child-sim + +TODO: Write this readme + +*This template is used to provide native simulator for a particular contract, and is not designed to be used on its own.* + +*This project was bootstrapped with [ckb-script-templates].* + + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/native-simulators/exec-child-sim/src/lib.rs b/tests/native-simulators/exec-child-sim/src/lib.rs new file mode 100644 index 0000000..2d0fd11 --- /dev/null +++ b/tests/native-simulators/exec-child-sim/src/lib.rs @@ -0,0 +1,2 @@ +use ckb_std_wrapper::ckb_std; +ckb_std::entry_simulator!(exec_child::program_entry); diff --git a/tests/native-simulators/exec-parent-sim/.gitignore b/tests/native-simulators/exec-parent-sim/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/native-simulators/exec-parent-sim/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/native-simulators/exec-parent-sim/Cargo.toml b/tests/native-simulators/exec-parent-sim/Cargo.toml new file mode 100644 index 0000000..70e1a1d --- /dev/null +++ b/tests/native-simulators/exec-parent-sim/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "exec-parent-sim" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +exec-parent = { path = "../../contracts/exec-parent", features = ["native-simulator"] } +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } + +[lib] +crate-type = ["cdylib"] diff --git a/tests/native-simulators/exec-parent-sim/README.md b/tests/native-simulators/exec-parent-sim/README.md new file mode 100644 index 0000000..170d4c9 --- /dev/null +++ b/tests/native-simulators/exec-parent-sim/README.md @@ -0,0 +1,10 @@ +# exec-parent-sim + +TODO: Write this readme + +*This template is used to provide native simulator for a particular contract, and is not designed to be used on its own.* + +*This project was bootstrapped with [ckb-script-templates].* + + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/native-simulators/exec-parent-sim/src/lib.rs b/tests/native-simulators/exec-parent-sim/src/lib.rs new file mode 100644 index 0000000..ff22906 --- /dev/null +++ b/tests/native-simulators/exec-parent-sim/src/lib.rs @@ -0,0 +1,2 @@ +use ckb_std_wrapper::ckb_std; +ckb_std::entry_simulator!(exec_parent::program_entry); diff --git a/tests/native-simulators/spawn-cases-sim/.gitignore b/tests/native-simulators/spawn-cases-sim/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/native-simulators/spawn-cases-sim/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/native-simulators/spawn-cases-sim/Cargo.toml b/tests/native-simulators/spawn-cases-sim/Cargo.toml new file mode 100644 index 0000000..db6869b --- /dev/null +++ b/tests/native-simulators/spawn-cases-sim/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "spawn-cases-sim" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +spawn-cases = { path = "../../contracts/spawn-cases", features = ["native-simulator"] } +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } + +[lib] +crate-type = ["cdylib"] diff --git a/tests/native-simulators/spawn-cases-sim/README.md b/tests/native-simulators/spawn-cases-sim/README.md new file mode 100644 index 0000000..0031f8a --- /dev/null +++ b/tests/native-simulators/spawn-cases-sim/README.md @@ -0,0 +1,10 @@ +# spawn-parent-sim + +TODO: Write this readme + +*This template is used to provide native simulator for a particular contract, and is not designed to be used on its own.* + +*This project was bootstrapped with [ckb-script-templates].* + + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/native-simulators/spawn-cases-sim/src/lib.rs b/tests/native-simulators/spawn-cases-sim/src/lib.rs new file mode 100644 index 0000000..307eab3 --- /dev/null +++ b/tests/native-simulators/spawn-cases-sim/src/lib.rs @@ -0,0 +1,2 @@ +use ckb_std_wrapper::ckb_std; +ckb_std::entry_simulator!(spawn_cases::program_entry); diff --git a/tests/native-simulators/spawn-child-sim/.gitignore b/tests/native-simulators/spawn-child-sim/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/native-simulators/spawn-child-sim/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/native-simulators/spawn-child-sim/Cargo.toml b/tests/native-simulators/spawn-child-sim/Cargo.toml new file mode 100644 index 0000000..a02f271 --- /dev/null +++ b/tests/native-simulators/spawn-child-sim/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "spawn-child-sim" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +spawn-child = { path = "../../contracts/spawn-child", features = ["native-simulator"] } +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } + +[lib] +crate-type = ["cdylib"] diff --git a/tests/native-simulators/spawn-child-sim/README.md b/tests/native-simulators/spawn-child-sim/README.md new file mode 100644 index 0000000..fcfa032 --- /dev/null +++ b/tests/native-simulators/spawn-child-sim/README.md @@ -0,0 +1,10 @@ +# spawn-child-sim + +TODO: Write this readme + +*This template is used to provide native simulator for a particular contract, and is not designed to be used on its own.* + +*This project was bootstrapped with [ckb-script-templates].* + + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/native-simulators/spawn-child-sim/src/lib.rs b/tests/native-simulators/spawn-child-sim/src/lib.rs new file mode 100644 index 0000000..0c4ad58 --- /dev/null +++ b/tests/native-simulators/spawn-child-sim/src/lib.rs @@ -0,0 +1,2 @@ +use ckb_std_wrapper::ckb_std; +ckb_std::entry_simulator!(spawn_child::program_entry); diff --git a/tests/native-simulators/spawn-parent-sim/.gitignore b/tests/native-simulators/spawn-parent-sim/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/tests/native-simulators/spawn-parent-sim/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/tests/native-simulators/spawn-parent-sim/Cargo.toml b/tests/native-simulators/spawn-parent-sim/Cargo.toml new file mode 100644 index 0000000..62b841a --- /dev/null +++ b/tests/native-simulators/spawn-parent-sim/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "spawn-parent-sim" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +spawn-parent = { path = "../../contracts/spawn-parent", features = ["native-simulator"] } +ckb-std-wrapper = { path = "../../libs/ckb-std-wrapper" } + +[lib] +crate-type = ["cdylib"] diff --git a/tests/native-simulators/spawn-parent-sim/README.md b/tests/native-simulators/spawn-parent-sim/README.md new file mode 100644 index 0000000..0031f8a --- /dev/null +++ b/tests/native-simulators/spawn-parent-sim/README.md @@ -0,0 +1,10 @@ +# spawn-parent-sim + +TODO: Write this readme + +*This template is used to provide native simulator for a particular contract, and is not designed to be used on its own.* + +*This project was bootstrapped with [ckb-script-templates].* + + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/tests/native-simulators/spawn-parent-sim/src/lib.rs b/tests/native-simulators/spawn-parent-sim/src/lib.rs new file mode 100644 index 0000000..af4b4dc --- /dev/null +++ b/tests/native-simulators/spawn-parent-sim/src/lib.rs @@ -0,0 +1,2 @@ +use ckb_std_wrapper::ckb_std; +ckb_std::entry_simulator!(spawn_parent::program_entry); diff --git a/tests/scripts/find_clang b/tests/scripts/find_clang new file mode 100755 index 0000000..f1ffeed --- /dev/null +++ b/tests/scripts/find_clang @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# +# An utility script used to find a binary of clang 16+ + +if [[ -n "${CLANG}" ]]; then + echo "${CLANG}" + exit 0 +fi + +CANDIDATES=("clang" "clang-16" "clang-17" "clang-18") + +BREW_PREFIX=$(brew --prefix 2> /dev/null) +if [[ -n "${BREW_PREFIX}" ]]; then + CANDIDATES+=( + "${BREW_PREFIX}/opt/llvm/bin/clang" + "${BREW_PREFIX}/opt/llvm@16/bin/clang" + "${BREW_PREFIX}/opt/llvm@17/bin/clang" + "${BREW_PREFIX}/opt/llvm@18/bin/clang" + ) +fi + +for candidate in ${CANDIDATES[@]}; do + OUTPUT=$($candidate -dumpversion 2> /dev/null | cut -d'.' -f 1) + + if [[ $((OUTPUT)) -ge 16 ]]; then + echo "$candidate" + exit 0 + fi +done + +>&2 echo "Cannot find clang of version 16+!" +exit 1 diff --git a/tests/scripts/reproducible_build_docker b/tests/scripts/reproducible_build_docker new file mode 100755 index 0000000..fdf35ad --- /dev/null +++ b/tests/scripts/reproducible_build_docker @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# +# An utility script helping with reproducible script builds via docker. +# Note that this utility serves only as one example, docker is not +# necessarily THE way to do reproducible build, nor is it the best way +# to do reproducible build. +set -ex + +DOCKER="${DOCKER:-docker}" +# docker pull docker.io/cryptape/llvm-n-rust:20240630 +DOCKER_IMAGE="${DOCKER_IMAGE:-docker.io/cryptape/llvm-n-rust@sha256:bafaf76d4f342a69b8691c08e77a330b7740631f3d1d9c9bee4ead521b29ee55}" +CHECKSUM_FILE_PATH="${CHECKSUM_FILE_PATH:-checksums.txt}" + +# We are parsing command line arguments based on tips from: +# https://stackoverflow.com/a/14203146 + +while [[ $# -gt 0 ]]; do + case $1 in + -p|--proxy) + PROXY="$2" + shift # past argument + shift # past value + ;; + -u|--update) + UPDATE="yes" + shift # past argument + ;; + --no-clean) + NOCLEAN="yes" + shift # past argument + ;; + -*|--*) + echo "Unknown option $1" + exit 1 + ;; + *) + echo "Unknown argument $1" + exit 1 + ;; + esac +done + +if [[ -n "${PROXY}" ]]; then + DOCKER_RUN_ARGS="-e ALL_PROXY=${PROXY} -e HTTPS_PROXY=${PROXY} -e HTTP_PROXY=${PROXY} ${DOCKER_RUN_ARGS}" +fi + +TASKS="" +if [[ "${NOCLEAN}" != "yes" ]]; then + TASKS+=" clean " +fi + +if [[ "${UPDATE}" = "yes" ]]; then + TASKS+=" checksum CHECKSUM_FILE=${CHECKSUM_FILE_PATH} " +else + TASKS+=" build " +fi + +$DOCKER run --rm $DOCKER_RUN_ARGS -v `pwd`:/code $DOCKER_IMAGE make $TASKS +# Reset file ownerships for all files docker might touch +$DOCKER run --rm $DOCKER_RUN_ARGS -e UID=`id -u` -e GID=`id -g` -v `pwd`:/code $DOCKER_IMAGE bash -c 'chown -R -f $UID:$GID checksums.txt build target' + +if [[ "${UPDATE}" = "yes" ]]; then + echo "${CHECKSUM_FILE_PATH} file is updated with latest binary hashes!" +else + shasum -a 256 -c ${CHECKSUM_FILE_PATH} +fi diff --git a/tests/tests/Cargo.toml b/tests/tests/Cargo.toml new file mode 100644 index 0000000..4e7a04f --- /dev/null +++ b/tests/tests/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "tests" +version = "0.1.0" +edition = "2021" + +[features] +default = ["native-simulator"] +native-simulator = [ "ckb-testtool/native-simulator" ] + +[dependencies] +ckb-testtool = "0.13.1" +serde_json = "1.0" +spawn_cmd = { path = "../libs/spawn_cmd" } diff --git a/tests/tests/src/lib.rs b/tests/tests/src/lib.rs new file mode 100644 index 0000000..e884b11 --- /dev/null +++ b/tests/tests/src/lib.rs @@ -0,0 +1,115 @@ +use ckb_testtool::{ + ckb_error::Error, + ckb_types::{ + bytes::Bytes, + core::{Cycle, TransactionView}, + }, + context::Context, +}; +use std::env; +use std::fs; +use std::path::PathBuf; +use std::str::FromStr; + +#[cfg(test)] +mod tests; + +#[cfg(test)] +mod tests_spawn_cases; + +// The exact same Loader code from capsule's template, except that +// now we use MODE as the environment variable +const TEST_ENV_VAR: &str = "MODE"; + +pub enum TestEnv { + Debug, + Release, +} + +impl FromStr for TestEnv { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "debug" => Ok(TestEnv::Debug), + "release" => Ok(TestEnv::Release), + _ => Err("no match"), + } + } +} + +pub struct Loader(PathBuf); + +impl Default for Loader { + fn default() -> Self { + let test_env = match env::var(TEST_ENV_VAR) { + Ok(val) => val.parse().expect("test env"), + Err(_) => TestEnv::Release, + }; + Self::with_test_env(test_env) + } +} + +impl Loader { + fn with_test_env(env: TestEnv) -> Self { + let load_prefix = match env { + TestEnv::Debug => "debug", + TestEnv::Release => "release", + }; + let mut base_path = match env::var("TOP") { + Ok(val) => { + let mut base_path: PathBuf = val.into(); + base_path.push("build"); + base_path + } + Err(_) => { + let mut base_path = PathBuf::new(); + // cargo may use a different cwd when running tests, for example: + // when running debug in vscode, it will use workspace root as cwd by default, + // when running test by `cargo test`, it will use tests directory as cwd, + // so we need a fallback path + base_path.push("build"); + if !base_path.exists() { + base_path.pop(); + base_path.push(".."); + base_path.push("build"); + } + base_path + } + }; + + base_path.push(load_prefix); + Loader(base_path) + } + + pub fn load_binary(&self, name: &str) -> Bytes { + let mut path = self.0.clone(); + path.push(name); + let result = fs::read(&path); + if result.is_err() { + panic!("Binary {:?} is missing!", path); + } + result.unwrap().into() + } +} + +// This helper method runs Context::verify_tx, but in case error happens, +// it also dumps current transaction to failed_txs folder. +pub fn verify_and_dump_failed_tx( + context: &Context, + tx: &TransactionView, + max_cycles: u64, +) -> Result { + let result = context.verify_tx(tx, max_cycles); + if result.is_err() { + let mut path = env::current_dir().expect("current dir"); + path.push("failed_txs"); + std::fs::create_dir_all(&path).expect("create failed_txs dir"); + let mock_tx = context.dump_tx(tx).expect("dump failed tx"); + let json = serde_json::to_string_pretty(&mock_tx).expect("json"); + path.push(format!("0x{:x}.json", tx.hash())); + println!("Failed tx written to {:?}", path); + std::fs::write(path, json).expect("write"); + } + result +} diff --git a/tests/tests/src/tests.rs b/tests/tests/src/tests.rs new file mode 100644 index 0000000..600672a --- /dev/null +++ b/tests/tests/src/tests.rs @@ -0,0 +1,248 @@ +// Include your tests here +// See https://github.com/xxuejie/ckb-native-build-sample/blob/main/tests/src/tests.rs for examples + +use ckb_testtool::{ + ckb_error::Error as CKBError, + ckb_types::{ + bytes::Bytes, + core::{Cycle, DepType, ScriptHashType, TransactionBuilder}, + packed::{CellDep, CellInput, CellOutput}, + prelude::*, + }, + context::Context, +}; +use spawn_cmd::SpawnCmd; + +pub const MAX_CYCLES: u64 = 500_000_000_000; + +#[test] +fn test_exec() { + let mut context = Context::default(); + context.add_contract_dir("../target/debug/"); + context.add_contract_dir("target/debug/"); + + let out_point_exec_parent = context.deploy_cell_by_name("exec-parent"); + let out_point_exec_child = context.deploy_cell_by_name("exec-child"); + + let exec_child_code_hash = context + .cells + .get(&out_point_exec_child) + .map(|(_, bin)| CellOutput::calc_data_hash(bin).as_bytes().to_vec()) + .unwrap(); + println!("=== exec child code hash: {:02x?}", &exec_child_code_hash); + + let lock_script = context + .build_script_with_hash_type( + &out_point_exec_parent, + ScriptHashType::Data2, + Default::default(), + ) + .expect("script") + .as_builder() + .args( + vec![exec_child_code_hash, vec![ScriptHashType::Data2.into()]] + .concat() + .pack(), + ) + .build(); + let input: CellInput = CellInput::new_builder() + .previous_output( + context.create_cell( + CellOutput::new_builder() + .capacity(1000u64.pack()) + .lock(lock_script.clone()) + .build(), + Bytes::new(), + ), + ) + .build(); + + let outputs = vec![ + CellOutput::new_builder() + .capacity(500u64.pack()) + .lock(lock_script.clone()) + .build(), + CellOutput::new_builder() + .capacity(500u64.pack()) + .lock(lock_script) + .build(), + ]; + + let outputs_data = vec![Bytes::new(); 2]; + + // build transaction + let tx = TransactionBuilder::default() + // .set_inputs(vec![input, input3, input2]) + .set_inputs(vec![input]) + .outputs(outputs) + .outputs_data(outputs_data.pack()) + .cell_dep( + CellDep::new_builder() + .out_point(out_point_exec_child) + .dep_type(DepType::Code.into()) + .build(), + ) + .build(); + + let tx = context.complete_tx(tx); + + // run + context + .verify_tx(&tx, MAX_CYCLES) + .expect("pass verification"); +} + +fn run_spawn(cmd: SpawnCmd, args: &[u8]) -> Result { + let mut context = Context::default(); + context.add_contract_dir("../target/debug/"); + context.add_contract_dir("target/debug/"); + + let out_point_parent = context.deploy_cell_by_name("spawn-parent"); + let out_point_child = context.deploy_cell_by_name("spawn-child"); + + // let exec_child_code_hash = context + // .cells + // .get(&out_point_child) + // .map(|(_, bin)| CellOutput::calc_data_hash(bin).as_bytes().to_vec()) + // .unwrap(); + // println!("=== spawn child code hash: {:02x?}", exec_child_code_hash); + + let args = { + let child_code_hash = context + .cells + .get(&out_point_child) + .map(|(_, bin)| CellOutput::calc_data_hash(bin).as_bytes().to_vec()) + .unwrap(); + + vec![vec![cmd.into()], child_code_hash, args.to_vec()].concat() + }; + + let lock_script = context + .build_script_with_hash_type(&out_point_parent, ScriptHashType::Data2, Default::default()) + .expect("script") + .as_builder() + .args(args.pack()) + .build(); + let input: CellInput = CellInput::new_builder() + .previous_output( + context.create_cell( + CellOutput::new_builder() + .capacity(1000u64.pack()) + .lock(lock_script.clone()) + .build(), + Bytes::new(), + ), + ) + .build(); + + let outputs = vec![ + CellOutput::new_builder() + .capacity(500u64.pack()) + .lock(lock_script.clone()) + .build(), + CellOutput::new_builder() + .capacity(500u64.pack()) + .lock(lock_script) + .build(), + ]; + + let outputs_data = vec![Bytes::new(); 2]; + + // build transaction + let tx = TransactionBuilder::default() + // .set_inputs(vec![input, input3, input2]) + .set_inputs(vec![input]) + .outputs(outputs) + .outputs_data(outputs_data.pack()) + .cell_dep( + CellDep::new_builder() + .out_point(out_point_child) + .dep_type(DepType::Code.into()) + .build(), + ) + .build(); + + let tx = context.complete_tx(tx); + + // run + context.verify_tx(&tx, MAX_CYCLES) +} + +fn run_spawn_success(cmd: SpawnCmd, args: &[u8]) { + run_spawn(cmd, args).expect("pass verification"); +} + +fn _run_spawn_failed(cmd: SpawnCmd, args: &[u8]) { + let err = run_spawn(cmd, args).unwrap_err(); + println!("Spawn error: {:?}", err); +} + +#[test] +fn test_spawn_base() { + run_spawn_success(SpawnCmd::Base, &[]); +} + +#[test] +fn test_spawn_ret_not0() { + run_spawn_success(SpawnCmd::SpawnRetNot0, &[]); +} + +// #[test] +// fn test_wait_ret_not0() { +// run_spawn_failed(SpawnCmd::WaitRetNot0, &[]); +// } + +#[test] +fn test_wait_invalid_pid() { + run_spawn_success(SpawnCmd::WaitInvalidPid, &[]); +} + +#[test] +fn test_spawn_empty_pipe() { + run_spawn_success(SpawnCmd::EmptyPipe, &[]); +} + +#[test] +fn test_spawn_invalid_fd() { + run_spawn_success(SpawnCmd::SpawnInvalidFd, &[]); +} + +#[test] +fn test_spawn_max_vms() { + run_spawn_success(SpawnCmd::SpawnMaxVms, &[]); +} + +#[test] +fn test_pipe_max_fds() { + run_spawn_success(SpawnCmd::PipeMaxFds, &[]); +} + +#[test] +fn test_spawn_io1() { + run_spawn_success(SpawnCmd::BaseIO1, &[]); +} + +#[test] +fn test_spawn_io2() { + run_spawn_success(SpawnCmd::BaseIO2, &[]); +} + +#[test] +fn test_spawn_io3() { + run_spawn_success(SpawnCmd::BaseIO3, &[]); +} + +#[test] +fn test_spawn_io4() { + run_spawn_success(SpawnCmd::BaseIO4, &[]); +} + +#[test] +fn test_io_read_more() { + run_spawn_success(SpawnCmd::IOReadMore, &[]); +} + +#[test] +fn test_io_write_more() { + run_spawn_success(SpawnCmd::IOWriteMore, &[]); +} diff --git a/tests/tests/src/tests_spawn_cases.rs b/tests/tests/src/tests_spawn_cases.rs new file mode 100644 index 0000000..1151f22 --- /dev/null +++ b/tests/tests/src/tests_spawn_cases.rs @@ -0,0 +1,154 @@ +use crate::tests::MAX_CYCLES; +use ckb_testtool::{ + ckb_error::Error as CKBError, + ckb_types::{ + bytes::Bytes, + core::{Cycle, ScriptHashType, TransactionBuilder}, + packed::{CellInput, CellOutput}, + prelude::*, + }, + context::Context, +}; +use spawn_cmd::SpawnCasesCmd; + +fn run_spawn_cases(cmd: SpawnCasesCmd, args: &[u8]) -> Result { + let mut context = Context::default(); + context.add_contract_dir("../target/debug/"); + context.add_contract_dir("target/debug/"); + + let out_point_parent = context.deploy_cell_by_name("spawn-cases"); + + let args = { + vec![vec![cmd.into()], args.to_vec()].concat() + }; + + let lock_script = context + .build_script_with_hash_type(&out_point_parent, ScriptHashType::Data2, Default::default()) + .expect("script") + .as_builder() + .args(args.pack()) + .build(); + let input: CellInput = CellInput::new_builder() + .previous_output( + context.create_cell( + CellOutput::new_builder() + .capacity(1000u64.pack()) + .lock(lock_script.clone()) + .build(), + Bytes::new(), + ), + ) + .build(); + + let outputs = vec![ + CellOutput::new_builder() + .capacity(500u64.pack()) + .lock(lock_script.clone()) + .build(), + CellOutput::new_builder() + .capacity(500u64.pack()) + .lock(lock_script) + .build(), + ]; + + let outputs_data = vec![Bytes::new(); 2]; + + // build transaction + let tx = TransactionBuilder::default() + // .set_inputs(vec![input, input3, input2]) + .set_inputs(vec![input]) + .outputs(outputs) + .outputs_data(outputs_data.pack()) + .build(); + + let tx = context.complete_tx(tx); + + // run + context.verify_tx(&tx, MAX_CYCLES) +} + +#[test] +fn check_spawn_simple_read_write() { + let result = run_spawn_cases(SpawnCasesCmd::ReadWrite, &[]); + let _ = result.expect("pass"); +} + +// There is no automated testing here, the simulator will throw an exception here. +// #[test] +// fn check_spawn_write_dead_lock() { +// let result = run_spawn_cases(SpawnCasesCmd::WriteDeadLock, &[]); +// assert!(result.unwrap_err().to_string().contains("deadlock")); +// } + +#[test] +fn check_spawn_invalid_fd() { + let result = run_spawn_cases(SpawnCasesCmd::InvalidFd, &[]); + result.expect("pass"); +} + +// +// #[test] +// fn check_spawn_wait_dead_lock() { +// let result = run_spawn_cases(SpawnCasesCmd::WaitDeadLock, &[]); +// assert!(result.unwrap_err().to_string().contains("deadlock")); +// } + +#[test] +fn check_spawn_read_write_with_close() { + let result = run_spawn_cases(SpawnCasesCmd::ReadWriteWithClose, &[]); + result.expect("pass"); +} + +#[test] +fn check_spawn_wait_multiple() { + let result = run_spawn_cases(SpawnCasesCmd::WaitMultiple, &[]); + result.expect("pass"); +} + +#[test] +fn check_spawn_inherited_fds() { + let result = run_spawn_cases(SpawnCasesCmd::InheritedFds, &[]); + result.expect("pass"); +} + +#[test] +fn check_spawn_inherited_fds_without_owner() { + let result = run_spawn_cases(SpawnCasesCmd::InheritedFdsWithoutOwner, &[]); + result.expect("pass"); +} + +#[test] +fn check_spawn_read_then_close() { + let result = run_spawn_cases(SpawnCasesCmd::ReadThenClose, &[]); + result.expect("pass"); +} + +#[test] +fn check_spawn_max_vms_count() { + let result = run_spawn_cases(SpawnCasesCmd::MaxVmsCount, &[]); + result.expect("pass"); +} + +#[test] +fn check_spawn_max_fds_limit() { + let result = run_spawn_cases(SpawnCasesCmd::MaxFdsLimit, &[]); + result.expect("pass"); +} + +#[test] +fn check_close_invalid_fd() { + let result = run_spawn_cases(SpawnCasesCmd::CloseInvalidFd, &[]); + result.expect("pass"); +} + +#[test] +fn check_write_closed_fd() { + let result = run_spawn_cases(SpawnCasesCmd::WriteClosedFd, &[]); + result.expect("pass"); +} + +#[test] +fn check_pid() { + let result = run_spawn_cases(SpawnCasesCmd::CheckPID, &[]); + result.expect("pass"); +}