ckb_std/syscalls/
native.rs

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