ckb_std/syscalls/
native.rs

1use crate::{ckb_constants::*, error::SysError};
2use core::ffi::CStr;
3
4#[cfg(target_arch = "riscv64")]
5unsafe fn syscall(mut a0: u64, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64, a7: u64) -> u64 {
6    unsafe {
7        core::arch::asm!(
8          "ecall",
9          inout("a0") a0,
10          in("a1") a1,
11          in("a2") a2,
12          in("a3") a3,
13          in("a4") a4,
14          in("a5") a5,
15          in("a7") a7
16        );
17        a0
18    }
19}
20
21#[cfg(not(target_arch = "riscv64"))]
22unsafe fn syscall(_a0: u64, _a1: u64, _a2: u64, _a3: u64, _a4: u64, _a5: u64, _a7: u64) -> u64 {
23    u64::MAX
24}
25
26/// Exit, this script will be terminated after the exit syscall.
27/// exit code `0` represents verification is success, others represent error code.
28pub fn exit(code: i8) -> ! {
29    unsafe { syscall(code as u64, 0, 0, 0, 0, 0, SYS_EXIT) };
30    loop {}
31}
32
33/// Load data
34/// Return data length or syscall error
35fn syscall_load(
36    buf_ptr: *mut u8,
37    len: usize,
38    offset: usize,
39    a3: u64,
40    a4: u64,
41    a5: u64,
42    syscall_num: u64,
43) -> Result<usize, SysError> {
44    let mut actual_data_len = len;
45    let len_ptr: *mut usize = &mut actual_data_len;
46    let ret = unsafe {
47        syscall(
48            buf_ptr as u64,
49            len_ptr as u64,
50            offset as u64,
51            a3,
52            a4,
53            a5,
54            syscall_num,
55        )
56    };
57    SysError::build_syscall_result(ret, len, actual_data_len)
58}
59
60/// Load transaction hash
61///
62/// Return the loaded data length or a syscall error
63///
64/// # Arguments
65///
66/// * `buf` - a writable buf used to receive the data
67/// * `offset` - offset
68///
69/// # Example
70///
71/// ```
72/// let mut tx_hash = [0u8; 32];
73/// let len = load_tx_hash(&mut tx_hash, 0).unwrap();
74/// assert_eq!(len, tx_hash.len());
75/// ```
76pub fn load_tx_hash(buf: &mut [u8], offset: usize) -> Result<usize, SysError> {
77    syscall_load(
78        buf.as_mut_ptr(),
79        buf.len(),
80        offset,
81        0,
82        0,
83        0,
84        SYS_LOAD_TX_HASH,
85    )
86}
87
88/// Load script hash
89///
90/// Return the loaded data length or a syscall error
91///
92/// # Arguments
93///
94/// * `buf` - a writable buf used to receive the data
95/// * `offset` - offset
96///
97/// # Example
98///
99/// ```
100/// let mut script_hash = [0u8; 32];
101/// let len = load_script_hash(&mut script_hash, 0).unwrap();
102/// assert_eq!(len, script_hash.len());
103/// ```
104pub fn load_script_hash(buf: &mut [u8], offset: usize) -> Result<usize, SysError> {
105    syscall_load(
106        buf.as_mut_ptr(),
107        buf.len(),
108        offset,
109        0,
110        0,
111        0,
112        SYS_LOAD_SCRIPT_HASH,
113    )
114}
115
116/// Load cell
117///
118/// Return the loaded data length or a syscall error
119///
120/// # Arguments
121///
122/// * `buf` - a writable buf used to receive the data
123/// * `offset` - offset
124/// * `index` - index of cell
125/// * `source` - source of cell
126pub fn load_cell(
127    buf: &mut [u8],
128    offset: usize,
129    index: usize,
130    source: Source,
131) -> Result<usize, SysError> {
132    syscall_load(
133        buf.as_mut_ptr(),
134        buf.len(),
135        offset,
136        index as u64,
137        source as u64,
138        0,
139        SYS_LOAD_CELL,
140    )
141}
142
143/// Load input
144///
145/// Return the loaded data length or a syscall error
146///
147/// # Arguments
148///
149/// * `buf` - a writable buf used to receive the data
150/// * `offset` - offset
151/// * `index` - index of cell
152/// * `source` - source of cell
153pub fn load_input(
154    buf: &mut [u8],
155    offset: usize,
156    index: usize,
157    source: Source,
158) -> Result<usize, SysError> {
159    syscall_load(
160        buf.as_mut_ptr(),
161        buf.len(),
162        offset,
163        index as u64,
164        source as u64,
165        0,
166        SYS_LOAD_INPUT,
167    )
168}
169
170/// Load header
171///
172/// Return the loaded data length or a syscall error
173///
174/// # Arguments
175///
176/// * `buf` - a writable buf used to receive the data
177/// * `offset` - offset
178/// * `index` - index of cell or header
179/// * `source` - source
180pub fn load_header(
181    buf: &mut [u8],
182    offset: usize,
183    index: usize,
184    source: Source,
185) -> Result<usize, SysError> {
186    syscall_load(
187        buf.as_mut_ptr(),
188        buf.len(),
189        offset,
190        index as u64,
191        source as u64,
192        0,
193        SYS_LOAD_HEADER,
194    )
195}
196
197/// Load witness
198///
199/// Return the loaded data length or a syscall error
200///
201/// # Arguments
202///
203/// * `buf` - a writable buf used to receive the data
204/// * `offset` - offset
205/// * `index` - index of cell
206/// * `source` - source
207pub fn load_witness(
208    buf: &mut [u8],
209    offset: usize,
210    index: usize,
211    source: Source,
212) -> Result<usize, SysError> {
213    syscall_load(
214        buf.as_mut_ptr(),
215        buf.len(),
216        offset,
217        index as u64,
218        source as u64,
219        0,
220        SYS_LOAD_WITNESS,
221    )
222}
223
224/// Load transaction
225///
226/// Return the loaded data length or a syscall error
227///
228/// # Arguments
229///
230/// * `buf` - a writable buf used to receive the data
231/// * `offset` - offset
232pub fn load_transaction(buf: &mut [u8], offset: usize) -> Result<usize, SysError> {
233    syscall_load(
234        buf.as_mut_ptr(),
235        buf.len(),
236        offset,
237        0,
238        0,
239        0,
240        SYS_LOAD_TRANSACTION,
241    )
242}
243
244/// Load cell by field
245///
246/// Return the loaded data length or a syscall error
247///
248/// # Arguments
249///
250/// * `buf` - a writable buf used to receive the data
251/// * `offset` - offset
252/// * `index` - index of cell
253/// * `source` - source of cell
254/// * `field` - field of cell
255///
256/// # Example
257///
258/// ```
259/// let mut buf = [0u8; size_of::<u64>()];
260/// let len = load_cell_by_field(&mut buf, 0, 0, Source::GroupInput, CellField::Capacity).unwrap();
261/// assert_eq!(len, buf.len());
262/// ```
263pub fn load_cell_by_field(
264    buf: &mut [u8],
265    offset: usize,
266    index: usize,
267    source: Source,
268    field: CellField,
269) -> Result<usize, SysError> {
270    syscall_load(
271        buf.as_mut_ptr(),
272        buf.len(),
273        offset,
274        index as u64,
275        source as u64,
276        field as u64,
277        SYS_LOAD_CELL_BY_FIELD,
278    )
279}
280
281/// Load header by field
282///
283/// Return the loaded data length or a syscall error
284///
285/// # Arguments
286///
287/// * `buf` - a writable buf used to receive the data
288/// * `offset` - offset
289/// * `index` - index
290/// * `source` - source
291/// * `field` - field
292///
293/// # Example
294///
295/// ```
296/// let mut buf = [0u8; 8];
297/// let len = load_header_by_field(&mut buf, 0, index, source, HeaderField::EpochNumber)?;
298/// debug_assert_eq!(len, buf.len());
299/// ```
300pub fn load_header_by_field(
301    buf: &mut [u8],
302    offset: usize,
303    index: usize,
304    source: Source,
305    field: HeaderField,
306) -> Result<usize, SysError> {
307    syscall_load(
308        buf.as_mut_ptr(),
309        buf.len(),
310        offset,
311        index as u64,
312        source as u64,
313        field as u64,
314        SYS_LOAD_HEADER_BY_FIELD,
315    )
316}
317
318/// Load input by field
319///
320/// Return the loaded data length or a syscall error
321///
322/// # Arguments
323///
324/// * `buf` - a writable buf used to receive the data
325/// * `offset` - offset
326/// * `index` - index
327/// * `source` - source
328/// * `field` - field
329///
330/// # Example
331///
332/// ```
333/// let mut buf = [0u8; 8];
334/// let len = load_input_by_field(&mut buf, 0, index, source, InputField::Since)?;
335/// debug_assert_eq!(len, buf.len());
336/// ```
337pub fn load_input_by_field(
338    buf: &mut [u8],
339    offset: usize,
340    index: usize,
341    source: Source,
342    field: InputField,
343) -> Result<usize, SysError> {
344    syscall_load(
345        buf.as_mut_ptr(),
346        buf.len(),
347        offset,
348        index as u64,
349        source as u64,
350        field as u64,
351        SYS_LOAD_INPUT_BY_FIELD,
352    )
353}
354
355/// Load cell data, read cell data
356///
357/// Return the loaded data length or a syscall error
358///
359/// # Arguments
360///
361/// * `buf` - a writable buf used to receive the data
362/// * `offset` - offset
363/// * `index` - index
364/// * `source` - source
365pub fn load_cell_data(
366    buf: &mut [u8],
367    offset: usize,
368    index: usize,
369    source: Source,
370) -> Result<usize, SysError> {
371    syscall_load(
372        buf.as_mut_ptr(),
373        buf.len(),
374        offset,
375        index as u64,
376        source as u64,
377        0,
378        SYS_LOAD_CELL_DATA,
379    )
380}
381
382/// Load script
383///
384/// Return the loaded data length or a syscall error
385///
386/// # Arguments
387///
388/// * `buf` - a writable buf used to receive the data
389/// * `offset` - offset
390pub fn load_script(buf: &mut [u8], offset: usize) -> Result<usize, SysError> {
391    syscall_load(
392        buf.as_mut_ptr(),
393        buf.len(),
394        offset,
395        0,
396        0,
397        0,
398        SYS_LOAD_SCRIPT,
399    )
400}
401
402/// Output debug message
403///
404/// You should use the macro version syscall: `debug!`
405///
406/// # Arguments
407///
408/// * `s` - string to output
409pub fn debug(mut s: alloc::string::String) {
410    s.push('\0');
411    let c_str = s.into_bytes();
412    unsafe {
413        syscall(c_str.as_ptr() as u64, 0, 0, 0, 0, 0, SYS_DEBUG);
414    }
415}
416
417/// Load cell data, read cell data
418///
419/// Return the loaded data length or a syscall error
420///
421/// # Arguments
422///
423/// * `buf_ptr` - a writable pointer used to receive the data
424/// * `len` - length that the `buf_ptr` can receives.
425/// * `offset` - offset
426/// * `index` - index
427/// * `source` - source
428pub fn load_cell_data_raw(
429    buf_ptr: *mut u8,
430    len: usize,
431    offset: usize,
432    index: usize,
433    source: Source,
434) -> Result<usize, SysError> {
435    syscall_load(
436        buf_ptr,
437        len,
438        offset,
439        index as u64,
440        source as u64,
441        0,
442        SYS_LOAD_CELL_DATA,
443    )
444}
445
446/// Load cell code, read cell data as executable code
447///
448/// Return the loaded data length or a syscall error
449///
450/// # Arguments
451///
452/// * `buf_ptr` - a writable pointer used to receive the data
453/// * `len` - length that the `buf_ptr` can receives.
454/// * `content_offset` - offset
455/// * `content_size` - read length
456/// * `index` - index
457/// * `source` - source
458pub fn load_cell_code(
459    buf_ptr: *mut u8,
460    len: usize,
461    content_offset: usize,
462    content_size: usize,
463    index: usize,
464    source: Source,
465) -> Result<usize, SysError> {
466    let ret = unsafe {
467        syscall(
468            buf_ptr as u64,
469            len as u64,
470            content_offset as u64,
471            content_size as u64,
472            index as u64,
473            source as u64,
474            SYS_LOAD_CELL_DATA_AS_CODE,
475        )
476    };
477    SysError::build_syscall_result(ret, len, len)
478}
479
480/// *VM version* syscall returns current running VM version, so far 2 values will be returned:
481///   - Error for Lina CKB-VM version
482///   - 1 for the new hardfork CKB-VM version.
483///
484/// This syscall consumes 500 cycles.
485pub fn vm_version() -> Result<u64, SysError> {
486    let ret = unsafe { syscall(0, 0, 0, 0, 0, 0, SYS_VM_VERSION) };
487    match ret {
488        1 | 2 => Ok(ret),
489        _ => Err(SysError::Unknown(ret)),
490    }
491}
492
493/// *Current Cycles* returns current cycle consumption just before executing this syscall.
494///  This syscall consumes 500 cycles.
495pub fn current_cycles() -> u64 {
496    unsafe { syscall(0, 0, 0, 0, 0, 0, SYS_CURRENT_CYCLES) }
497}
498
499/// Exec runs an executable file from specified cell data in the context of an
500/// already existing machine, replacing the previous executable. The used cycles
501/// does not change, but the code, registers and memory of the vm are replaced
502/// by those of the new program. It's cycles consumption consists of two parts:
503///
504/// - Fixed 500 cycles
505/// - Initial Loading Cycles (<https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0014-vm-cycle-limits/0014-vm-
506///   cycle-limits.md>)
507///
508/// The arguments used here are:
509///
510///   * `index`: an index value denoting the index of entries to read.
511///   * `source`: a flag denoting the source of cells or witnesses to locate, possible values include:
512///       + 1: input cells.
513///       + `0x0100000000000001`: input cells with the same running script as current script
514///       + 2: output cells.
515///       + `0x0100000000000002`: output cells with the same running script as current script
516///       + 3: dep cells.
517///   * `place`: A value of 0 or 1:
518///       + 0: read from cell data
519///       + 1: read from witness
520///   * `bounds`: high 32 bits means `offset`, low 32 bits means `length`. if `length` equals to zero, it read to end
521///               instead of reading 0 bytes.
522///   * `argv`: argv is a one-dimensional array of strings
523pub fn exec(index: usize, source: Source, place: usize, bounds: usize, argv: &[&CStr]) -> u64 {
524    // https://www.gnu.org/software/libc/manual/html_node/Program-Arguments.html
525    let argc = argv.len();
526    // On some platforms, CStr may be u8, adding as *const i8 is used to reduce such warnings
527    let argv_ptr: alloc::vec::Vec<*const i8> =
528        argv.iter().map(|e| e.as_ptr() as *const i8).collect();
529    unsafe {
530        syscall(
531            index as u64,
532            source as u64,
533            place as u64,
534            bounds as u64,
535            argc as u64,
536            argv_ptr.as_ptr() as u64,
537            SYS_EXEC,
538        )
539    }
540}
541
542pub use crate::syscalls::internal::SpawnArgs;
543
544/// The parent process calls the Spawn system call, which creates a new process (a child process) that is an
545/// independent ckb-vm instance. It's important to note that the parent process will not be blocked by the child
546/// process as a result of this syscall.
547/// Note: available after ckb 2nd hardfork.
548///
549/// # Arguments
550///
551/// * `index`, `source`, `bounds` and `place` - same as exec.
552/// * `spgs` - spawn arguments.
553///
554/// Returns success or a syscall error.
555///
556/// # Scheduler Algorithm V1
557///
558/// This document describes the design and functionality of a scheduler algorithm, covering process states, system call
559/// behavior, message handling, and priority rules. The scheduler manages virtual processes, transitioning them through
560/// various states based on operations and interactions, to ensure efficient scheduling.
561///
562/// # Process States
563///
564/// Each process within this scheduler has one of the following six states:
565/// * Runnable: The process is ready to execute.
566/// * Running: The process is running.
567/// * Terminated: The process has completed its execution.
568/// * WaitForRead: The process is waiting for data to be available for it to read.
569/// * WaitForWrite: The process is waiting for another process to read data it wants to write.
570/// * WaitForExit: The process is waiting for another process to exit before it can continue.
571///
572/// # System Calls and State Transitions
573///
574/// Specific system calls are responsible for changing the state of a process within this scheduler:
575/// * spawn: Creates a new process, initializing it in the Runnable state.
576/// * read: Attempts to read data from a file descriptor. If data is unavailable or is less than expected, the process
577///     state changes to WaitForRead.
578/// * write: Attempts to write data to a file descriptor. If the operation is blocked due to data needing to be read by
579///     another process, the process enters the WaitForWrite state. The process will stay in state WaitForWrite until
580///     all data has been consumed.
581/// * wait: Waits for a target process to exit. Once the target process has terminated, the waiting process transitions
582///     to Runnable.
583///
584/// # IO Handling and State Recovery
585///
586/// IO handling allows processes in certain states to transition back to Runnable when specific conditions are met:
587/// * A WaitForRead process becomes Runnable once the needed data has been read successfully.
588/// * A WaitForWrite process transitions to Runnable once its data has been successfully read by another process.
589///
590/// # Process Priority
591///
592/// The scheduler assigns incremental IDs to processes, establishing an execution order:
593/// * The root process has an ID of 0.
594/// * When multiple processes are in the Runnable state, the scheduler selects the process with the lowest ID to execute
595///     first. This ensures a predictable and fair ordering of execution for processes ready to run. The selected
596///     process status is changed to Running, and it continues to run until its status is changed.
597pub fn spawn(
598    index: usize,
599    source: Source,
600    place: usize,
601    bounds: usize,
602    spgs: &mut SpawnArgs,
603) -> Result<u64, SysError> {
604    let ret = unsafe {
605        syscall(
606            index as u64,
607            source as u64,
608            place as u64,
609            bounds as u64,
610            spgs as *mut SpawnArgs as u64,
611            0,
612            SYS_SPAWN,
613        )
614    };
615    match ret {
616        0 => Ok(unsafe { *spgs.process_id }),
617        1 => Err(SysError::IndexOutOfBound),
618        2 => Err(SysError::ItemMissing),
619        3 => Err(SysError::Encoding),
620        6 => Err(SysError::InvalidFd),
621        8 => Err(SysError::MaxVmsSpawned),
622        x => Err(SysError::Unknown(x)),
623    }
624}
625
626/// The syscall pauses until the execution of a process specified by pid has ended.
627/// Note: available after ckb 2nd hardfork.
628///
629/// # Arguments
630///
631/// * `pid` - process id
632///
633/// Returns exit code.
634pub fn wait(pid: u64) -> Result<i8, SysError> {
635    let mut code: u64 = 0;
636    let ret = unsafe { syscall(pid, &mut code as *mut u64 as u64, 0, 0, 0, 0, SYS_WAIT) };
637    match ret {
638        0 => Ok(code as i8),
639        5 => Err(SysError::WaitFailure),
640        x => Err(SysError::Unknown(x)),
641    }
642}
643
644/// This syscall is used to get the current process id. Root process ID is 0.
645/// Note: available after ckb 2nd hardfork.
646pub fn process_id() -> u64 {
647    unsafe { syscall(0, 0, 0, 0, 0, 0, SYS_PROCESS_ID) }
648}
649
650/// This syscall create a pipe with read-write pair of file descriptions. The file descriptor with read permission is
651/// located at fds[0], and the corresponding file descriptor with write permission is located at fds[1].
652/// Note: available after ckb 2nd hardfork.
653pub fn pipe() -> Result<(u64, u64), SysError> {
654    let mut fds: [u64; 2] = [0, 0];
655    let ret = unsafe { syscall(fds.as_mut_ptr() as u64, 0, 0, 0, 0, 0, SYS_PIPE) };
656    match ret {
657        0 => Ok((fds[0], fds[1])),
658        9 => Err(SysError::MaxFdsCreated),
659        x => Err(SysError::Unknown(x)),
660    }
661}
662
663/// This syscall reads data from a pipe via a file descriptor. The syscall Read attempts to read up to value pointed by
664/// length bytes from file descriptor fd into the buffer, and the actual length of data read is returned.
665/// Note: available after ckb 2nd hardfork.
666pub fn read(fd: u64, buffer: &mut [u8]) -> Result<usize, SysError> {
667    let mut l: u64 = buffer.len() as u64;
668    let ret = unsafe {
669        syscall(
670            fd,
671            buffer.as_mut_ptr() as u64,
672            &mut l as *mut u64 as u64,
673            0,
674            0,
675            0,
676            SYS_READ,
677        )
678    };
679    match ret {
680        0 => Ok(l as usize),
681        1 => Err(SysError::IndexOutOfBound),
682        6 => Err(SysError::InvalidFd),
683        7 => Err(SysError::OtherEndClosed),
684        x => Err(SysError::Unknown(x)),
685    }
686}
687
688/// This syscall writes data to a pipe via a file descriptor. The syscall Write writes up to value pointed by length
689/// bytes from the buffer, and the actual length of data written is returned.
690///
691/// If buffer is empty and fd is avaliable, then write() can still succeed: A data with a length of 0 is written to the
692/// pipe. The peer needs to use a read() syscall to consume the empty data, and read() will returns Ok(0).
693///
694/// Note: available after ckb 2nd hardfork.
695pub fn write(fd: u64, buffer: &[u8]) -> Result<usize, SysError> {
696    let mut l: u64 = buffer.len() as u64;
697    let ret = unsafe {
698        syscall(
699            fd,
700            buffer.as_ptr() as u64,
701            &mut l as *mut u64 as u64,
702            0,
703            0,
704            0,
705            SYS_WRITE,
706        )
707    };
708    match ret {
709        0 => Ok(l as usize),
710        1 => Err(SysError::IndexOutOfBound),
711        6 => Err(SysError::InvalidFd),
712        7 => Err(SysError::OtherEndClosed),
713        x => Err(SysError::Unknown(x)),
714    }
715}
716
717/// This syscall retrieves the file descriptors available to the current process, which are passed in from the parent
718/// process. These results are copied from the inherited_fds parameter of the Spawn syscall.
719/// Note: available after ckb 2nd hardfork.
720pub fn inherited_fds(fds: &mut [u64]) -> u64 {
721    let mut l: u64 = fds.len() as u64;
722    unsafe {
723        syscall(
724            fds.as_mut_ptr() as u64,
725            &mut l as *mut u64 as u64,
726            0,
727            0,
728            0,
729            0,
730            SYS_INHERITED_FDS,
731        )
732    };
733    l
734}
735
736/// This syscall manually closes a file descriptor. After calling this, any attempt to read/write the file descriptor
737/// pointed to the other end would fail.
738/// Note: available after ckb 2nd hardfork.
739pub fn close(fd: u64) -> Result<(), SysError> {
740    let ret = unsafe { syscall(fd, 0, 0, 0, 0, 0, SYS_CLOSE) };
741    match ret {
742        0 => Ok(()),
743        6 => Err(SysError::InvalidFd),
744        x => Err(SysError::Unknown(x)),
745    }
746}
747
748/// Load extension field associated either with an input cell, a dep cell, or
749/// a header dep based on source and index value.
750///
751/// # Arguments
752///
753/// * `buf` - a writable buf used to receive the data
754/// * `offset` - offset
755/// * `index` - index of cell
756/// * `source` - source of cell
757///
758/// Note: available after ckb 2nd hardfork.
759pub fn load_block_extension(
760    buf: &mut [u8],
761    offset: usize,
762    index: usize,
763    source: Source,
764) -> Result<usize, SysError> {
765    syscall_load(
766        buf.as_mut_ptr(),
767        buf.len(),
768        offset,
769        index as u64,
770        source as u64,
771        0,
772        SYS_LOAD_BLOCK_EXTENSION,
773    )
774}