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