1use crate::{error::ScriptError, verify_env::TxVerifyEnv};
4use ckb_chain_spec::consensus::Consensus;
5use ckb_types::{
6 core::{
7 Cycle, ScriptHashType,
8 cell::{CellMeta, ResolvedTransaction},
9 },
10 packed::{Byte32, CellOutput, OutPoint, Script},
11 prelude::*,
12};
13use ckb_vm::{
14 ISA_B, ISA_IMC, ISA_MOP, Syscalls,
15 machine::{VERSION0, VERSION1, VERSION2},
16};
17use serde::{Deserialize, Serialize};
18use std::collections::{BTreeMap, HashMap};
19use std::fmt;
20use std::sync::{
21 Arc, Mutex, RwLock,
22 atomic::{AtomicU64, Ordering},
23};
24
25use ckb_traits::CellDataProvider;
26use ckb_vm::snapshot2::Snapshot2Context;
27
28use ckb_vm::{
29 DefaultMachineRunner, RISCV_GENERAL_REGISTER_NUMBER, SupportMachine,
30 bytes::Bytes,
31 machine::Pause,
32 snapshot2::{DataSource, Snapshot2},
33};
34use std::mem::size_of;
35
36pub type VmIsa = u8;
38pub type VmVersion = u32;
40
41#[cfg(has_asm)]
45pub type Machine = ckb_vm::machine::asm::AsmMachine;
46#[cfg(all(not(has_asm), not(feature = "flatmemory")))]
48pub type Machine = ckb_vm::TraceMachine<
49 ckb_vm::DefaultCoreMachine<u64, ckb_vm::WXorXMemory<ckb_vm::SparseMemory<u64>>>,
50>;
51#[cfg(all(not(has_asm), feature = "flatmemory"))]
53pub type Machine = ckb_vm::TraceMachine<
54 ckb_vm::DefaultCoreMachine<u64, ckb_vm::XorXMemory<ckb_vm::FlatMemory<u64>>>,
55>;
56
57pub type DebugPrinter = Arc<dyn Fn(&Byte32, &str) + Send + Sync>;
59pub type SyscallGenerator<DL, V, M> =
61 fn(&VmId, &SgData<DL>, &VmContext<DL>, &V) -> Vec<Box<(dyn Syscalls<M>)>>;
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
65pub enum ScriptVersion {
66 V0 = 0,
68 V1 = 1,
70 V2 = 2,
72}
73
74impl ScriptVersion {
75 pub const fn latest() -> Self {
77 Self::V2
78 }
79
80 pub fn vm_isa(self) -> VmIsa {
82 match self {
83 Self::V0 => ISA_IMC,
84 Self::V1 => ISA_IMC | ISA_B | ISA_MOP,
85 Self::V2 => ISA_IMC | ISA_B | ISA_MOP,
86 }
87 }
88
89 pub fn vm_version(self) -> VmVersion {
91 match self {
92 Self::V0 => VERSION0,
93 Self::V1 => VERSION1,
94 Self::V2 => VERSION2,
95 }
96 }
97
98 pub fn data_hash_type(self) -> ScriptHashType {
104 match self {
105 Self::V0 => ScriptHashType::Data,
106 Self::V1 => ScriptHashType::Data1,
107 Self::V2 => ScriptHashType::Data2,
108 }
109 }
110
111 pub fn init_core_machine_without_limit(self) -> <Machine as DefaultMachineRunner>::Inner {
115 self.init_core_machine(u64::MAX)
116 }
117
118 pub fn init_core_machine(self, max_cycles: Cycle) -> <Machine as DefaultMachineRunner>::Inner {
120 let isa = self.vm_isa();
121 let version = self.vm_version();
122 <<Machine as DefaultMachineRunner>::Inner as SupportMachine>::new(isa, version, max_cycles)
123 }
124}
125
126#[derive(Clone, Debug)]
132pub struct ScriptGroup {
133 pub script: Script,
137 pub group_type: ScriptGroupType,
139 pub input_indices: Vec<usize>,
141 pub output_indices: Vec<usize>,
143}
144
145impl ScriptGroup {
156 pub(crate) fn new(script: &Script, group_type: ScriptGroupType) -> Self {
158 Self {
159 group_type,
160 script: script.to_owned(),
161 input_indices: vec![],
162 output_indices: vec![],
163 }
164 }
165
166 pub(crate) fn from_lock_script(script: &Script) -> Self {
168 Self::new(script, ScriptGroupType::Lock)
169 }
170
171 pub(crate) fn from_type_script(script: &Script) -> Self {
173 Self::new(script, ScriptGroupType::Type)
174 }
175}
176
177#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
182#[serde(rename_all = "snake_case")]
183pub enum ScriptGroupType {
184 Lock,
186 Type,
188}
189
190impl fmt::Display for ScriptGroupType {
191 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192 match self {
193 ScriptGroupType::Lock => write!(f, "Lock"),
194 ScriptGroupType::Type => write!(f, "Type"),
195 }
196 }
197}
198
199#[derive(Clone)]
202pub struct TransactionState {
203 pub current: usize,
205 pub state: Option<FullSuspendedState>,
207 pub current_cycles: Cycle,
209 pub limit_cycles: Cycle,
211}
212
213impl TransactionState {
214 pub fn new(
216 state: Option<FullSuspendedState>,
217 current: usize,
218 current_cycles: Cycle,
219 limit_cycles: Cycle,
220 ) -> Self {
221 TransactionState {
222 current,
223 state,
224 current_cycles,
225 limit_cycles,
226 }
227 }
228
229 pub fn next_limit_cycles(&self, step_cycles: Cycle, max_cycles: Cycle) -> (Cycle, bool) {
231 let remain = max_cycles - self.current_cycles;
232 let next_limit = self.limit_cycles + step_cycles;
233
234 if next_limit < remain {
235 (next_limit, false)
236 } else {
237 (remain, true)
238 }
239 }
240}
241
242#[allow(clippy::large_enum_variant)]
244#[derive(Debug)]
245pub enum VerifyResult {
246 Completed(Cycle),
248 Suspended(TransactionState),
250}
251
252impl std::fmt::Debug for TransactionState {
253 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> std::fmt::Result {
254 f.debug_struct("TransactionState")
255 .field("current", &self.current)
256 .field("current_cycles", &self.current_cycles)
257 .field("limit_cycles", &self.limit_cycles)
258 .finish()
259 }
260}
261
262#[derive(Eq, PartialEq, Clone, Debug)]
264pub enum ChunkCommand {
265 Suspend,
267 Resume,
269 Stop,
271}
272
273pub type VmId = u64;
275pub const FIRST_VM_ID: VmId = 0;
277
278#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
280pub struct Fd(pub u64);
281
282pub const FIRST_FD_SLOT: u64 = 2;
284
285impl Fd {
286 pub fn create(slot: u64) -> (Fd, Fd, u64) {
288 (Fd(slot), Fd(slot + 1), slot + 2)
289 }
290
291 pub fn other_fd(&self) -> Fd {
293 Fd(self.0 ^ 0x1)
294 }
295
296 pub fn is_read(&self) -> bool {
298 self.0 % 2 == 0
299 }
300
301 pub fn is_write(&self) -> bool {
303 self.0 % 2 == 1
304 }
305}
306
307#[derive(Clone, Debug, PartialEq, Eq, Hash)]
309pub struct ReadState {
310 pub fd: Fd,
312 pub length: u64,
314 pub buffer_addr: u64,
316 pub length_addr: u64,
318}
319
320#[derive(Clone, Debug, PartialEq, Eq, Hash)]
322pub struct WriteState {
323 pub fd: Fd,
325 pub consumed: u64,
327 pub length: u64,
329 pub buffer_addr: u64,
331 pub length_addr: u64,
333}
334
335#[derive(Clone, Debug, PartialEq, Eq, Hash)]
337pub enum VmState {
338 Runnable,
340 Terminated,
342 Wait {
344 target_vm_id: VmId,
346 exit_code_addr: u64,
348 },
349 WaitForWrite(WriteState),
351 WaitForRead(ReadState),
353}
354
355#[derive(Clone, Debug)]
357pub struct DataLocation {
358 pub data_piece_id: DataPieceId,
360 pub offset: u64,
362 pub length: u64,
364}
365
366#[derive(Clone, Debug)]
368pub struct ExecV2Args {
369 pub location: DataLocation,
371 pub argc: u64,
373 pub argv: u64,
375}
376
377#[derive(Clone, Debug)]
379pub struct SpawnArgs {
380 pub location: DataLocation,
382 pub argc: u64,
384 pub argv: u64,
386 pub fds: Vec<Fd>,
388 pub process_id_addr: u64,
390}
391
392#[derive(Clone, Debug)]
394pub struct WaitArgs {
395 pub target_id: VmId,
397 pub exit_code_addr: u64,
399}
400
401#[derive(Clone, Debug)]
403pub struct PipeArgs {
404 pub fd1_addr: u64,
406 pub fd2_addr: u64,
408}
409
410#[derive(Clone, Debug)]
412pub struct FdArgs {
413 pub fd: Fd,
415 pub length: u64,
418 pub buffer_addr: u64,
420 pub length_addr: u64,
424}
425
426#[derive(Clone, Debug)]
429pub enum Message {
430 ExecV2(VmId, ExecV2Args),
432 Spawn(VmId, SpawnArgs),
434 Wait(VmId, WaitArgs),
436 Pipe(VmId, PipeArgs),
438 FdRead(VmId, FdArgs),
440 FdWrite(VmId, FdArgs),
442 InheritedFileDescriptor(VmId, FdArgs),
444 Close(VmId, Fd),
446}
447
448#[derive(Clone, Debug, PartialEq, Eq, Hash)]
450pub enum DataPieceId {
451 Input(u32),
453 Output(u32),
455 CellDep(u32),
457 GroupInput(u32),
459 GroupOutput(u32),
461 Witness(u32),
463 WitnessGroupInput(u32),
465 WitnessGroupOutput(u32),
467}
468
469impl TryFrom<(u64, u64, u64)> for DataPieceId {
470 type Error = String;
471
472 fn try_from(value: (u64, u64, u64)) -> Result<Self, Self::Error> {
473 let (source, index, place) = value;
474 let index: u32 =
475 u32::try_from(index).map_err(|e| format!("Error casting index to u32: {}", e))?;
476 match (source, place) {
477 (1, 0) => Ok(DataPieceId::Input(index)),
478 (2, 0) => Ok(DataPieceId::Output(index)),
479 (3, 0) => Ok(DataPieceId::CellDep(index)),
480 (0x0100000000000001, 0) => Ok(DataPieceId::GroupInput(index)),
481 (0x0100000000000002, 0) => Ok(DataPieceId::GroupOutput(index)),
482 (1, 1) => Ok(DataPieceId::Witness(index)),
483 (2, 1) => Ok(DataPieceId::Witness(index)),
484 (0x0100000000000001, 1) => Ok(DataPieceId::WitnessGroupInput(index)),
485 (0x0100000000000002, 1) => Ok(DataPieceId::WitnessGroupOutput(index)),
486 _ => Err(format!("Invalid source value: {:#x}", source)),
487 }
488 }
489}
490
491#[derive(Clone, Debug)]
495pub struct FullSuspendedState {
496 pub total_cycles: Cycle,
498 pub next_vm_id: VmId,
500 pub next_fd_slot: u64,
502 pub vms: Vec<(VmId, VmState, Snapshot2<DataPieceId>)>,
504 pub fds: Vec<(Fd, VmId)>,
506 pub inherited_fd: Vec<(VmId, Vec<Fd>)>,
508 pub terminated_vms: Vec<(VmId, i8)>,
510 pub instantiated_ids: Vec<VmId>,
513}
514
515impl FullSuspendedState {
516 pub fn size(&self) -> u64 {
519 (size_of::<Cycle>()
520 + size_of::<VmId>()
521 + size_of::<u64>()
522 + self.vms.iter().fold(0, |mut acc, (_, _, snapshot)| {
523 acc += size_of::<VmId>() + size_of::<VmState>();
524 acc += snapshot.pages_from_source.len()
525 * (size_of::<u64>()
526 + size_of::<u8>()
527 + size_of::<DataPieceId>()
528 + size_of::<u64>()
529 + size_of::<u64>());
530 for dirty_page in &snapshot.dirty_pages {
531 acc += size_of::<u64>() + size_of::<u8>() + dirty_page.2.len();
532 }
533 acc += size_of::<u32>()
534 + RISCV_GENERAL_REGISTER_NUMBER * size_of::<u64>()
535 + size_of::<u64>()
536 + size_of::<u64>()
537 + size_of::<u64>();
538 acc
539 })
540 + (self.fds.len() * (size_of::<Fd>() + size_of::<VmId>()))) as u64
541 + (self.inherited_fd.len() * (size_of::<Fd>())) as u64
542 + (self.terminated_vms.len() * (size_of::<VmId>() + size_of::<i8>())) as u64
543 + (self.instantiated_ids.len() * size_of::<VmId>()) as u64
544 }
545}
546
547#[derive(Debug, PartialEq, Eq, Clone)]
549pub enum DataGuard {
550 NotLoaded(OutPoint),
552 Loaded(Bytes),
554}
555
556#[derive(Debug, Clone)]
558pub struct LazyData(Arc<RwLock<DataGuard>>);
559
560impl LazyData {
561 fn from_cell_meta(cell_meta: &CellMeta) -> LazyData {
562 match &cell_meta.mem_cell_data {
563 Some(data) => LazyData(Arc::new(RwLock::new(DataGuard::Loaded(data.to_owned())))),
564 None => LazyData(Arc::new(RwLock::new(DataGuard::NotLoaded(
565 cell_meta.out_point.clone(),
566 )))),
567 }
568 }
569
570 fn access<DL: CellDataProvider>(&self, data_loader: &DL) -> Result<Bytes, ScriptError> {
571 let guard = self
572 .0
573 .read()
574 .map_err(|_| ScriptError::Other("RwLock poisoned".into()))?
575 .to_owned();
576 match guard {
577 DataGuard::NotLoaded(out_point) => {
578 let data = data_loader
579 .get_cell_data(&out_point)
580 .ok_or(ScriptError::Other("cell data not found".into()))?;
581 let mut write_guard = self
582 .0
583 .write()
584 .map_err(|_| ScriptError::Other("RwLock poisoned".into()))?;
585 *write_guard = DataGuard::Loaded(data.clone());
586 Ok(data)
587 }
588 DataGuard::Loaded(bytes) => Ok(bytes),
589 }
590 }
591}
592
593#[derive(Debug, Clone)]
595pub enum Binaries {
596 Unique(Byte32, usize, LazyData),
598 Duplicate(Byte32, usize, LazyData),
602 Multiple,
605}
606
607impl Binaries {
608 fn new(data_hash: Byte32, dep_index: usize, data: LazyData) -> Self {
609 Self::Unique(data_hash, dep_index, data)
610 }
611
612 fn merge(&mut self, data_hash: &Byte32) {
613 match self {
614 Self::Unique(hash, dep_index, data) | Self::Duplicate(hash, dep_index, data) => {
615 if hash != data_hash {
616 *self = Self::Multiple;
617 } else {
618 *self = Self::Duplicate(hash.to_owned(), *dep_index, data.to_owned());
619 }
620 }
621 Self::Multiple => {}
622 }
623 }
624}
625
626#[derive(Clone, Debug)]
628pub struct TxData<DL> {
629 pub rtx: Arc<ResolvedTransaction>,
631
632 pub info: Arc<TxInfo<DL>>,
634}
635
636#[derive(Clone, Debug)]
639pub struct TxInfo<DL> {
640 pub data_loader: DL,
642 pub consensus: Arc<Consensus>,
644 pub tx_env: Arc<TxVerifyEnv>,
646
647 pub binaries_by_data_hash: HashMap<Byte32, (usize, LazyData)>,
649 pub binaries_by_type_hash: HashMap<Byte32, Binaries>,
651 pub lock_groups: BTreeMap<Byte32, ScriptGroup>,
653 pub type_groups: BTreeMap<Byte32, ScriptGroup>,
655 pub outputs: Vec<CellMeta>,
657}
658
659impl<DL> TxData<DL>
660where
661 DL: CellDataProvider,
662{
663 pub fn new(
665 rtx: Arc<ResolvedTransaction>,
666 data_loader: DL,
667 consensus: Arc<Consensus>,
668 tx_env: Arc<TxVerifyEnv>,
669 ) -> Self {
670 let tx_hash = rtx.transaction.hash();
671 let resolved_cell_deps = &rtx.resolved_cell_deps;
672 let resolved_inputs = &rtx.resolved_inputs;
673 let outputs = rtx
674 .transaction
675 .outputs_with_data_iter()
676 .enumerate()
677 .map(|(index, (cell_output, data))| {
678 let out_point = OutPoint::new_builder()
679 .tx_hash(tx_hash.clone())
680 .index(index.pack())
681 .build();
682 let data_hash = CellOutput::calc_data_hash(&data);
683 CellMeta {
684 cell_output,
685 out_point,
686 transaction_info: None,
687 data_bytes: data.len() as u64,
688 mem_cell_data: Some(data),
689 mem_cell_data_hash: Some(data_hash),
690 }
691 })
692 .collect();
693
694 let mut binaries_by_data_hash: HashMap<Byte32, (usize, LazyData)> = HashMap::default();
695 let mut binaries_by_type_hash: HashMap<Byte32, Binaries> = HashMap::default();
696 for (i, cell_meta) in resolved_cell_deps.iter().enumerate() {
697 let data_hash = data_loader
698 .load_cell_data_hash(cell_meta)
699 .expect("cell data hash");
700 let lazy = LazyData::from_cell_meta(cell_meta);
701 binaries_by_data_hash.insert(data_hash.to_owned(), (i, lazy.to_owned()));
702
703 if let Some(t) = &cell_meta.cell_output.type_().to_opt() {
704 binaries_by_type_hash
705 .entry(t.calc_script_hash())
706 .and_modify(|bin| bin.merge(&data_hash))
707 .or_insert_with(|| Binaries::new(data_hash.to_owned(), i, lazy.to_owned()));
708 }
709 }
710
711 let mut lock_groups = BTreeMap::default();
712 let mut type_groups = BTreeMap::default();
713 for (i, cell_meta) in resolved_inputs.iter().enumerate() {
714 let output = &cell_meta.cell_output;
717 let lock_group_entry = lock_groups
718 .entry(output.calc_lock_hash())
719 .or_insert_with(|| ScriptGroup::from_lock_script(&output.lock()));
720 lock_group_entry.input_indices.push(i);
721 if let Some(t) = &output.type_().to_opt() {
722 let type_group_entry = type_groups
723 .entry(t.calc_script_hash())
724 .or_insert_with(|| ScriptGroup::from_type_script(t));
725 type_group_entry.input_indices.push(i);
726 }
727 }
728 for (i, output) in rtx.transaction.outputs().into_iter().enumerate() {
729 if let Some(t) = &output.type_().to_opt() {
730 let type_group_entry = type_groups
731 .entry(t.calc_script_hash())
732 .or_insert_with(|| ScriptGroup::from_type_script(t));
733 type_group_entry.output_indices.push(i);
734 }
735 }
736
737 Self {
738 rtx,
739 info: Arc::new(TxInfo {
740 data_loader,
741 consensus,
742 tx_env,
743 binaries_by_data_hash,
744 binaries_by_type_hash,
745 lock_groups,
746 type_groups,
747 outputs,
748 }),
749 }
750 }
751
752 #[inline]
753 pub fn extract_script(&self, script: &Script) -> Result<Bytes, ScriptError> {
755 self.info.extract_script(script)
756 }
757}
758
759impl<DL> TxInfo<DL>
760where
761 DL: CellDataProvider,
762{
763 #[inline]
764 pub fn extract_script(&self, script: &Script) -> Result<Bytes, ScriptError> {
766 let (lazy, _) = self.extract_script_and_dep_index(script)?;
767 lazy.access(&self.data_loader)
768 }
769}
770
771impl<DL> TxData<DL> {
772 #[inline]
773 pub fn tx_hash(&self) -> Byte32 {
775 self.rtx.transaction.hash()
776 }
777
778 #[inline]
779 pub fn extract_referenced_dep_index(&self, script: &Script) -> Result<usize, ScriptError> {
781 self.info.extract_referenced_dep_index(script)
782 }
783
784 #[inline]
785 pub fn find_script_group(
787 &self,
788 script_group_type: ScriptGroupType,
789 script_hash: &Byte32,
790 ) -> Option<&ScriptGroup> {
791 self.info.find_script_group(script_group_type, script_hash)
792 }
793
794 #[inline]
795 pub fn select_version(&self, script: &Script) -> Result<ScriptVersion, ScriptError> {
797 self.info.select_version(script)
798 }
799
800 #[inline]
801 pub fn groups(&self) -> impl Iterator<Item = (&'_ Byte32, &'_ ScriptGroup)> {
803 self.info.groups()
804 }
805
806 #[inline]
807 pub fn groups_with_type(
809 &self,
810 ) -> impl Iterator<Item = (ScriptGroupType, &'_ Byte32, &'_ ScriptGroup)> {
811 self.info.groups_with_type()
812 }
813}
814
815impl<DL> TxInfo<DL> {
816 #[inline]
817 pub fn extract_referenced_dep_index(&self, script: &Script) -> Result<usize, ScriptError> {
819 let (_, dep_index) = self.extract_script_and_dep_index(script)?;
820 Ok(*dep_index)
821 }
822
823 fn extract_script_and_dep_index(
824 &self,
825 script: &Script,
826 ) -> Result<(&LazyData, &usize), ScriptError> {
827 let script_hash_type = ScriptHashType::try_from(script.hash_type())
828 .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?;
829 match script_hash_type {
830 ScriptHashType::Data | ScriptHashType::Data1 | ScriptHashType::Data2 => {
831 if let Some((dep_index, lazy)) = self.binaries_by_data_hash.get(&script.code_hash())
832 {
833 Ok((lazy, dep_index))
834 } else {
835 Err(ScriptError::ScriptNotFound(script.code_hash()))
836 }
837 }
838 ScriptHashType::Type => {
839 if let Some(ref bin) = self.binaries_by_type_hash.get(&script.code_hash()) {
840 match bin {
841 Binaries::Unique(_, dep_index, lazy) => Ok((lazy, dep_index)),
842 Binaries::Duplicate(_, dep_index, lazy) => Ok((lazy, dep_index)),
843 Binaries::Multiple => Err(ScriptError::MultipleMatches),
844 }
845 } else {
846 Err(ScriptError::ScriptNotFound(script.code_hash()))
847 }
848 }
849 }
850 }
851
852 pub fn find_script_group(
854 &self,
855 script_group_type: ScriptGroupType,
856 script_hash: &Byte32,
857 ) -> Option<&ScriptGroup> {
858 match script_group_type {
859 ScriptGroupType::Lock => self.lock_groups.get(script_hash),
860 ScriptGroupType::Type => self.type_groups.get(script_hash),
861 }
862 }
863
864 fn is_vm_version_1_and_syscalls_2_enabled(&self) -> bool {
865 let epoch_number = self.tx_env.epoch_number_without_proposal_window();
870 let hardfork_switch = self.consensus.hardfork_switch();
871 hardfork_switch
872 .ckb2021
873 .is_vm_version_1_and_syscalls_2_enabled(epoch_number)
874 }
875
876 fn is_vm_version_2_and_syscalls_3_enabled(&self) -> bool {
877 let epoch_number = self.tx_env.epoch_number_without_proposal_window();
882 let hardfork_switch = self.consensus.hardfork_switch();
883 hardfork_switch
884 .ckb2023
885 .is_vm_version_2_and_syscalls_3_enabled(epoch_number)
886 }
887
888 pub fn select_version(&self, script: &Script) -> Result<ScriptVersion, ScriptError> {
890 let is_vm_version_2_and_syscalls_3_enabled = self.is_vm_version_2_and_syscalls_3_enabled();
891 let is_vm_version_1_and_syscalls_2_enabled = self.is_vm_version_1_and_syscalls_2_enabled();
892 let script_hash_type = ScriptHashType::try_from(script.hash_type())
893 .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?;
894 match script_hash_type {
895 ScriptHashType::Data => Ok(ScriptVersion::V0),
896 ScriptHashType::Data1 => {
897 if is_vm_version_1_and_syscalls_2_enabled {
898 Ok(ScriptVersion::V1)
899 } else {
900 Err(ScriptError::InvalidVmVersion(1))
901 }
902 }
903 ScriptHashType::Data2 => {
904 if is_vm_version_2_and_syscalls_3_enabled {
905 Ok(ScriptVersion::V2)
906 } else {
907 Err(ScriptError::InvalidVmVersion(2))
908 }
909 }
910 ScriptHashType::Type => {
911 if is_vm_version_2_and_syscalls_3_enabled {
912 Ok(ScriptVersion::V2)
913 } else if is_vm_version_1_and_syscalls_2_enabled {
914 Ok(ScriptVersion::V1)
915 } else {
916 Ok(ScriptVersion::V0)
917 }
918 }
919 }
920 }
921
922 pub fn groups(&self) -> impl Iterator<Item = (&'_ Byte32, &'_ ScriptGroup)> {
924 self.lock_groups.iter().chain(self.type_groups.iter())
925 }
926
927 pub fn groups_with_type(
929 &self,
930 ) -> impl Iterator<Item = (ScriptGroupType, &'_ Byte32, &'_ ScriptGroup)> {
931 self.lock_groups
932 .iter()
933 .map(|(hash, group)| (ScriptGroupType::Lock, hash, group))
934 .chain(
935 self.type_groups
936 .iter()
937 .map(|(hash, group)| (ScriptGroupType::Type, hash, group)),
938 )
939 }
940}
941
942#[derive(Clone, Debug)]
944pub struct SgData<DL> {
945 pub rtx: Arc<ResolvedTransaction>,
947
948 pub tx_info: Arc<TxInfo<DL>>,
950
951 pub sg_info: Arc<SgInfo>,
953}
954
955#[derive(Clone, Debug)]
957pub struct SgInfo {
958 pub script_version: ScriptVersion,
960 pub script_group: ScriptGroup,
962 pub script_hash: Byte32,
964 pub program_data_piece_id: DataPieceId,
966}
967
968impl<DL> SgData<DL> {
969 pub fn new(tx_data: &TxData<DL>, script_group: &ScriptGroup) -> Result<Self, ScriptError> {
971 let script_hash = script_group.script.calc_script_hash();
972 let script_version = tx_data.select_version(&script_group.script)?;
973 let dep_index = tx_data
974 .extract_referenced_dep_index(&script_group.script)?
975 .try_into()
976 .map_err(|_| ScriptError::Other("u32 overflow".to_string()))?;
977 Ok(Self {
978 rtx: Arc::clone(&tx_data.rtx),
979 tx_info: Arc::clone(&tx_data.info),
980 sg_info: Arc::new(SgInfo {
981 script_version,
982 script_hash,
983 script_group: script_group.clone(),
984 program_data_piece_id: DataPieceId::CellDep(dep_index),
985 }),
986 })
987 }
988
989 pub fn data_loader(&self) -> &DL {
991 &self.tx_info.data_loader
992 }
993
994 pub fn group_inputs(&self) -> &[usize] {
996 &self.sg_info.script_group.input_indices
997 }
998
999 pub fn group_outputs(&self) -> &[usize] {
1001 &self.sg_info.script_group.output_indices
1002 }
1003
1004 pub fn outputs(&self) -> &[CellMeta] {
1006 &self.tx_info.outputs
1007 }
1008}
1009
1010impl<DL> DataSource<DataPieceId> for SgData<DL>
1011where
1012 DL: CellDataProvider,
1013{
1014 fn load_data(&self, id: &DataPieceId, offset: u64, length: u64) -> Option<(Bytes, u64)> {
1015 match id {
1016 DataPieceId::Input(i) => self
1017 .rtx
1018 .resolved_inputs
1019 .get(*i as usize)
1020 .and_then(|cell| self.data_loader().load_cell_data(cell)),
1021 DataPieceId::Output(i) => self
1022 .rtx
1023 .transaction
1024 .outputs_data()
1025 .get(*i as usize)
1026 .map(|data| data.raw_data()),
1027 DataPieceId::CellDep(i) => self
1028 .rtx
1029 .resolved_cell_deps
1030 .get(*i as usize)
1031 .and_then(|cell| self.data_loader().load_cell_data(cell)),
1032 DataPieceId::GroupInput(i) => self
1033 .sg_info
1034 .script_group
1035 .input_indices
1036 .get(*i as usize)
1037 .and_then(|gi| self.rtx.resolved_inputs.get(*gi))
1038 .and_then(|cell| self.data_loader().load_cell_data(cell)),
1039 DataPieceId::GroupOutput(i) => self
1040 .sg_info
1041 .script_group
1042 .output_indices
1043 .get(*i as usize)
1044 .and_then(|gi| self.rtx.transaction.outputs_data().get(*gi))
1045 .map(|data| data.raw_data()),
1046 DataPieceId::Witness(i) => self
1047 .rtx
1048 .transaction
1049 .witnesses()
1050 .get(*i as usize)
1051 .map(|data| data.raw_data()),
1052 DataPieceId::WitnessGroupInput(i) => self
1053 .sg_info
1054 .script_group
1055 .input_indices
1056 .get(*i as usize)
1057 .and_then(|gi| self.rtx.transaction.witnesses().get(*gi))
1058 .map(|data| data.raw_data()),
1059 DataPieceId::WitnessGroupOutput(i) => self
1060 .sg_info
1061 .script_group
1062 .output_indices
1063 .get(*i as usize)
1064 .and_then(|gi| self.rtx.transaction.witnesses().get(*gi))
1065 .map(|data| data.raw_data()),
1066 }
1067 .map(|data| {
1068 let offset = std::cmp::min(offset as usize, data.len());
1069 let full_length = data.len() - offset;
1070 let real_length = if length > 0 {
1071 std::cmp::min(full_length, length as usize)
1072 } else {
1073 full_length
1074 };
1075 (data.slice(offset..offset + real_length), full_length as u64)
1076 })
1077 }
1078}
1079
1080#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1083pub enum VmArgs {
1084 Reader {
1086 vm_id: u64,
1088 argc: u64,
1090 argv: u64,
1092 },
1093 Vector(Vec<Bytes>),
1095}
1096
1097#[derive(Clone)]
1099pub struct VmContext<DL>
1100where
1101 DL: CellDataProvider,
1102{
1103 pub base_cycles: Arc<AtomicU64>,
1105 pub message_box: Arc<Mutex<Vec<Message>>>,
1107 pub snapshot2_context: Arc<Mutex<Snapshot2Context<DataPieceId, SgData<DL>>>>,
1109}
1110
1111impl<DL> VmContext<DL>
1112where
1113 DL: CellDataProvider + Clone,
1114{
1115 pub fn new(sg_data: &SgData<DL>, message_box: &Arc<Mutex<Vec<Message>>>) -> Self {
1119 Self {
1120 base_cycles: Arc::new(AtomicU64::new(0)),
1121 message_box: Arc::clone(message_box),
1122 snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(sg_data.clone()))),
1123 }
1124 }
1125
1126 pub fn set_base_cycles(&mut self, base_cycles: u64) {
1128 self.base_cycles.store(base_cycles, Ordering::Release);
1129 }
1130}
1131
1132#[derive(Clone)]
1134pub enum RunMode {
1135 LimitCycles(Cycle),
1137 Pause(Pause),
1139}