ckb_script/syscalls/
spawn.rs

1use crate::syscalls::{
2    INDEX_OUT_OF_BOUND, SLICE_OUT_OF_BOUND, SOURCE_ENTRY_MASK, SOURCE_GROUP_FLAG, SPAWN,
3    SPAWN_EXTRA_CYCLES_BASE, SPAWN_YIELD_CYCLES_BASE, Source,
4};
5use crate::types::{DataLocation, DataPieceId, Fd, Message, SgData, SpawnArgs, VmContext, VmId};
6use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider};
7use ckb_vm::{
8    Error as VMError, Register,
9    machine::SupportMachine,
10    memory::Memory,
11    registers::{A0, A1, A2, A3, A4, A7},
12    snapshot2::Snapshot2Context,
13    syscalls::Syscalls,
14};
15use std::sync::{Arc, Mutex};
16
17pub struct Spawn<DL>
18where
19    DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
20{
21    id: VmId,
22    message_box: Arc<Mutex<Vec<Message>>>,
23    snapshot2_context: Arc<Mutex<Snapshot2Context<DataPieceId, SgData<DL>>>>,
24}
25
26impl<DL> Spawn<DL>
27where
28    DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
29{
30    pub fn new(vm_id: &VmId, vm_context: &VmContext<DL>) -> Self {
31        Self {
32            id: *vm_id,
33            message_box: Arc::clone(&vm_context.message_box),
34            snapshot2_context: Arc::clone(&vm_context.snapshot2_context),
35        }
36    }
37}
38
39impl<Mac, DL> Syscalls<Mac> for Spawn<DL>
40where
41    Mac: SupportMachine,
42    DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
43{
44    fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> {
45        Ok(())
46    }
47
48    fn ecall(&mut self, machine: &mut Mac) -> Result<bool, VMError> {
49        if machine.registers()[A7].to_u64() != SPAWN {
50            return Ok(false);
51        }
52        let index = machine.registers()[A0].to_u64();
53        let mut source = machine.registers()[A1].to_u64();
54        let place = machine.registers()[A2].to_u64();
55        // To keep compatible with the old behavior. When Source is wrong, a
56        // Vm internal error should be returned.
57        if let Source::Group(_) = Source::parse_from_u64(source)? {
58            source = source & SOURCE_ENTRY_MASK | SOURCE_GROUP_FLAG;
59        } else {
60            source &= SOURCE_ENTRY_MASK;
61        }
62        let data_piece_id = match DataPieceId::try_from((source, index, place)) {
63            Ok(id) => id,
64            Err(_) => {
65                machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND));
66                return Ok(true);
67            }
68        };
69        let bounds = machine.registers()[A3].to_u64();
70        let offset = bounds >> 32;
71        let length = bounds as u32 as u64;
72        let spgs_addr = machine.registers()[A4].to_u64();
73        let argc_addr = spgs_addr;
74        let argc = machine
75            .memory_mut()
76            .load64(&Mac::REG::from_u64(argc_addr))?;
77        let argv_addr = spgs_addr.wrapping_add(8);
78        let argv = machine
79            .memory_mut()
80            .load64(&Mac::REG::from_u64(argv_addr))?;
81        let process_id_addr_addr = spgs_addr.wrapping_add(16);
82        let process_id_addr = machine
83            .memory_mut()
84            .load64(&Mac::REG::from_u64(process_id_addr_addr))?
85            .to_u64();
86        let fds_addr_addr = spgs_addr.wrapping_add(24);
87        let mut fds_addr = machine
88            .memory_mut()
89            .load64(&Mac::REG::from_u64(fds_addr_addr))?
90            .to_u64();
91
92        let mut fds = vec![];
93        if fds_addr != 0 {
94            loop {
95                let fd = machine
96                    .memory_mut()
97                    .load64(&Mac::REG::from_u64(fds_addr))?
98                    .to_u64();
99                if fd == 0 {
100                    break;
101                }
102                fds.push(Fd(fd));
103                fds_addr += 8;
104            }
105        }
106
107        // We are fetching the actual cell here for some in-place validation
108        let sc = self
109            .snapshot2_context
110            .lock()
111            .map_err(|e| VMError::Unexpected(e.to_string()))?;
112        let (_, full_length) = match sc.load_data(&data_piece_id, 0, 0) {
113            Ok(val) => val,
114            Err(VMError::SnapshotDataLoadError) => {
115                // This comes from TxData results in an out of bound error, to
116                // mimic current behavior, we would return INDEX_OUT_OF_BOUND error.
117                machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND));
118                return Ok(true);
119            }
120            Err(e) => return Err(e),
121        };
122        if offset >= full_length {
123            machine.set_register(A0, Mac::REG::from_u8(SLICE_OUT_OF_BOUND));
124            return Ok(true);
125        }
126        if length > 0 {
127            let end = offset.checked_add(length).ok_or(VMError::MemOutOfBound)?;
128            if end > full_length {
129                machine.set_register(A0, Mac::REG::from_u8(SLICE_OUT_OF_BOUND));
130                return Ok(true);
131            }
132        }
133        machine.add_cycles_no_checking(SPAWN_EXTRA_CYCLES_BASE)?;
134        machine.add_cycles_no_checking(SPAWN_YIELD_CYCLES_BASE)?;
135        self.message_box
136            .lock()
137            .map_err(|e| VMError::Unexpected(e.to_string()))?
138            .push(Message::Spawn(
139                self.id,
140                SpawnArgs {
141                    location: DataLocation {
142                        data_piece_id,
143                        offset,
144                        length,
145                    },
146                    argc: argc.to_u64(),
147                    argv: argv.to_u64(),
148                    fds,
149                    process_id_addr,
150                },
151            ));
152        Err(VMError::Yield)
153    }
154}