1use crate::{error::ScriptError, verify_env::TxVerifyEnv};
2use ckb_chain_spec::consensus::Consensus;
3use ckb_types::{
4 core::{
5 cell::{CellMeta, ResolvedTransaction},
6 Cycle, ScriptHashType,
7 },
8 packed::{Byte32, CellOutput, OutPoint, Script},
9 prelude::*,
10};
11use ckb_vm::{
12 machine::{VERSION0, VERSION1, VERSION2},
13 ISA_B, ISA_IMC, ISA_MOP,
14};
15use serde::{Deserialize, Serialize};
16use std::collections::{BTreeMap, HashMap};
17use std::fmt;
18use std::sync::{
19 atomic::{AtomicU64, Ordering},
20 Arc, Mutex, RwLock,
21};
22
23#[cfg(has_asm)]
24use ckb_vm::machine::asm::{AsmCoreMachine, AsmMachine};
25
26#[cfg(not(has_asm))]
27use ckb_vm::{DefaultCoreMachine, TraceMachine, WXorXMemory};
28
29use ckb_traits::CellDataProvider;
30use ckb_vm::snapshot2::Snapshot2Context;
31
32use ckb_vm::{
33 bytes::Bytes,
34 machine::Pause,
35 snapshot2::{DataSource, Snapshot2},
36 RISCV_GENERAL_REGISTER_NUMBER,
37};
38use std::mem::size_of;
39
40pub type VmIsa = u8;
42pub type VmVersion = u32;
44
45#[cfg(has_asm)]
46pub(crate) type CoreMachineType = AsmCoreMachine;
47#[cfg(all(not(has_asm), not(feature = "flatmemory")))]
48pub(crate) type CoreMachineType = DefaultCoreMachine<u64, WXorXMemory<ckb_vm::SparseMemory<u64>>>;
49#[cfg(all(not(has_asm), feature = "flatmemory"))]
50pub(crate) type CoreMachineType = DefaultCoreMachine<u64, WXorXMemory<ckb_vm::FlatMemory<u64>>>;
51
52#[cfg(has_asm)]
54pub type CoreMachine = Box<AsmCoreMachine>;
55#[cfg(all(not(has_asm), not(feature = "flatmemory")))]
57pub type CoreMachine = DefaultCoreMachine<u64, WXorXMemory<ckb_vm::SparseMemory<u64>>>;
58#[cfg(all(not(has_asm), feature = "flatmemory"))]
59pub type CoreMachine = DefaultCoreMachine<u64, WXorXMemory<ckb_vm::FlatMemory<u64>>>;
60
61#[cfg(has_asm)]
62pub(crate) type Machine = AsmMachine;
63#[cfg(not(has_asm))]
64pub(crate) type Machine = TraceMachine<CoreMachine>;
65
66pub(crate) type DebugPrinter = Arc<dyn Fn(&Byte32, &str) + Send + Sync>;
67
68pub struct DebugContext {
69 pub debug_printer: DebugPrinter,
70 #[cfg(test)]
71 pub skip_pause: Arc<std::sync::atomic::AtomicBool>,
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
76pub enum ScriptVersion {
77 V0 = 0,
79 V1 = 1,
81 V2 = 2,
83}
84
85impl ScriptVersion {
86 pub const fn latest() -> Self {
88 Self::V2
89 }
90
91 pub fn vm_isa(self) -> VmIsa {
93 match self {
94 Self::V0 => ISA_IMC,
95 Self::V1 => ISA_IMC | ISA_B | ISA_MOP,
96 Self::V2 => ISA_IMC | ISA_B | ISA_MOP,
97 }
98 }
99
100 pub fn vm_version(self) -> VmVersion {
102 match self {
103 Self::V0 => VERSION0,
104 Self::V1 => VERSION1,
105 Self::V2 => VERSION2,
106 }
107 }
108
109 pub fn data_hash_type(self) -> ScriptHashType {
115 match self {
116 Self::V0 => ScriptHashType::Data,
117 Self::V1 => ScriptHashType::Data1,
118 Self::V2 => ScriptHashType::Data2,
119 }
120 }
121
122 pub fn init_core_machine_without_limit(self) -> CoreMachine {
126 self.init_core_machine(u64::MAX)
127 }
128
129 pub fn init_core_machine(self, max_cycles: Cycle) -> CoreMachine {
131 let isa = self.vm_isa();
132 let version = self.vm_version();
133 CoreMachineType::new(isa, version, max_cycles)
134 }
135}
136
137#[derive(Clone, Debug)]
143pub struct ScriptGroup {
144 pub script: Script,
148 pub group_type: ScriptGroupType,
150 pub input_indices: Vec<usize>,
152 pub output_indices: Vec<usize>,
154}
155
156impl ScriptGroup {
167 pub(crate) fn new(script: &Script, group_type: ScriptGroupType) -> Self {
169 Self {
170 group_type,
171 script: script.to_owned(),
172 input_indices: vec![],
173 output_indices: vec![],
174 }
175 }
176
177 pub(crate) fn from_lock_script(script: &Script) -> Self {
179 Self::new(script, ScriptGroupType::Lock)
180 }
181
182 pub(crate) fn from_type_script(script: &Script) -> Self {
184 Self::new(script, ScriptGroupType::Type)
185 }
186}
187
188#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
193#[serde(rename_all = "snake_case")]
194pub enum ScriptGroupType {
195 Lock,
197 Type,
199}
200
201impl fmt::Display for ScriptGroupType {
202 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
203 match self {
204 ScriptGroupType::Lock => write!(f, "Lock"),
205 ScriptGroupType::Type => write!(f, "Type"),
206 }
207 }
208}
209
210#[derive(Clone)]
213pub struct TransactionState {
214 pub current: usize,
216 pub state: Option<FullSuspendedState>,
218 pub current_cycles: Cycle,
220 pub limit_cycles: Cycle,
222}
223
224impl TransactionState {
225 pub fn new(
227 state: Option<FullSuspendedState>,
228 current: usize,
229 current_cycles: Cycle,
230 limit_cycles: Cycle,
231 ) -> Self {
232 TransactionState {
233 current,
234 state,
235 current_cycles,
236 limit_cycles,
237 }
238 }
239
240 pub fn next_limit_cycles(&self, step_cycles: Cycle, max_cycles: Cycle) -> (Cycle, bool) {
242 let remain = max_cycles - self.current_cycles;
243 let next_limit = self.limit_cycles + step_cycles;
244
245 if next_limit < remain {
246 (next_limit, false)
247 } else {
248 (remain, true)
249 }
250 }
251}
252
253#[allow(clippy::large_enum_variant)]
255#[derive(Debug)]
256pub enum VerifyResult {
257 Completed(Cycle),
259 Suspended(TransactionState),
261}
262
263impl std::fmt::Debug for TransactionState {
264 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> std::fmt::Result {
265 f.debug_struct("TransactionState")
266 .field("current", &self.current)
267 .field("current_cycles", &self.current_cycles)
268 .field("limit_cycles", &self.limit_cycles)
269 .finish()
270 }
271}
272
273#[derive(Eq, PartialEq, Clone, Debug)]
275pub enum ChunkCommand {
276 Suspend,
278 Resume,
280 Stop,
282}
283
284pub type VmId = u64;
285pub const FIRST_VM_ID: VmId = 0;
286
287#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
288pub struct Fd(pub(crate) u64);
289
290pub const FIRST_FD_SLOT: u64 = 2;
291
292impl Fd {
293 pub fn create(slot: u64) -> (Fd, Fd, u64) {
294 (Fd(slot), Fd(slot + 1), slot + 2)
295 }
296
297 pub fn other_fd(&self) -> Fd {
298 Fd(self.0 ^ 0x1)
299 }
300
301 pub fn is_read(&self) -> bool {
302 self.0 % 2 == 0
303 }
304
305 pub fn is_write(&self) -> bool {
306 self.0 % 2 == 1
307 }
308}
309
310#[derive(Clone, Debug, PartialEq, Eq, Hash)]
312pub struct ReadState {
313 pub fd: Fd,
314 pub length: u64,
315 pub buffer_addr: u64,
316 pub length_addr: u64,
317}
318
319#[derive(Clone, Debug, PartialEq, Eq, Hash)]
321pub struct WriteState {
322 pub fd: Fd,
323 pub consumed: u64,
324 pub length: u64,
325 pub buffer_addr: u64,
326 pub length_addr: u64,
327}
328
329#[derive(Clone, Debug, PartialEq, Eq, Hash)]
331pub enum VmState {
332 Runnable,
334 Terminated,
336 Wait {
338 target_vm_id: VmId,
340 exit_code_addr: u64,
342 },
343 WaitForWrite(WriteState),
345 WaitForRead(ReadState),
347}
348
349#[derive(Clone, Debug)]
351pub struct DataLocation {
352 pub data_piece_id: DataPieceId,
354 pub offset: u64,
356 pub length: u64,
358}
359
360#[derive(Clone, Debug)]
361pub struct ExecV2Args {
362 pub location: DataLocation,
363 pub argc: u64,
364 pub argv: u64,
365}
366
367#[derive(Clone, Debug)]
368pub struct SpawnArgs {
369 pub location: DataLocation,
370 pub argc: u64,
371 pub argv: u64,
372 pub fds: Vec<Fd>,
373 pub process_id_addr: u64,
374}
375
376#[derive(Clone, Debug)]
377pub struct WaitArgs {
378 pub target_id: VmId,
379 pub exit_code_addr: u64,
380}
381
382#[derive(Clone, Debug)]
383pub struct PipeArgs {
384 pub fd1_addr: u64,
385 pub fd2_addr: u64,
386}
387
388#[derive(Clone, Debug)]
389pub struct FdArgs {
390 pub fd: Fd,
391 pub length: u64,
392 pub buffer_addr: u64,
393 pub length_addr: u64,
394}
395
396#[derive(Clone, Debug)]
397pub enum Message {
398 ExecV2(VmId, ExecV2Args),
399 Spawn(VmId, SpawnArgs),
400 Wait(VmId, WaitArgs),
401 Pipe(VmId, PipeArgs),
402 FdRead(VmId, FdArgs),
403 FdWrite(VmId, FdArgs),
404 InheritedFileDescriptor(VmId, FdArgs),
405 Close(VmId, Fd),
406}
407
408#[derive(Clone, Debug, PartialEq, Eq, Hash)]
410pub enum DataPieceId {
411 Input(u32),
413 Output(u32),
415 CellDep(u32),
417 GroupInput(u32),
419 GroupOutput(u32),
421 Witness(u32),
423 WitnessGroupInput(u32),
425 WitnessGroupOutput(u32),
427}
428
429impl TryFrom<(u64, u64, u64)> for DataPieceId {
430 type Error = String;
431
432 fn try_from(value: (u64, u64, u64)) -> Result<Self, Self::Error> {
433 let (source, index, place) = value;
434 let index: u32 =
435 u32::try_from(index).map_err(|e| format!("Error casting index to u32: {}", e))?;
436 match (source, place) {
437 (1, 0) => Ok(DataPieceId::Input(index)),
438 (2, 0) => Ok(DataPieceId::Output(index)),
439 (3, 0) => Ok(DataPieceId::CellDep(index)),
440 (0x0100000000000001, 0) => Ok(DataPieceId::GroupInput(index)),
441 (0x0100000000000002, 0) => Ok(DataPieceId::GroupOutput(index)),
442 (1, 1) => Ok(DataPieceId::Witness(index)),
443 (2, 1) => Ok(DataPieceId::Witness(index)),
444 (0x0100000000000001, 1) => Ok(DataPieceId::WitnessGroupInput(index)),
445 (0x0100000000000002, 1) => Ok(DataPieceId::WitnessGroupOutput(index)),
446 _ => Err(format!("Invalid source value: {:#x}", source)),
447 }
448 }
449}
450
451#[derive(Clone, Debug)]
455pub struct FullSuspendedState {
456 pub total_cycles: Cycle,
457 pub next_vm_id: VmId,
458 pub next_fd_slot: u64,
459 pub vms: Vec<(VmId, VmState, Snapshot2<DataPieceId>)>,
460 pub fds: Vec<(Fd, VmId)>,
461 pub inherited_fd: Vec<(VmId, Vec<Fd>)>,
462 pub terminated_vms: Vec<(VmId, i8)>,
463 pub instantiated_ids: Vec<VmId>,
464}
465
466impl FullSuspendedState {
467 pub fn size(&self) -> u64 {
468 (size_of::<Cycle>()
469 + size_of::<VmId>()
470 + size_of::<u64>()
471 + self.vms.iter().fold(0, |mut acc, (_, _, snapshot)| {
472 acc += size_of::<VmId>() + size_of::<VmState>();
473 acc += snapshot.pages_from_source.len()
474 * (size_of::<u64>()
475 + size_of::<u8>()
476 + size_of::<DataPieceId>()
477 + size_of::<u64>()
478 + size_of::<u64>());
479 for dirty_page in &snapshot.dirty_pages {
480 acc += size_of::<u64>() + size_of::<u8>() + dirty_page.2.len();
481 }
482 acc += size_of::<u32>()
483 + RISCV_GENERAL_REGISTER_NUMBER * size_of::<u64>()
484 + size_of::<u64>()
485 + size_of::<u64>()
486 + size_of::<u64>();
487 acc
488 })
489 + (self.fds.len() * (size_of::<Fd>() + size_of::<VmId>()))) as u64
490 + (self.inherited_fd.len() * (size_of::<Fd>())) as u64
491 + (self.terminated_vms.len() * (size_of::<VmId>() + size_of::<i8>())) as u64
492 + (self.instantiated_ids.len() * size_of::<VmId>()) as u64
493 }
494}
495
496#[derive(Debug, PartialEq, Eq, Clone)]
497pub enum DataGuard {
498 NotLoaded(OutPoint),
499 Loaded(Bytes),
500}
501
502#[derive(Debug, Clone)]
504pub struct LazyData(Arc<RwLock<DataGuard>>);
505
506impl LazyData {
507 fn from_cell_meta(cell_meta: &CellMeta) -> LazyData {
508 match &cell_meta.mem_cell_data {
509 Some(data) => LazyData(Arc::new(RwLock::new(DataGuard::Loaded(data.to_owned())))),
510 None => LazyData(Arc::new(RwLock::new(DataGuard::NotLoaded(
511 cell_meta.out_point.clone(),
512 )))),
513 }
514 }
515
516 fn access<DL: CellDataProvider>(&self, data_loader: &DL) -> Result<Bytes, ScriptError> {
517 let guard = self
518 .0
519 .read()
520 .map_err(|_| ScriptError::Other("RwLock poisoned".into()))?
521 .to_owned();
522 match guard {
523 DataGuard::NotLoaded(out_point) => {
524 let data = data_loader
525 .get_cell_data(&out_point)
526 .ok_or(ScriptError::Other("cell data not found".into()))?;
527 let mut write_guard = self
528 .0
529 .write()
530 .map_err(|_| ScriptError::Other("RwLock poisoned".into()))?;
531 *write_guard = DataGuard::Loaded(data.clone());
532 Ok(data)
533 }
534 DataGuard::Loaded(bytes) => Ok(bytes),
535 }
536 }
537}
538
539#[derive(Debug, Clone)]
540pub enum Binaries {
541 Unique(Byte32, usize, LazyData),
542 Duplicate(Byte32, usize, LazyData),
543 Multiple,
544}
545
546impl Binaries {
547 fn new(data_hash: Byte32, dep_index: usize, data: LazyData) -> Self {
548 Self::Unique(data_hash, dep_index, data)
549 }
550
551 fn merge(&mut self, data_hash: &Byte32) {
552 match self {
553 Self::Unique(ref hash, dep_index, data)
554 | Self::Duplicate(ref hash, dep_index, data) => {
555 if hash != data_hash {
556 *self = Self::Multiple;
557 } else {
558 *self = Self::Duplicate(hash.to_owned(), *dep_index, data.to_owned());
559 }
560 }
561 Self::Multiple => {}
562 }
563 }
564}
565
566#[derive(Clone, Debug)]
568pub struct TxData<DL> {
569 pub rtx: Arc<ResolvedTransaction>,
571
572 pub info: Arc<TxInfo<DL>>,
574}
575
576#[derive(Clone, Debug)]
579pub struct TxInfo<DL> {
580 pub data_loader: DL,
582 pub consensus: Arc<Consensus>,
584 pub tx_env: Arc<TxVerifyEnv>,
586
587 pub binaries_by_data_hash: HashMap<Byte32, (usize, LazyData)>,
589 pub binaries_by_type_hash: HashMap<Byte32, Binaries>,
591 pub lock_groups: BTreeMap<Byte32, ScriptGroup>,
593 pub type_groups: BTreeMap<Byte32, ScriptGroup>,
595 pub outputs: Vec<CellMeta>,
597}
598
599impl<DL> TxData<DL>
600where
601 DL: CellDataProvider,
602{
603 pub fn new(
605 rtx: Arc<ResolvedTransaction>,
606 data_loader: DL,
607 consensus: Arc<Consensus>,
608 tx_env: Arc<TxVerifyEnv>,
609 ) -> Self {
610 let tx_hash = rtx.transaction.hash();
611 let resolved_cell_deps = &rtx.resolved_cell_deps;
612 let resolved_inputs = &rtx.resolved_inputs;
613 let outputs = rtx
614 .transaction
615 .outputs_with_data_iter()
616 .enumerate()
617 .map(|(index, (cell_output, data))| {
618 let out_point = OutPoint::new_builder()
619 .tx_hash(tx_hash.clone())
620 .index(index.pack())
621 .build();
622 let data_hash = CellOutput::calc_data_hash(&data);
623 CellMeta {
624 cell_output,
625 out_point,
626 transaction_info: None,
627 data_bytes: data.len() as u64,
628 mem_cell_data: Some(data),
629 mem_cell_data_hash: Some(data_hash),
630 }
631 })
632 .collect();
633
634 let mut binaries_by_data_hash: HashMap<Byte32, (usize, LazyData)> = HashMap::default();
635 let mut binaries_by_type_hash: HashMap<Byte32, Binaries> = HashMap::default();
636 for (i, cell_meta) in resolved_cell_deps.iter().enumerate() {
637 let data_hash = data_loader
638 .load_cell_data_hash(cell_meta)
639 .expect("cell data hash");
640 let lazy = LazyData::from_cell_meta(cell_meta);
641 binaries_by_data_hash.insert(data_hash.to_owned(), (i, lazy.to_owned()));
642
643 if let Some(t) = &cell_meta.cell_output.type_().to_opt() {
644 binaries_by_type_hash
645 .entry(t.calc_script_hash())
646 .and_modify(|bin| bin.merge(&data_hash))
647 .or_insert_with(|| Binaries::new(data_hash.to_owned(), i, lazy.to_owned()));
648 }
649 }
650
651 let mut lock_groups = BTreeMap::default();
652 let mut type_groups = BTreeMap::default();
653 for (i, cell_meta) in resolved_inputs.iter().enumerate() {
654 let output = &cell_meta.cell_output;
657 let lock_group_entry = lock_groups
658 .entry(output.calc_lock_hash())
659 .or_insert_with(|| ScriptGroup::from_lock_script(&output.lock()));
660 lock_group_entry.input_indices.push(i);
661 if let Some(t) = &output.type_().to_opt() {
662 let type_group_entry = type_groups
663 .entry(t.calc_script_hash())
664 .or_insert_with(|| ScriptGroup::from_type_script(t));
665 type_group_entry.input_indices.push(i);
666 }
667 }
668 for (i, output) in rtx.transaction.outputs().into_iter().enumerate() {
669 if let Some(t) = &output.type_().to_opt() {
670 let type_group_entry = type_groups
671 .entry(t.calc_script_hash())
672 .or_insert_with(|| ScriptGroup::from_type_script(t));
673 type_group_entry.output_indices.push(i);
674 }
675 }
676
677 Self {
678 rtx,
679 info: Arc::new(TxInfo {
680 data_loader,
681 consensus,
682 tx_env,
683 binaries_by_data_hash,
684 binaries_by_type_hash,
685 lock_groups,
686 type_groups,
687 outputs,
688 }),
689 }
690 }
691
692 #[inline]
693 pub fn extract_script(&self, script: &Script) -> Result<Bytes, ScriptError> {
695 self.info.extract_script(script)
696 }
697}
698
699impl<DL> TxInfo<DL>
700where
701 DL: CellDataProvider,
702{
703 #[inline]
704 pub fn extract_script(&self, script: &Script) -> Result<Bytes, ScriptError> {
706 let (lazy, _) = self.extract_script_and_dep_index(script)?;
707 lazy.access(&self.data_loader)
708 }
709}
710
711impl<DL> TxData<DL> {
712 #[inline]
713 pub fn tx_hash(&self) -> Byte32 {
715 self.rtx.transaction.hash()
716 }
717
718 #[inline]
719 pub fn extract_referenced_dep_index(&self, script: &Script) -> Result<usize, ScriptError> {
721 self.info.extract_referenced_dep_index(script)
722 }
723
724 #[inline]
725 pub fn find_script_group(
727 &self,
728 script_group_type: ScriptGroupType,
729 script_hash: &Byte32,
730 ) -> Option<&ScriptGroup> {
731 self.info.find_script_group(script_group_type, script_hash)
732 }
733
734 #[inline]
735 pub fn select_version(&self, script: &Script) -> Result<ScriptVersion, ScriptError> {
737 self.info.select_version(script)
738 }
739
740 #[inline]
741 pub fn groups(&self) -> impl Iterator<Item = (&'_ Byte32, &'_ ScriptGroup)> {
743 self.info.groups()
744 }
745
746 #[inline]
747 pub fn groups_with_type(
749 &self,
750 ) -> impl Iterator<Item = (ScriptGroupType, &'_ Byte32, &'_ ScriptGroup)> {
751 self.info.groups_with_type()
752 }
753}
754
755impl<DL> TxInfo<DL> {
756 #[inline]
757 pub fn extract_referenced_dep_index(&self, script: &Script) -> Result<usize, ScriptError> {
759 let (_, dep_index) = self.extract_script_and_dep_index(script)?;
760 Ok(*dep_index)
761 }
762
763 fn extract_script_and_dep_index(
764 &self,
765 script: &Script,
766 ) -> Result<(&LazyData, &usize), ScriptError> {
767 let script_hash_type = ScriptHashType::try_from(script.hash_type())
768 .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?;
769 match script_hash_type {
770 ScriptHashType::Data | ScriptHashType::Data1 | ScriptHashType::Data2 => {
771 if let Some((dep_index, lazy)) = self.binaries_by_data_hash.get(&script.code_hash())
772 {
773 Ok((lazy, dep_index))
774 } else {
775 Err(ScriptError::ScriptNotFound(script.code_hash()))
776 }
777 }
778 ScriptHashType::Type => {
779 if let Some(ref bin) = self.binaries_by_type_hash.get(&script.code_hash()) {
780 match bin {
781 Binaries::Unique(_, dep_index, ref lazy) => Ok((lazy, dep_index)),
782 Binaries::Duplicate(_, dep_index, ref lazy) => Ok((lazy, dep_index)),
783 Binaries::Multiple => Err(ScriptError::MultipleMatches),
784 }
785 } else {
786 Err(ScriptError::ScriptNotFound(script.code_hash()))
787 }
788 }
789 }
790 }
791
792 pub fn find_script_group(
794 &self,
795 script_group_type: ScriptGroupType,
796 script_hash: &Byte32,
797 ) -> Option<&ScriptGroup> {
798 match script_group_type {
799 ScriptGroupType::Lock => self.lock_groups.get(script_hash),
800 ScriptGroupType::Type => self.type_groups.get(script_hash),
801 }
802 }
803
804 fn is_vm_version_1_and_syscalls_2_enabled(&self) -> bool {
805 let epoch_number = self.tx_env.epoch_number_without_proposal_window();
810 let hardfork_switch = self.consensus.hardfork_switch();
811 hardfork_switch
812 .ckb2021
813 .is_vm_version_1_and_syscalls_2_enabled(epoch_number)
814 }
815
816 fn is_vm_version_2_and_syscalls_3_enabled(&self) -> bool {
817 let epoch_number = self.tx_env.epoch_number_without_proposal_window();
822 let hardfork_switch = self.consensus.hardfork_switch();
823 hardfork_switch
824 .ckb2023
825 .is_vm_version_2_and_syscalls_3_enabled(epoch_number)
826 }
827
828 pub fn select_version(&self, script: &Script) -> Result<ScriptVersion, ScriptError> {
830 let is_vm_version_2_and_syscalls_3_enabled = self.is_vm_version_2_and_syscalls_3_enabled();
831 let is_vm_version_1_and_syscalls_2_enabled = self.is_vm_version_1_and_syscalls_2_enabled();
832 let script_hash_type = ScriptHashType::try_from(script.hash_type())
833 .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?;
834 match script_hash_type {
835 ScriptHashType::Data => Ok(ScriptVersion::V0),
836 ScriptHashType::Data1 => {
837 if is_vm_version_1_and_syscalls_2_enabled {
838 Ok(ScriptVersion::V1)
839 } else {
840 Err(ScriptError::InvalidVmVersion(1))
841 }
842 }
843 ScriptHashType::Data2 => {
844 if is_vm_version_2_and_syscalls_3_enabled {
845 Ok(ScriptVersion::V2)
846 } else {
847 Err(ScriptError::InvalidVmVersion(2))
848 }
849 }
850 ScriptHashType::Type => {
851 if is_vm_version_2_and_syscalls_3_enabled {
852 Ok(ScriptVersion::V2)
853 } else if is_vm_version_1_and_syscalls_2_enabled {
854 Ok(ScriptVersion::V1)
855 } else {
856 Ok(ScriptVersion::V0)
857 }
858 }
859 }
860 }
861
862 pub fn groups(&self) -> impl Iterator<Item = (&'_ Byte32, &'_ ScriptGroup)> {
864 self.lock_groups.iter().chain(self.type_groups.iter())
865 }
866
867 pub fn groups_with_type(
869 &self,
870 ) -> impl Iterator<Item = (ScriptGroupType, &'_ Byte32, &'_ ScriptGroup)> {
871 self.lock_groups
872 .iter()
873 .map(|(hash, group)| (ScriptGroupType::Lock, hash, group))
874 .chain(
875 self.type_groups
876 .iter()
877 .map(|(hash, group)| (ScriptGroupType::Type, hash, group)),
878 )
879 }
880}
881
882#[derive(Clone, Debug)]
884pub struct SgData<DL> {
885 pub rtx: Arc<ResolvedTransaction>,
887
888 pub tx_info: Arc<TxInfo<DL>>,
890
891 pub sg_info: Arc<SgInfo>,
893}
894
895#[derive(Clone, Debug)]
897pub struct SgInfo {
898 pub script_version: ScriptVersion,
900 pub script_group: ScriptGroup,
902 pub script_hash: Byte32,
904 pub program_data_piece_id: DataPieceId,
906}
907
908impl<DL> SgData<DL> {
909 pub fn new(tx_data: &TxData<DL>, script_group: &ScriptGroup) -> Result<Self, ScriptError> {
910 let script_hash = script_group.script.calc_script_hash();
911 let script_version = tx_data.select_version(&script_group.script)?;
912 let dep_index = tx_data
913 .extract_referenced_dep_index(&script_group.script)?
914 .try_into()
915 .map_err(|_| ScriptError::Other("u32 overflow".to_string()))?;
916 Ok(Self {
917 rtx: Arc::clone(&tx_data.rtx),
918 tx_info: Arc::clone(&tx_data.info),
919 sg_info: Arc::new(SgInfo {
920 script_version,
921 script_hash,
922 script_group: script_group.clone(),
923 program_data_piece_id: DataPieceId::CellDep(dep_index),
924 }),
925 })
926 }
927
928 pub fn data_loader(&self) -> &DL {
929 &self.tx_info.data_loader
930 }
931
932 pub fn group_inputs(&self) -> &[usize] {
933 &self.sg_info.script_group.input_indices
934 }
935
936 pub fn group_outputs(&self) -> &[usize] {
937 &self.sg_info.script_group.output_indices
938 }
939
940 pub fn outputs(&self) -> &[CellMeta] {
941 &self.tx_info.outputs
942 }
943}
944
945impl<DL> DataSource<DataPieceId> for SgData<DL>
946where
947 DL: CellDataProvider,
948{
949 fn load_data(&self, id: &DataPieceId, offset: u64, length: u64) -> Option<(Bytes, u64)> {
950 match id {
951 DataPieceId::Input(i) => self
952 .rtx
953 .resolved_inputs
954 .get(*i as usize)
955 .and_then(|cell| self.data_loader().load_cell_data(cell)),
956 DataPieceId::Output(i) => self
957 .rtx
958 .transaction
959 .outputs_data()
960 .get(*i as usize)
961 .map(|data| data.raw_data()),
962 DataPieceId::CellDep(i) => self
963 .rtx
964 .resolved_cell_deps
965 .get(*i as usize)
966 .and_then(|cell| self.data_loader().load_cell_data(cell)),
967 DataPieceId::GroupInput(i) => self
968 .sg_info
969 .script_group
970 .input_indices
971 .get(*i as usize)
972 .and_then(|gi| self.rtx.resolved_inputs.get(*gi))
973 .and_then(|cell| self.data_loader().load_cell_data(cell)),
974 DataPieceId::GroupOutput(i) => self
975 .sg_info
976 .script_group
977 .output_indices
978 .get(*i as usize)
979 .and_then(|gi| self.rtx.transaction.outputs_data().get(*gi))
980 .map(|data| data.raw_data()),
981 DataPieceId::Witness(i) => self
982 .rtx
983 .transaction
984 .witnesses()
985 .get(*i as usize)
986 .map(|data| data.raw_data()),
987 DataPieceId::WitnessGroupInput(i) => self
988 .sg_info
989 .script_group
990 .input_indices
991 .get(*i as usize)
992 .and_then(|gi| self.rtx.transaction.witnesses().get(*gi))
993 .map(|data| data.raw_data()),
994 DataPieceId::WitnessGroupOutput(i) => self
995 .sg_info
996 .script_group
997 .output_indices
998 .get(*i as usize)
999 .and_then(|gi| self.rtx.transaction.witnesses().get(*gi))
1000 .map(|data| data.raw_data()),
1001 }
1002 .map(|data| {
1003 let offset = std::cmp::min(offset as usize, data.len());
1004 let full_length = data.len() - offset;
1005 let real_length = if length > 0 {
1006 std::cmp::min(full_length, length as usize)
1007 } else {
1008 full_length
1009 };
1010 (data.slice(offset..offset + real_length), full_length as u64)
1011 })
1012 }
1013}
1014
1015#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1018pub enum VmArgs {
1019 Reader {
1021 vm_id: u64,
1023 argc: u64,
1025 argv: u64,
1027 },
1028 Vector(Vec<Bytes>),
1030}
1031
1032#[derive(Clone)]
1034pub struct VmContext<DL>
1035where
1036 DL: CellDataProvider,
1037{
1038 pub(crate) base_cycles: Arc<AtomicU64>,
1039 pub(crate) message_box: Arc<Mutex<Vec<Message>>>,
1041 pub(crate) snapshot2_context: Arc<Mutex<Snapshot2Context<DataPieceId, SgData<DL>>>>,
1042}
1043
1044impl<DL> VmContext<DL>
1045where
1046 DL: CellDataProvider + Clone,
1047{
1048 pub fn new(sg_data: &SgData<DL>, message_box: &Arc<Mutex<Vec<Message>>>) -> Self {
1052 Self {
1053 base_cycles: Arc::new(AtomicU64::new(0)),
1054 message_box: Arc::clone(message_box),
1055 snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(sg_data.clone()))),
1056 }
1057 }
1058
1059 pub fn set_base_cycles(&mut self, base_cycles: u64) {
1060 self.base_cycles.store(base_cycles, Ordering::Release);
1061 }
1062}
1063
1064#[derive(Clone)]
1066pub enum RunMode {
1067 LimitCycles(Cycle),
1069 Pause(Pause),
1071}