1pub mod generated {
2 pub mod traces {
3 use once_cell::sync::Lazy;
4 use prost_reflect::DescriptorPool;
5
6 pub static DESCRIPTOR_POOL: Lazy<DescriptorPool> = Lazy::new(|| {
7 DescriptorPool::decode(include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin")).as_ref())
8 .unwrap()
9 });
10
11 include!(concat!(env!("OUT_DIR"), "/generated.traces.rs"));
12 }
13}
14pub mod readonly_machines;
15
16use crate::{
17 generated::traces,
18 readonly_machines::{ReadonlyMachine, ReadonlySnapshotMachine},
19};
20use ckb_chain_spec::consensus::ConsensusBuilder;
21use ckb_mock_tx_types::{MockTransaction, Resource};
22use ckb_script::{
23 ROOT_VM_ID, Scheduler, generate_ckb_syscalls,
24 types::{DebugPrinter, ScriptGroup, SgData, TerminatedResult, VmContext, VmId, VmState},
25};
26use ckb_script::{
27 TransactionScriptsVerifier, TxVerifyEnv,
28 types::{DataPieceId, Machine},
29};
30use ckb_std::ckb_constants::Source;
31use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider};
32use ckb_types::{
33 core::{EpochNumberWithFraction, HeaderView, cell::resolve_transaction, hardfork},
34 packed::Byte32,
35 prelude::*,
36};
37use ckb_vm::{
38 CoreMachine, DefaultMachineRunner, Error, FlattenedArgsReader, Memory, Register, SupportMachine, Syscalls,
39 registers::{A0, A1, A2, A3, A4, A5, A7},
40 snapshot2::DataSource,
41};
42use ckb_vm_fuzzing_utils::SyscallCode;
43use clap::{Args, ValueEnum};
44use prost::Message;
45use serde::{Deserialize, Serialize};
46use std::collections::{HashMap, HashSet};
47use std::sync::{Arc, Mutex};
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
50pub enum CollectorKind {
51 Syscall,
53
54 TxParts,
56
57 VmCreate,
59}
60
61impl CollectorKind {
62 pub fn message_name(&self) -> &'static str {
63 match self {
64 CollectorKind::Syscall => "generated.traces.Syscalls",
65 CollectorKind::TxParts => "generated.traces.Parts",
66 CollectorKind::VmCreate => "generated.traces.VmCreations",
67 }
68 }
69}
70
71impl TryFrom<&[u8]> for traces::Parts {
72 type Error = Error;
73
74 fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
75 Self::decode(v).map_err(|e| Error::External(format!("prost decoding error: {}", e)))
76 }
77}
78
79impl From<traces::Parts> for Vec<u8> {
80 fn from(value: traces::Parts) -> Self {
81 value.encode_to_vec()
82 }
83}
84
85impl TryFrom<&[u8]> for traces::Syscalls {
86 type Error = Error;
87
88 fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
89 Self::decode(v).map_err(|e| Error::External(format!("prost decoding error: {}", e)))
90 }
91}
92
93impl From<traces::Syscalls> for Vec<u8> {
94 fn from(value: traces::Syscalls) -> Self {
95 value.encode_to_vec()
96 }
97}
98
99impl TryFrom<&[u8]> for traces::VmCreations {
100 type Error = Error;
101
102 fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
103 Self::decode(v).map_err(|e| Error::External(format!("prost decoding error: {}", e)))
104 }
105}
106
107impl From<traces::VmCreations> for Vec<u8> {
108 fn from(value: traces::VmCreations) -> Self {
109 value.encode_to_vec()
110 }
111}
112
113#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Args)]
114pub struct BinaryLocator {
115 #[arg(long)]
117 pub index: u64,
118
119 #[arg(long)]
121 pub source: u64,
122
123 #[arg(long)]
125 pub offset: u32,
126
127 #[arg(long)]
129 pub length: u32,
130
131 #[arg(long, value_parser = parse_from_cell, default_value_t = true)]
133 pub from_cell: bool,
134}
135
136fn parse_from_cell(s: &str) -> Result<bool, String> {
137 if s == "t" || s == "true" { Ok(true) } else { Ok(false) }
138}
139
140#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
141pub struct CollectorKey {
142 pub vm_id: VmId,
143 pub generation_id: u64,
144}
145
146#[derive(Clone, Debug, PartialEq, Eq)]
147pub struct CollectorResult<T> {
148 pub exit_code: i8,
149 pub cycles: u64,
150 pub traces: HashMap<CollectorKey, T>,
151}
152
153pub trait Collector: Clone + Default {
154 type Trace;
155
156 fn syscall_generator<DL, M>(
157 vm_id: &VmId,
158 sg_data: &SgData<DL>,
159 vm_context: &VmContext<DL>,
160 data: &Self,
161 ) -> Vec<Box<dyn Syscalls<M>>>
162 where
163 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
164 M: SupportMachine + 'static;
165
166 fn preprocess<DL, V, M>(
167 &self,
168 _verifier: &TransactionScriptsVerifier<DL, V, M>,
169 _script_group: &ScriptGroup,
170 _scheduler: &mut Scheduler<DL, V, M>,
171 ) -> Result<(), Error>
172 where
173 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
174 V: Clone,
175 M: DefaultMachineRunner,
176 {
177 Ok(())
178 }
179
180 fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
181 where
182 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
183 V: Clone,
184 M: DefaultMachineRunner;
185
186 fn seal(self) -> HashMap<CollectorKey, Self::Trace>;
187
188 fn build_verifier(
189 &self,
190 mock_tx: &MockTransaction,
191 ) -> Result<TransactionScriptsVerifier<Resource, Self, Machine>, Error> {
192 let resource = Resource::from_mock_tx(mock_tx).map_err(Error::External)?;
193 let resolved_transaction =
194 resolve_transaction(mock_tx.core_transaction(), &mut HashSet::new(), &resource, &resource)
195 .map_err(|e| Error::External(format!("resolving transaction error: {}", e)))?;
196
197 let hardforks = hardfork::HardForks {
198 ckb2021: hardfork::CKB2021::new_mirana().as_builder().rfc_0032(20).build().unwrap(),
199 ckb2023: hardfork::CKB2023::new_mirana().as_builder().rfc_0049(30).build().unwrap(),
200 };
201 let consensus = Arc::new(ConsensusBuilder::default().hardfork_switch(hardforks).build());
202 let epoch = EpochNumberWithFraction::new(35, 0, 1);
203 let header_view = HeaderView::new_advanced_builder().epoch(epoch.pack()).build();
204 let tx_env = Arc::new(TxVerifyEnv::new_commit(&header_view));
205 Ok(TransactionScriptsVerifier::new_with_generator(
206 Arc::new(resolved_transaction),
207 resource,
208 consensus,
209 tx_env,
210 Self::syscall_generator,
211 self.clone(),
212 ))
213 }
214
215 fn collect<DL, V, M>(
216 self,
217 verifier: &TransactionScriptsVerifier<DL, V, M>,
218 script_group: &ScriptGroup,
219 ) -> Result<CollectorResult<Self::Trace>, Error>
220 where
221 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
222 V: Clone,
223 M: DefaultMachineRunner,
224 {
225 let mut scheduler = verifier
226 .create_scheduler(script_group)
227 .map_err(|e| Error::External(format!("scheduler creation error: {}", e)))?;
228 self.preprocess(verifier, script_group, &mut scheduler)?;
229
230 let (exit_code, cycles) = loop {
231 let iteration_result = scheduler.iterate()?;
232 if let Some(TerminatedResult { exit_code, consumed_cycles: cycles }) = iteration_result.terminated_status {
233 break (exit_code, cycles);
234 }
235 self.postprocess(&mut scheduler)?;
236 };
237
238 Ok(CollectorResult { exit_code, cycles, traces: self.seal() })
239 }
240}
241
242#[derive(Default, Clone)]
243pub struct TxPartsBasedCollector {
244 syscall_collector: SyscallBasedCollector,
245 data: Arc<Mutex<HashMap<CollectorKey, traces::Parts>>>,
246}
247
248impl TxPartsBasedCollector {
249 fn fetch<V, F: Fn(&traces::Parts) -> V>(&self, vm_id: VmId, f: F) -> V {
250 let key = self.syscall_collector.key(vm_id);
251 let mut m = self.data.lock().expect("lock");
252
253 let parts = m.entry(key).or_default();
254 f(parts)
255 }
256
257 fn modify<F: Fn(&mut traces::Parts)>(&mut self, vm_id: VmId, f: F) {
258 let key = self.syscall_collector.key(vm_id);
259 let mut m = self.data.lock().expect("lock");
260
261 let parts = m.entry(key).or_default();
262 f(parts);
263 }
264}
265
266impl Collector for TxPartsBasedCollector {
267 type Trace = traces::Parts;
268
269 fn syscall_generator<DL, M>(
270 vm_id: &VmId,
271 sg_data: &SgData<DL>,
272 vm_context: &VmContext<DL>,
273 data: &Self,
274 ) -> Vec<Box<dyn Syscalls<M>>>
275 where
276 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
277 M: SupportMachine + 'static,
278 {
279 vec![Box::new(TxPartsBasedCollectorVMSyscalls {
280 vm_id: *vm_id,
281 sg_data: sg_data.clone(),
282 data: data.clone(),
283 inner_collector_syscalls: SyscallBasedCollector::syscall_generator(
284 vm_id,
285 sg_data,
286 vm_context,
287 &data.syscall_collector,
288 ),
289 ckb_syscalls: generate_ckb_syscalls(vm_id, sg_data, vm_context, &debug_printer()),
290 })]
291 }
292
293 fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
294 where
295 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
296 V: Clone,
297 M: DefaultMachineRunner,
298 {
299 self.syscall_collector.postprocess(scheduler)
302 }
303
304 fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
305 let mut m = self.data.lock().expect("lock").clone();
306 for (key, syscalls) in self.syscall_collector.seal() {
307 m.entry(key).or_default().other_syscalls = Some(syscalls);
308 }
309 m
310 }
311}
312
313struct TxPartsBasedCollectorVMSyscalls<DL, M> {
314 vm_id: VmId,
315 sg_data: SgData<DL>,
316 data: TxPartsBasedCollector,
317 inner_collector_syscalls: Vec<Box<dyn Syscalls<M>>>,
318 ckb_syscalls: Vec<Box<dyn Syscalls<M>>>,
319}
320
321impl<DL: CellDataProvider + Send + Sync, M: SupportMachine> Syscalls<M> for TxPartsBasedCollectorVMSyscalls<DL, M> {
322 fn initialize(&mut self, machine: &mut M) -> Result<(), Error> {
323 for syscall in &mut self.inner_collector_syscalls {
324 syscall.initialize(machine)?;
325 }
326 for syscall in &mut self.ckb_syscalls {
327 syscall.initialize(machine)?;
328 }
329 Ok(())
330 }
331
332 fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
333 let mut skip_syscall_based_collector = false;
336 if let Ok(code) = machine.registers()[A7].to_u64().try_into() {
337 match code {
338 SyscallCode::LoadTxHash => {
339 let tx_hash = self.sg_data.rtx.transaction.hash();
340 self.data.modify(self.vm_id, |parts| {
341 parts.tx_hash = tx_hash.as_slice().to_vec();
342 });
343 skip_syscall_based_collector = true;
344 }
345 SyscallCode::LoadCell => {
346 let index = machine.registers()[A3].to_u64();
347 let source = machine.registers()[A4].to_u64();
348
349 if let Some(actual_index) = locate_input(index, source, &self.sg_data.sg_info.script_group) {
350 let fill_length = std::cmp::min(actual_index + 1, self.sg_data.rtx.resolved_inputs.len());
351 let already_filled_length = self.data.fetch(self.vm_id, |parts| parts.input_cells.len());
352
353 if already_filled_length < fill_length {
354 let inputs: Vec<Vec<u8>> = self
355 .sg_data
356 .rtx
357 .resolved_inputs
358 .iter()
359 .skip(already_filled_length)
360 .take(fill_length - already_filled_length)
361 .map(|meta| meta.cell_output.as_slice().to_vec())
362 .collect();
363
364 self.data.modify(self.vm_id, |parts| parts.input_cells.extend_from_slice(&inputs));
365 }
366 skip_syscall_based_collector = true;
367 }
368 }
369 SyscallCode::LoadCellData => {
370 let index = machine.registers()[A3].to_u64();
371 let source = machine.registers()[A4].to_u64();
372
373 if let Some(actual_index) = locate_input(index, source, &self.sg_data.sg_info.script_group) {
374 let fill_length = std::cmp::min(actual_index + 1, self.sg_data.rtx.resolved_inputs.len());
375 let already_filled_length = self.data.fetch(self.vm_id, |parts| parts.input_cell_data.len());
376
377 if already_filled_length < fill_length {
378 let input_data: Vec<Vec<u8>> = self
379 .sg_data
380 .rtx
381 .resolved_inputs
382 .iter()
383 .skip(already_filled_length)
384 .take(fill_length - already_filled_length)
385 .map(|meta| {
386 self.sg_data.data_loader().load_cell_data(meta).expect("load data").to_vec()
387 })
388 .collect();
389
390 self.data.modify(self.vm_id, |parts| parts.input_cell_data.extend_from_slice(&input_data));
391 }
392 skip_syscall_based_collector = true;
393 }
394 }
395 SyscallCode::LoadWitness => {
396 let index = machine.registers()[A3].to_u64();
397 let source = machine.registers()[A4].to_u64();
398
399 if let Some(actual_index) = locate_witness(index, source, &self.sg_data.sg_info.script_group) {
400 let fill_length =
401 std::cmp::min(actual_index + 1, self.sg_data.rtx.transaction.witnesses().len());
402 let already_filled_length = self.data.fetch(self.vm_id, |parts| parts.witnesses.len());
403
404 if already_filled_length < fill_length {
405 let witnesses: Vec<Vec<u8>> = self
406 .sg_data
407 .rtx
408 .transaction
409 .witnesses()
410 .into_iter()
411 .skip(already_filled_length)
412 .take(fill_length - already_filled_length)
413 .map(|witness| witness.raw_data().to_vec())
414 .collect();
415
416 self.data.modify(self.vm_id, |parts| parts.witnesses.extend_from_slice(&witnesses));
417 }
418 skip_syscall_based_collector = true;
419 }
420 }
421 _ => (),
422 }
423 }
424
425 if skip_syscall_based_collector {
426 delegate_to_syscalls(machine, &mut self.ckb_syscalls)
427 } else {
428 delegate_to_syscalls(machine, &mut self.inner_collector_syscalls)
429 }
430 }
431}
432
433#[derive(Default, Clone)]
434pub struct SyscallBasedCollector {
435 partial_contents: Arc<Mutex<HashMap<CollectorKey, PartialSyscallContent>>>,
436 data: Arc<Mutex<HashMap<CollectorKey, traces::Syscalls>>>,
437 generation_tracker: GenerationTracker,
438}
439
440impl SyscallBasedCollector {
441 fn insert_syscall(&self, key: CollectorKey, syscall: traces::Syscall) {
447 self.data.lock().expect("lock").entry(key).or_default().syscalls.push(syscall);
448 }
449
450 fn set_args(&self, key: CollectorKey, args: Vec<Vec<u8>>) {
451 self.data.lock().expect("lock").entry(key).or_default().args = args;
452 }
453
454 pub fn key(&self, vm_id: VmId) -> CollectorKey {
455 self.generation_tracker.key(vm_id)
456 }
457}
458
459impl Collector for SyscallBasedCollector {
460 type Trace = traces::Syscalls;
461
462 fn syscall_generator<DL, M>(
463 vm_id: &VmId,
464 sg_data: &SgData<DL>,
465 vm_context: &VmContext<DL>,
466 data: &Self,
467 ) -> Vec<Box<dyn Syscalls<M>>>
468 where
469 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
470 M: SupportMachine + 'static,
471 {
472 let mut syscalls = GenerationTracker::syscall_generator(vm_id, sg_data, vm_context, &data.generation_tracker);
475 for syscall in generate_ckb_syscalls(vm_id, sg_data, vm_context, &debug_printer()) {
476 syscalls.push(syscall);
477 }
478
479 vec![Box::new(SyscallBasedCollectorVMSyscalls { vm_id: *vm_id, data: data.clone(), syscalls })]
480 }
481
482 fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
483 where
484 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
485 V: Clone,
486 M: DefaultMachineRunner,
487 {
488 self.generation_tracker.postprocess(scheduler)?;
489
490 let mut fulfills = HashSet::new();
491 for (key, partial_content) in self.partial_contents.lock().expect("lock").iter() {
492 if scheduler.state(&key.vm_id) == Some(VmState::Runnable) {
494 let syscall = scheduler.peek(
495 &key.vm_id,
496 |machine| apply_partial_content(partial_content, &mut ReadonlyMachine::new(machine.inner_mut())),
497 |snapshot, sg_data| {
498 apply_partial_content(
499 partial_content,
500 &mut ReadonlySnapshotMachine::<
501 _,
502 _,
503 <<M as DefaultMachineRunner>::Inner as CoreMachine>::REG,
504 >::new(snapshot, sg_data),
505 )
506 },
507 )?;
508
509 match (partial_content, &syscall) {
511 (
512 PartialSyscallContent::Spawn { args },
513 traces::Syscall { value: Some(traces::syscall::Value::SuccessOutputData(process_id)) },
514 ) => {
515 let key = self.key(*process_id);
516 self.set_args(key, args.clone());
517 }
518 (
519 PartialSyscallContent::Exec { args },
520 traces::Syscall { value: Some(traces::syscall::Value::Terminated(_)) },
521 ) => {
522 let key = self.key(key.vm_id);
524 self.set_args(key, args.clone());
525 }
526 _ => (),
527 }
528
529 self.insert_syscall(key.clone(), syscall);
530 fulfills.insert(key.clone());
531 }
532 }
533
534 self.partial_contents.lock().expect("lock").retain(|test_key, _| !fulfills.contains(test_key));
535 Ok(())
536 }
537
538 fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
539 self.data.lock().expect("lock").clone()
540 }
541}
542
543struct SyscallBasedCollectorVMSyscalls<M> {
544 vm_id: VmId,
545 data: SyscallBasedCollector,
546 syscalls: Vec<Box<dyn Syscalls<M>>>,
547}
548
549impl<M: SupportMachine> Syscalls<M> for SyscallBasedCollectorVMSyscalls<M> {
550 fn initialize(&mut self, machine: &mut M) -> Result<(), Error> {
551 for syscall in &mut self.syscalls {
552 syscall.initialize(machine)?;
553 }
554 Ok(())
555 }
556
557 fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
558 let key = self.data.key(self.vm_id);
559 assert!(!self.data.partial_contents.lock().expect("lock").contains_key(&key));
560
561 let partial_content = build_partial_content(machine)?;
562
563 let result = delegate_to_syscalls(machine, &mut self.syscalls);
564 if let Some(partial_content) = partial_content {
565 match result {
566 Ok(true) => {
567 let data = apply_partial_content(&partial_content, &mut ReadonlyMachine::new(machine))?;
569 self.data.insert_syscall(key, data);
570 }
571 Err(Error::Yield) => {
572 self.data.partial_contents.lock().expect("lock").insert(key, partial_content);
574 }
575 _ => {
576 }
579 }
580 }
581
582 result
583 }
584}
585
586enum PartialSyscallContent {
587 ReturnWithCode,
588 IoData { data_addr: u64, input_length: u64 },
589 IoDataAsCode { data_addr: u64, input_length: u64 }, Exec { args: Vec<Vec<u8>> },
591 Spawn { args: Vec<Vec<u8>> },
592 Wait,
593 Pipe { fds_addr: u64 },
594 Write,
595 Read,
596 InheritedFd { buffer_addr: u64 },
597}
598
599fn build_partial_content<M: SupportMachine>(machine: &mut M) -> Result<Option<PartialSyscallContent>, Error> {
600 Ok(if let Ok(code) = machine.registers()[A7].to_u64().try_into() {
601 Some(match code {
602 SyscallCode::LoadTransaction
603 | SyscallCode::LoadScript
604 | SyscallCode::LoadTxHash
605 | SyscallCode::LoadScriptHash
606 | SyscallCode::LoadCell
607 | SyscallCode::LoadHeader
608 | SyscallCode::LoadInput
609 | SyscallCode::LoadWitness
610 | SyscallCode::LoadCellByField
611 | SyscallCode::LoadHeaderByField
612 | SyscallCode::LoadInputByField
613 | SyscallCode::LoadCellData
614 | SyscallCode::LoadBlockExtension => {
615 let data_addr = machine.registers()[A0].to_u64();
616 let length_addr = machine.registers()[A1].to_u64();
617 let input_length = machine.memory_mut().load64(&M::REG::from_u64(length_addr))?.to_u64();
618
619 PartialSyscallContent::IoData { data_addr, input_length }
620 }
621 SyscallCode::LoadCellDataAsCode => {
622 let data_addr = machine.registers()[A0].to_u64();
623 let input_length = machine.registers()[A3].to_u64();
624 PartialSyscallContent::IoDataAsCode { data_addr, input_length }
625 }
626 SyscallCode::VmVersion => PartialSyscallContent::ReturnWithCode,
627 SyscallCode::CurrentCycles => PartialSyscallContent::ReturnWithCode,
628 SyscallCode::Exec => {
629 let argc = machine.registers()[A4].to_u64();
630 let argv = machine.registers()[A5].to_u64();
631 let args = extract_args(machine, argc, argv)?;
632 PartialSyscallContent::Exec { args }
633 }
634 SyscallCode::Spawn => {
635 let (argc, argv) = extract_spawn_argc_argv(machine)?;
636 let args = extract_args(machine, argc, argv)?;
637 PartialSyscallContent::Spawn { args }
638 }
639 SyscallCode::Wait => PartialSyscallContent::Wait,
640 SyscallCode::ProcessId => PartialSyscallContent::ReturnWithCode,
641 SyscallCode::Pipe => {
642 let fds_addr = machine.registers()[A0].to_u64();
643 PartialSyscallContent::Pipe { fds_addr }
644 }
645 SyscallCode::Write => PartialSyscallContent::Write,
646 SyscallCode::Read => PartialSyscallContent::Read,
647 SyscallCode::InheritedFd => {
648 let buffer_addr = machine.registers()[A0].to_u64();
649 PartialSyscallContent::InheritedFd { buffer_addr }
650 }
651 SyscallCode::Close => PartialSyscallContent::ReturnWithCode,
652 SyscallCode::Debug => return Ok(None),
653 })
654 } else {
655 None
656 })
657}
658
659fn apply_partial_content<M: SupportMachine>(
661 partial_content: &PartialSyscallContent,
662 machine: &mut M,
663) -> Result<traces::Syscall, Error> {
664 Ok(match partial_content {
665 PartialSyscallContent::ReturnWithCode => {
666 let return_code = machine.registers()[A0].to_i64();
667 return return_syscall(return_code);
668 }
669 PartialSyscallContent::IoData { data_addr, input_length } => {
670 let return_code = machine.registers()[A0].to_i64();
671 if return_code != 0 {
672 return return_syscall(return_code);
673 }
674 let length_addr = machine.registers()[A1].clone();
675 let output_length = machine.memory_mut().load64(&length_addr)?.to_u64();
676
677 let actual_data_length = std::cmp::min(*input_length, output_length);
678 let data = machine.memory_mut().load_bytes(*data_addr, actual_data_length)?;
679
680 traces::Syscall {
681 value: Some(traces::syscall::Value::IoData(traces::IoData {
682 available_data: data.as_ref().to_vec(),
683 additional_length: output_length - data.len() as u64,
684 })),
685 }
686 }
687 PartialSyscallContent::IoDataAsCode { data_addr, input_length } => {
688 let return_code = machine.registers()[A0].to_i64();
689 if return_code != 0 {
690 return return_syscall(return_code);
691 }
692 let data = machine.memory_mut().load_bytes(*data_addr, *input_length)?;
693 traces::Syscall {
694 value: Some(traces::syscall::Value::IoData(traces::IoData {
695 available_data: data.as_ref().to_vec(),
696 additional_length: 0,
697 })),
698 }
699 }
700 PartialSyscallContent::Exec { .. } => {
701 let return_code = machine.registers()[A0].to_i64();
702 if return_code != 0 {
703 return return_syscall(return_code);
704 }
705 traces::Syscall { value: Some(traces::syscall::Value::Terminated(traces::Terminated {})) }
706 }
707 PartialSyscallContent::Spawn { .. } => {
708 let return_code = machine.registers()[A0].to_i64();
709 if return_code != 0 {
710 return return_syscall(return_code);
711 }
712 let process_id = extract_spawned_process_id(machine)?;
713 traces::Syscall { value: Some(traces::syscall::Value::SuccessOutputData(process_id)) }
714 }
715 PartialSyscallContent::Wait => {
716 let return_code = machine.registers()[A0].to_i64();
717 if return_code != 0 {
718 return return_syscall(return_code);
719 }
720 let exit_code_addr = machine.registers()[A1].clone();
721 let exit_code = machine.memory_mut().load8(&exit_code_addr)?.to_i64() as u64;
722 traces::Syscall { value: Some(traces::syscall::Value::SuccessOutputData(exit_code)) }
723 }
724 PartialSyscallContent::Pipe { fds_addr } => {
725 let return_code = machine.registers()[A0].to_i64();
726 if return_code != 0 {
727 return return_syscall(return_code);
728 }
729 let fd1 = machine.memory_mut().load64(&M::REG::from_u64(*fds_addr))?.to_u64();
730 let fd2 = machine.memory_mut().load64(&M::REG::from_u64(*fds_addr + 8))?.to_u64();
731 traces::Syscall { value: Some(traces::syscall::Value::Fds(traces::Fds { fds: vec![fd1, fd2] })) }
732 }
733 PartialSyscallContent::Write => {
734 let return_code = machine.registers()[A0].to_i64();
735 if return_code != 0 {
736 return return_syscall(return_code);
737 }
738 let length_addr = machine.registers()[A2].clone();
739 let length = machine.memory_mut().load64(&length_addr)?.to_u64();
740 traces::Syscall { value: Some(traces::syscall::Value::SuccessOutputData(length)) }
741 }
742 PartialSyscallContent::Read => {
743 let return_code = machine.registers()[A0].to_i64();
744 if return_code != 0 {
745 return return_syscall(return_code);
746 }
747 let data_addr = machine.registers()[A1].to_u64();
748 let length_addr = machine.registers()[A2].clone();
749 let length = machine.memory_mut().load64(&length_addr)?.to_u64();
750 let data = machine.memory_mut().load_bytes(data_addr, length)?;
751
752 traces::Syscall {
753 value: Some(traces::syscall::Value::IoData(traces::IoData {
754 available_data: data.as_ref().to_vec(),
755 additional_length: 0,
756 })),
757 }
758 }
759 PartialSyscallContent::InheritedFd { buffer_addr } => {
760 let count_addr = machine.registers()[A1].clone();
761 let count = machine.memory_mut().load64(&count_addr)?.to_u64();
762
763 let addr = *buffer_addr;
764 let mut fds = Vec::with_capacity(count as usize);
765 for i in 0..count {
766 fds.push(machine.memory_mut().load64(&M::REG::from_u64(addr + i * 8))?.to_u64());
767 }
768 traces::Syscall { value: Some(traces::syscall::Value::Fds(traces::Fds { fds })) }
769 }
770 })
771}
772
773fn return_syscall(code: i64) -> Result<traces::Syscall, Error> {
774 Ok(traces::Syscall { value: Some(traces::syscall::Value::ReturnWithCode(code)) })
775}
776
777#[derive(Default, Clone)]
778pub struct GenerationTracker {
779 pending: Arc<Mutex<HashSet<VmId>>>,
780 generations: Arc<Mutex<HashMap<VmId, u64>>>,
781}
782
783impl GenerationTracker {
784 fn mark_pending(&self, vm_id: VmId) {
785 let mut s = self.pending.lock().expect("lock");
786
787 s.insert(vm_id);
788 }
789
790 fn increase_generation(&self, vm_id: VmId) {
791 let mut m = self.generations.lock().expect("lock");
792
793 *m.entry(vm_id).or_default() += 1;
794 }
795
796 pub fn generation(&self, vm_id: VmId) -> u64 {
797 let mut m = self.generations.lock().expect("lock");
798
799 *m.entry(vm_id).or_default()
800 }
801
802 pub fn key(&self, vm_id: VmId) -> CollectorKey {
803 CollectorKey { vm_id, generation_id: self.generation(vm_id) }
804 }
805}
806
807impl Collector for GenerationTracker {
808 type Trace = ();
809
810 fn syscall_generator<DL, M>(
811 vm_id: &VmId,
812 _sg_data: &SgData<DL>,
813 _vm_context: &VmContext<DL>,
814 data: &Self,
815 ) -> Vec<Box<dyn Syscalls<M>>>
816 where
817 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
818 M: SupportMachine + 'static,
819 {
820 vec![Box::new(GenerationTrackerSyscalls { vm_id: *vm_id, data: data.clone() })]
821 }
822
823 fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
824 where
825 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
826 V: Clone,
827 M: DefaultMachineRunner,
828 {
829 for vm_id in self.pending.lock().expect("lock").drain() {
830 assert_eq!(scheduler.state(&vm_id), Some(VmState::Runnable));
831 let terminated = scheduler.peek(
832 &vm_id,
833 |machine| {
834 let machine = ReadonlyMachine::new(machine.inner_mut());
835 Ok(machine.registers()[A0].to_u64() == 0)
836 },
837 |snapshot, sg_data| {
838 let machine =
839 ReadonlySnapshotMachine::<_, _, <<M as DefaultMachineRunner>::Inner as CoreMachine>::REG>::new(
840 snapshot, sg_data,
841 );
842 Ok(machine.registers()[A0].to_u64() == 0)
843 },
844 )?;
845 if terminated {
846 self.increase_generation(vm_id);
847 }
848 }
849 Ok(())
850 }
851
852 fn seal(self) -> HashMap<CollectorKey, ()> {
853 HashMap::default()
854 }
855}
856
857struct GenerationTrackerSyscalls {
858 vm_id: VmId,
859 data: GenerationTracker,
860}
861
862impl<M: SupportMachine> Syscalls<M> for GenerationTrackerSyscalls {
863 fn initialize(&mut self, _machine: &mut M) -> Result<(), Error> {
864 Ok(())
865 }
866
867 fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
868 if let Ok(code) = SyscallCode::try_from(machine.registers()[A7].to_u64()) {
869 if code == SyscallCode::Exec {
870 self.data.mark_pending(self.vm_id);
871 }
872 }
873 Ok(false)
874 }
875}
876
877#[derive(Default, Clone)]
878pub struct BinaryLocatorCollector<C: Collector> {
879 partial_locators: Arc<Mutex<HashMap<CollectorKey, PartialLocator>>>,
880 data: Arc<Mutex<HashMap<CollectorKey, BinaryLocator>>>,
881 generation_tracker: GenerationTracker,
882 collector: C,
883}
884
885impl<C: Collector + Send + 'static> Collector for BinaryLocatorCollector<C> {
886 type Trace = (BinaryLocator, C::Trace);
887
888 fn syscall_generator<DL, M>(
889 vm_id: &VmId,
890 sg_data: &SgData<DL>,
891 vm_context: &VmContext<DL>,
892 data: &Self,
893 ) -> Vec<Box<dyn Syscalls<M>>>
894 where
895 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
896 M: SupportMachine + 'static,
897 {
898 let mut syscalls = GenerationTracker::syscall_generator(vm_id, sg_data, vm_context, &data.generation_tracker);
901 for syscall in C::syscall_generator(vm_id, sg_data, vm_context, &data.collector) {
902 syscalls.push(syscall);
903 }
904
905 vec![Box::new(BinaryLocatorCollectorSyscalls { vm_id: *vm_id, data: data.clone(), syscalls })]
906 }
907
908 fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
909 where
910 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
911 V: Clone,
912 M: DefaultMachineRunner,
913 {
914 self.generation_tracker.postprocess(scheduler)?;
915 self.collector.postprocess(scheduler)?;
916
917 let sg_data = scheduler.sg_data().clone();
918
919 for (key, partial_locator) in self.partial_locators.lock().expect("lock").drain() {
920 assert_eq!(scheduler.state(&key.vm_id), Some(VmState::Runnable));
921
922 let new_key = self.generation_tracker.key(key.vm_id);
923 let locator = scheduler.peek(
924 &key.vm_id,
925 |machine| {
926 apply_partial_locator(
927 &key,
928 &new_key,
929 &partial_locator,
930 &mut ReadonlyMachine::new(machine.inner_mut()),
931 &sg_data,
932 )
933 },
934 |snapshot, sg_data| {
935 apply_partial_locator(
936 &key,
937 &new_key,
938 &partial_locator,
939 &mut ReadonlySnapshotMachine::<
940 _,
941 _,
942 <<M as DefaultMachineRunner>::Inner as CoreMachine>::REG,
943 >::new(snapshot, sg_data),
944 sg_data,
945 )
946 },
947 )?;
948
949 if let Some((vm_id, locator)) = locator {
950 let key = self.generation_tracker.key(vm_id);
951 self.data.lock().expect("lock").insert(key, locator);
952 }
953 }
954 Ok(())
955 }
956
957 fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
958 let mut self_data = self.data.lock().expect("lock").clone();
959
960 let mut result = HashMap::default();
961 for (key, collector_trace) in self.collector.seal().drain() {
962 if let Some(self_trace) = self_data.remove(&key) {
963 result.insert(key, (self_trace, collector_trace));
964 }
965 }
966 result
967 }
968
969 fn preprocess<DL, V, M>(
970 &self,
971 verifier: &TransactionScriptsVerifier<DL, V, M>,
972 script_group: &ScriptGroup,
973 scheduler: &mut Scheduler<DL, V, M>,
974 ) -> Result<(), Error>
975 where
976 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
977 V: Clone,
978 M: DefaultMachineRunner,
979 {
980 let root_locator = BinaryLocator {
981 index: scheduler
982 .sg_data()
983 .tx_info
984 .extract_referenced_dep_index(&script_group.script)
985 .map_err(|e| Error::External(format!("extract dep index: {}", e)))? as u64,
986 source: Source::CellDep as u64,
987 offset: 0,
988 length: verifier
989 .extract_script(&script_group.script)
990 .map_err(|e| Error::External(format!("extract script error: {}", e)))?
991 .len() as u32,
992 from_cell: true,
993 };
994 self.data.lock().expect("lock").insert(CollectorKey { vm_id: ROOT_VM_ID, generation_id: 0 }, root_locator);
995
996 Ok(())
997 }
998}
999
1000struct BinaryLocatorCollectorSyscalls<C: Collector, M> {
1001 vm_id: VmId,
1002 data: BinaryLocatorCollector<C>,
1003 syscalls: Vec<Box<dyn Syscalls<M>>>,
1004}
1005
1006impl<C: Collector + Send, M: SupportMachine> Syscalls<M> for BinaryLocatorCollectorSyscalls<C, M> {
1007 fn initialize(&mut self, machine: &mut M) -> Result<(), Error> {
1008 for syscall in &mut self.syscalls {
1009 syscall.initialize(machine)?;
1010 }
1011 Ok(())
1012 }
1013
1014 fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
1015 let key = self.data.generation_tracker.key(self.vm_id);
1016 assert!(!self.data.partial_locators.lock().expect("lock").contains_key(&key));
1017
1018 if let Ok(code) = SyscallCode::try_from(machine.registers()[A7].to_u64()) {
1019 if code == SyscallCode::Exec || code == SyscallCode::Spawn {
1020 if let Some(partial_locator) = build_partial_locator(machine) {
1025 self.data.partial_locators.lock().expect("lock").insert(key, partial_locator);
1026 }
1027 }
1028 }
1029
1030 delegate_to_syscalls(machine, &mut self.syscalls)
1031 }
1032}
1033
1034enum PartialLocator {
1035 Exec(BinaryLocator),
1036 Spawn(BinaryLocator),
1037}
1038
1039enum PartialVmCreate {
1040 Exec(CollectorKey),
1041 Spawn(CollectorKey),
1042}
1043
1044#[derive(Default, Clone)]
1045pub struct VmCreateCollector {
1046 pending: Arc<Mutex<Option<PartialVmCreate>>>,
1047 data: Arc<Mutex<HashMap<CollectorKey, Vec<CollectorKey>>>>,
1048 generation_tracker: GenerationTracker,
1049}
1050
1051impl Collector for VmCreateCollector {
1052 type Trace = traces::VmCreations;
1053
1054 fn syscall_generator<DL, M>(
1055 vm_id: &VmId,
1056 sg_data: &SgData<DL>,
1057 vm_context: &VmContext<DL>,
1058 data: &Self,
1059 ) -> Vec<Box<dyn Syscalls<M>>>
1060 where
1061 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
1062 M: SupportMachine + 'static,
1063 {
1064 let mut syscalls = GenerationTracker::syscall_generator(vm_id, sg_data, vm_context, &data.generation_tracker);
1067 for syscall in generate_ckb_syscalls(vm_id, sg_data, vm_context, &debug_printer()) {
1068 syscalls.push(syscall);
1069 }
1070 vec![Box::new(VmCreateCollectorVMSyscalls { vm_id: *vm_id, data: data.clone(), syscalls })]
1071 }
1072
1073 fn preprocess<DL, V, M>(
1074 &self,
1075 _verifier: &TransactionScriptsVerifier<DL, V, M>,
1076 _script_group: &ScriptGroup,
1077 _scheduler: &mut Scheduler<DL, V, M>,
1078 ) -> Result<(), Error>
1079 where
1080 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
1081 V: Clone,
1082 M: DefaultMachineRunner,
1083 {
1084 let key = self.generation_tracker.key(ROOT_VM_ID);
1085 self.data.lock().unwrap().insert(key, vec![]);
1086 Ok(())
1087 }
1088
1089 fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
1090 where
1091 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
1092 V: Clone,
1093 M: DefaultMachineRunner,
1094 {
1095 self.generation_tracker.postprocess(scheduler)?;
1096
1097 if let Some(partial_vm_create) = self.pending.lock().unwrap().take() {
1098 match partial_vm_create {
1099 PartialVmCreate::Exec(key) => {
1100 let child_key = self.generation_tracker.key(key.vm_id);
1101 self.data.lock().unwrap().entry(key).or_default().push(child_key.clone());
1102 self.data.lock().unwrap().insert(child_key, vec![]);
1103 }
1104 PartialVmCreate::Spawn(key) => {
1105 assert_eq!(scheduler.state(&key.vm_id), Some(VmState::Runnable));
1106 let child_id = scheduler.peek(
1107 &key.vm_id,
1108 |machine| {
1109 let mut machine = ReadonlyMachine::new(machine.inner_mut());
1110 extract_spawned_process_id(&mut machine)
1111 },
1112 |snapshot, sg_data| {
1113 let mut machine = ReadonlySnapshotMachine::<
1114 _,
1115 _,
1116 <<M as DefaultMachineRunner>::Inner as CoreMachine>::REG,
1117 >::new(snapshot, sg_data);
1118 extract_spawned_process_id(&mut machine)
1119 },
1120 )?;
1121 let child_key = self.generation_tracker.key(child_id);
1122 self.data.lock().unwrap().entry(key).or_default().push(child_key.clone());
1123 self.data.lock().unwrap().insert(child_key, vec![]);
1124 }
1125 }
1126 }
1127 Ok(())
1128 }
1129
1130 fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
1131 self.data
1132 .lock()
1133 .expect("lock")
1134 .iter()
1135 .map(|(k, v)| {
1136 (
1137 k.clone(),
1138 traces::VmCreations {
1139 vm_creations: v
1140 .iter()
1141 .map(|ck| traces::VmCreation { vm_id: ck.vm_id, generation_id: ck.generation_id })
1142 .collect(),
1143 },
1144 )
1145 })
1146 .collect()
1147 }
1148}
1149
1150struct VmCreateCollectorVMSyscalls<M> {
1151 vm_id: VmId,
1152 data: VmCreateCollector,
1153 syscalls: Vec<Box<dyn Syscalls<M>>>,
1154}
1155
1156impl<M: SupportMachine> Syscalls<M> for VmCreateCollectorVMSyscalls<M> {
1157 fn initialize(&mut self, machine: &mut M) -> Result<(), Error> {
1158 for syscall in &mut self.syscalls {
1159 syscall.initialize(machine)?;
1160 }
1161 Ok(())
1162 }
1163
1164 fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
1165 if let Ok(code) = SyscallCode::try_from(machine.registers()[A7].to_u64()) {
1166 let key = self.data.generation_tracker.key(self.vm_id);
1167 match code {
1168 SyscallCode::Exec => {
1169 let _ = self.data.pending.lock().unwrap().insert(PartialVmCreate::Exec(key));
1170 }
1171 SyscallCode::Spawn => {
1172 let _ = self.data.pending.lock().unwrap().insert(PartialVmCreate::Spawn(key));
1173 }
1174 _ => {}
1175 }
1176 }
1177 delegate_to_syscalls(machine, &mut self.syscalls)
1178 }
1179}
1180
1181macro_rules! define_combine_collector {
1182 ($name:ident, $trace_name:ident, $($field:ident: $type:ident),*) => {
1183 #[derive(Clone)]
1184 pub struct $trace_name<$($type: Collector),*> {
1185 $(pub $field: Option<$type::Trace>),*
1186 }
1187
1188 impl<$($type: Collector),*> Default for $trace_name<$($type),*> {
1189 fn default() -> Self {
1190 Self { $($field: None),* }
1191 }
1192 }
1193
1194 #[derive(Default, Clone)]
1195 pub struct $name<$($type: Collector),*> {
1196 $(pub $field: $type),*
1197 }
1198
1199 impl<$($type: Collector),*> $name<$($type),*> {
1200 pub fn new($($field: $type),*) -> Self {
1201 Self {
1202 $($field),*
1203 }
1204 }
1205 }
1206
1207 impl<$($type: Collector),*> Collector for $name<$($type),*>
1208 {
1209 type Trace = $trace_name<$($type),*>;
1210
1211 fn syscall_generator<DL, M>(
1212 vm_id: &VmId,
1213 sg_data: &SgData<DL>,
1214 vm_context: &VmContext<DL>,
1215 data: &Self,
1216 ) -> Vec<Box<dyn Syscalls<M>>>
1217 where
1218 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
1219 M: SupportMachine + 'static,
1220 {
1221 let mut r = vec![];
1222 $(r.extend($type::syscall_generator(vm_id, sg_data, vm_context, &data.$field));)*
1223 r
1224 }
1225
1226 fn preprocess<DL, V, M>(
1227 &self,
1228 verifier: &TransactionScriptsVerifier<DL, V, M>,
1229 script_group: &ScriptGroup,
1230 scheduler: &mut Scheduler<DL, V, M>,
1231 ) -> Result<(), Error>
1232 where
1233 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
1234 V: Clone,
1235 M: DefaultMachineRunner,
1236 {
1237 $(self.$field.preprocess(verifier, script_group, scheduler)?;)*
1238 Ok(())
1239 }
1240
1241 fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
1242 where
1243 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
1244 V: Clone,
1245 M: DefaultMachineRunner,
1246 {
1247 $(self.$field.postprocess(scheduler)?;)*
1248 Ok(())
1249 }
1250
1251 fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
1252 let mut r: HashMap<CollectorKey, Self::Trace> = HashMap::new();
1253 $(
1254 for (k, v) in self.$field.seal() {
1255 let d = r.entry(k).or_insert_with($trace_name::default);
1256 d.$field = Some(v);
1257 }
1258 )*
1259 r
1260 }
1261 }
1262 };
1263}
1264
1265define_combine_collector!(CombineCollector2, CombineCollector2Trace, c1: C, c2: D);
1266define_combine_collector!(CombineCollector3, CombineCollector3Trace, c1: C, c2: D, c3: E);
1267define_combine_collector!(CombineCollector4, CombineCollector4Trace, c1: C, c2: D, c3: E, c4: F);
1268define_combine_collector!(CombineCollector5, CombineCollector5Trace, c1: C, c2: D, c3: E, c4: F, c5: G);
1269define_combine_collector!(CombineCollector6, CombineCollector6Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H);
1270define_combine_collector!(CombineCollector7, CombineCollector7Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I);
1271define_combine_collector!(CombineCollector8, CombineCollector8Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J);
1272define_combine_collector!(CombineCollector9, CombineCollector9Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K);
1273define_combine_collector!(CombineCollector10, CombineCollector10Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L);
1274define_combine_collector!(CombineCollector11, CombineCollector11Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N);
1275define_combine_collector!(CombineCollector12, CombineCollector12Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O);
1276define_combine_collector!(CombineCollector13, CombineCollector13Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O, c13: P);
1277define_combine_collector!(CombineCollector14, CombineCollector14Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O, c13: P, c14: Q);
1278define_combine_collector!(CombineCollector15, CombineCollector15Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O, c13: P, c14: Q, c15: R);
1279define_combine_collector!(CombineCollector16, CombineCollector16Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O, c13: P, c14: Q, c15: R, c16: S);
1280
1281fn build_partial_locator<M: SupportMachine>(machine: &mut M) -> Option<PartialLocator> {
1282 let regs = machine.registers();
1283 let index = regs[A0].to_u64();
1284 let source = regs[A1].to_u64();
1285 let bounds = regs[A3].to_u64();
1286 let offset = (bounds >> 32) as u32;
1287 let length = bounds as u32;
1288 let from_cell = regs[A2].to_u64() == 0;
1289 let locator = BinaryLocator { index, source, offset, length, from_cell };
1290
1291 if let Ok(code) = machine.registers()[A7].to_u64().try_into() {
1292 match code {
1293 SyscallCode::Exec => Some(PartialLocator::Exec(locator)),
1294 SyscallCode::Spawn => Some(PartialLocator::Spawn(locator)),
1295 _ => None,
1296 }
1297 } else {
1298 None
1299 }
1300}
1301
1302fn apply_partial_locator<M: SupportMachine, DL: CellDataProvider>(
1303 old_key: &CollectorKey,
1304 new_key: &CollectorKey,
1305 partial_locator: &PartialLocator,
1306 machine: &mut M,
1307 sg_data: &SgData<DL>,
1308) -> Result<Option<(VmId, BinaryLocator)>, Error> {
1309 assert_eq!(old_key.vm_id, new_key.vm_id);
1310
1311 match partial_locator {
1312 PartialLocator::Exec(locator) => {
1313 if old_key != new_key {
1314 Ok(Some((new_key.vm_id, normalize_locator(locator, sg_data)?)))
1315 } else {
1316 Ok(None)
1317 }
1318 }
1319 PartialLocator::Spawn(locator) => {
1320 let process_id = extract_spawned_process_id(machine)?;
1321 assert_ne!(process_id, new_key.vm_id);
1322
1323 Ok(Some((process_id, normalize_locator(locator, sg_data)?)))
1324 }
1325 }
1326}
1327
1328fn normalize_locator<DL: CellDataProvider>(
1331 locator: &BinaryLocator,
1332 sg_data: &SgData<DL>,
1333) -> Result<BinaryLocator, Error> {
1334 if locator.length > 0 {
1335 return Ok(locator.clone());
1336 }
1337
1338 let data_piece_id = DataPieceId::try_from((locator.source, locator.index, if locator.from_cell { 0 } else { 1 }))
1339 .map_err(|e| Error::External(format!("Converting data piece error: {}", e)))?;
1340
1341 let (_, length) = sg_data
1342 .load_data(&data_piece_id, locator.offset as u64, 0)
1343 .ok_or_else(|| Error::External(format!("Locator {:?} is invalid!", locator)))?;
1344
1345 let mut locator = locator.clone();
1346 locator.length = length as u32;
1347
1348 Ok(locator)
1349}
1350
1351fn delegate_to_syscalls<M: SupportMachine>(
1352 machine: &mut M,
1353 syscalls: &mut [Box<dyn Syscalls<M>>],
1354) -> Result<bool, Error> {
1355 for syscall in syscalls {
1356 let processed = syscall.ecall(machine)?;
1357 if processed {
1358 return Ok(true);
1359 }
1360 }
1361 Ok(false)
1362}
1363
1364fn debug_printer() -> DebugPrinter {
1365 Arc::new(|_hash: &Byte32, message: &str| {
1366 let message = message.trim_end_matches('\n');
1367 if !message.is_empty() {
1368 println!("Script log: {}", message);
1369 }
1370 })
1371}
1372
1373fn locate_input(index: u64, source: u64, script_group: &ScriptGroup) -> Option<usize> {
1374 if source == Source::Input as u64 {
1375 return Some(index as usize);
1376 } else if source == Source::GroupInput as u64 {
1377 return script_group.input_indices.get(index as usize).copied();
1378 }
1379 None
1380}
1381
1382fn locate_witness(index: u64, source: u64, script_group: &ScriptGroup) -> Option<usize> {
1383 if source == Source::Input as u64 || source == Source::Output as u64 {
1384 return Some(index as usize);
1385 } else if source == Source::GroupInput as u64 {
1386 return script_group.input_indices.get(index as usize).copied();
1387 } else if source == Source::GroupOutput as u64 {
1388 return script_group.output_indices.get(index as usize).copied();
1389 }
1390 None
1391}
1392
1393fn extract_spawned_process_id<M: SupportMachine>(machine: &mut M) -> Result<u64, Error> {
1394 let spgs_addr = machine.registers()[A4].clone();
1395 let process_id_addr_addr = spgs_addr.overflowing_add(&M::REG::from_u64(16));
1396 let process_id_addr = machine.memory_mut().load64(&process_id_addr_addr)?;
1397 let process_id = machine.memory_mut().load64(&process_id_addr)?.to_u64();
1398
1399 Ok(process_id)
1400}
1401
1402fn extract_spawn_argc_argv<M: SupportMachine>(machine: &mut M) -> Result<(u64, u64), Error> {
1403 let spgs_addr = machine.registers()[A4].clone();
1404 let argc_addr = spgs_addr.clone();
1405 let argc = machine.memory_mut().load64(&argc_addr)?.to_u64();
1406 let argv_addr = spgs_addr.overflowing_add(&M::REG::from_u64(8));
1407 let argv = machine.memory_mut().load64(&argv_addr)?.to_u64();
1408 Ok((argc, argv))
1409}
1410
1411fn extract_args<M: SupportMachine>(machine: &mut M, argc: u64, argv: u64) -> Result<Vec<Vec<u8>>, Error> {
1412 let reader = FlattenedArgsReader::new(machine.memory_mut(), M::REG::from_u64(argc), M::REG::from_u64(argv));
1413 let mut result = Vec::with_capacity(reader.len());
1414 for item in reader {
1415 result.push(item?.to_vec());
1416 }
1417 Ok(result)
1418}