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, SupportMachine, bytes::Bytes, machine::Pause, snapshot2::DataSource,
30};
31
32pub type VmIsa = u8;
34pub type VmVersion = u32;
36
37#[cfg(has_asm)]
41pub type Machine = ckb_vm::machine::asm::AsmMachine;
42#[cfg(all(not(has_asm), not(feature = "flatmemory")))]
44pub type Machine = ckb_vm::TraceMachine<
45 ckb_vm::DefaultCoreMachine<u64, ckb_vm::WXorXMemory<ckb_vm::SparseMemory<u64>>>,
46>;
47#[cfg(all(not(has_asm), feature = "flatmemory"))]
49pub type Machine = ckb_vm::TraceMachine<
50 ckb_vm::DefaultCoreMachine<u64, ckb_vm::WXorXMemory<ckb_vm::FlatMemory<u64>>>,
51>;
52
53pub type DebugPrinter = Arc<dyn Fn(&Byte32, &str) + Send + Sync>;
55pub type SyscallGenerator<DL, V, M> =
57 fn(&VmId, &SgData<DL>, &VmContext<DL>, &V) -> Vec<Box<dyn Syscalls<M>>>;
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
61pub enum ScriptVersion {
62 V0 = 0,
64 V1 = 1,
66 V2 = 2,
68}
69
70impl ScriptVersion {
71 pub const fn latest() -> Self {
73 Self::V2
74 }
75
76 pub fn vm_isa(self) -> VmIsa {
78 match self {
79 Self::V0 => ISA_IMC,
80 Self::V1 => ISA_IMC | ISA_B | ISA_MOP,
81 Self::V2 => ISA_IMC | ISA_B | ISA_MOP,
82 }
83 }
84
85 pub fn vm_version(self) -> VmVersion {
87 match self {
88 Self::V0 => VERSION0,
89 Self::V1 => VERSION1,
90 Self::V2 => VERSION2,
91 }
92 }
93
94 pub fn data_hash_type(self) -> ScriptHashType {
100 match self {
101 Self::V0 => ScriptHashType::Data,
102 Self::V1 => ScriptHashType::Data1,
103 Self::V2 => ScriptHashType::Data2,
104 }
105 }
106
107 pub fn init_core_machine_without_limit(self) -> <Machine as DefaultMachineRunner>::Inner {
111 self.init_core_machine(u64::MAX)
112 }
113
114 pub fn init_core_machine(self, max_cycles: Cycle) -> <Machine as DefaultMachineRunner>::Inner {
116 let isa = self.vm_isa();
117 let version = self.vm_version();
118 <<Machine as DefaultMachineRunner>::Inner as SupportMachine>::new(isa, version, max_cycles)
119 }
120}
121
122#[derive(Clone, Debug)]
128pub struct ScriptGroup {
129 pub script: Script,
133 pub group_type: ScriptGroupType,
135 pub input_indices: Vec<usize>,
137 pub output_indices: Vec<usize>,
139}
140
141impl ScriptGroup {
152 pub(crate) fn new(script: &Script, group_type: ScriptGroupType) -> Self {
154 Self {
155 group_type,
156 script: script.to_owned(),
157 input_indices: vec![],
158 output_indices: vec![],
159 }
160 }
161
162 pub(crate) fn from_lock_script(script: &Script) -> Self {
164 Self::new(script, ScriptGroupType::Lock)
165 }
166
167 pub(crate) fn from_type_script(script: &Script) -> Self {
169 Self::new(script, ScriptGroupType::Type)
170 }
171}
172
173#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
178#[serde(rename_all = "snake_case")]
179pub enum ScriptGroupType {
180 Lock,
182 Type,
184}
185
186impl fmt::Display for ScriptGroupType {
187 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188 match self {
189 ScriptGroupType::Lock => write!(f, "Lock"),
190 ScriptGroupType::Type => write!(f, "Type"),
191 }
192 }
193}
194
195#[derive(Clone)]
197pub struct TransactionState {
198 pub current: usize,
200 pub current_cycles: Cycle,
202 pub limit_cycles: Cycle,
204}
205
206impl TransactionState {
207 pub fn new(current: usize, current_cycles: Cycle, limit_cycles: Cycle) -> Self {
209 TransactionState {
210 current,
211 current_cycles,
212 limit_cycles,
213 }
214 }
215
216 pub fn next_limit_cycles(&self, step_cycles: Cycle, max_cycles: Cycle) -> (Cycle, bool) {
218 let remain = max_cycles - self.current_cycles;
219 let next_limit = self.limit_cycles + step_cycles;
220
221 if next_limit < remain {
222 (next_limit, false)
223 } else {
224 (remain, true)
225 }
226 }
227}
228
229#[allow(clippy::large_enum_variant)]
231#[derive(Debug)]
232pub enum VerifyResult {
233 Completed(Cycle),
235 Suspended(TransactionState),
237}
238
239impl std::fmt::Debug for TransactionState {
240 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> std::fmt::Result {
241 f.debug_struct("TransactionState")
242 .field("current", &self.current)
243 .field("current_cycles", &self.current_cycles)
244 .field("limit_cycles", &self.limit_cycles)
245 .finish()
246 }
247}
248
249#[derive(Eq, PartialEq, Clone, Debug)]
251pub enum ChunkCommand {
252 Suspend,
254 Resume,
256 Stop,
258}
259
260pub type VmId = u64;
262pub const FIRST_VM_ID: VmId = 0;
264
265#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
267pub struct Fd(pub u64);
268
269pub const FIRST_FD_SLOT: u64 = 2;
271
272impl Fd {
273 pub fn create(slot: u64) -> (Fd, Fd, u64) {
275 (Fd(slot), Fd(slot + 1), slot + 2)
276 }
277
278 pub fn other_fd(&self) -> Fd {
280 Fd(self.0 ^ 0x1)
281 }
282
283 pub fn is_read(&self) -> bool {
285 self.0.is_multiple_of(2)
286 }
287
288 pub fn is_write(&self) -> bool {
290 self.0 % 2 == 1
291 }
292}
293
294#[derive(Clone, Debug, PartialEq, Eq, Hash)]
296pub struct ReadState {
297 pub fd: Fd,
299 pub length: u64,
301 pub buffer_addr: u64,
303 pub length_addr: u64,
305}
306
307#[derive(Clone, Debug, PartialEq, Eq, Hash)]
309pub struct WriteState {
310 pub fd: Fd,
312 pub consumed: u64,
314 pub length: u64,
316 pub buffer_addr: u64,
318 pub length_addr: u64,
320}
321
322#[derive(Clone, Debug, PartialEq, Eq, Hash)]
324pub enum VmState {
325 Runnable,
327 Terminated,
329 Wait {
331 target_vm_id: VmId,
333 exit_code_addr: u64,
335 },
336 WaitForWrite(WriteState),
338 WaitForRead(ReadState),
340}
341
342#[derive(Clone, Debug)]
344pub struct DataLocation {
345 pub data_piece_id: DataPieceId,
347 pub offset: u64,
349 pub length: u64,
351}
352
353#[derive(Clone, Debug)]
355pub struct ExecV2Args {
356 pub location: DataLocation,
358 pub argc: u64,
360 pub argv: u64,
362}
363
364#[derive(Clone, Debug)]
366pub struct SpawnArgs {
367 pub location: DataLocation,
369 pub argc: u64,
371 pub argv: u64,
373 pub fds: Vec<Fd>,
375 pub process_id_addr: u64,
377}
378
379#[derive(Clone, Debug)]
381pub struct WaitArgs {
382 pub target_id: VmId,
384 pub exit_code_addr: u64,
386}
387
388#[derive(Clone, Debug)]
390pub struct PipeArgs {
391 pub fd1_addr: u64,
393 pub fd2_addr: u64,
395}
396
397#[derive(Clone, Debug)]
399pub struct FdArgs {
400 pub fd: Fd,
402 pub length: u64,
405 pub buffer_addr: u64,
407 pub length_addr: u64,
411}
412
413#[derive(Clone, Debug)]
416pub enum Message {
417 ExecV2(VmId, ExecV2Args),
419 Spawn(VmId, SpawnArgs),
421 Wait(VmId, WaitArgs),
423 Pipe(VmId, PipeArgs),
425 FdRead(VmId, FdArgs),
427 FdWrite(VmId, FdArgs),
429 InheritedFileDescriptor(VmId, FdArgs),
431 Close(VmId, Fd),
433}
434
435#[derive(Clone, Debug, PartialEq, Eq, Hash)]
437pub enum DataPieceId {
438 Input(u32),
440 Output(u32),
442 CellDep(u32),
444 GroupInput(u32),
446 GroupOutput(u32),
448 Witness(u32),
450 WitnessGroupInput(u32),
452 WitnessGroupOutput(u32),
454}
455
456impl TryFrom<(u64, u64, u64)> for DataPieceId {
457 type Error = String;
458
459 fn try_from(value: (u64, u64, u64)) -> Result<Self, Self::Error> {
460 let (source, index, place) = value;
461 let index: u32 =
462 u32::try_from(index).map_err(|e| format!("Error casting index to u32: {}", e))?;
463 match (source, place) {
464 (1, 0) => Ok(DataPieceId::Input(index)),
465 (2, 0) => Ok(DataPieceId::Output(index)),
466 (3, 0) => Ok(DataPieceId::CellDep(index)),
467 (0x0100000000000001, 0) => Ok(DataPieceId::GroupInput(index)),
468 (0x0100000000000002, 0) => Ok(DataPieceId::GroupOutput(index)),
469 (1, 1) => Ok(DataPieceId::Witness(index)),
470 (2, 1) => Ok(DataPieceId::Witness(index)),
471 (0x0100000000000001, 1) => Ok(DataPieceId::WitnessGroupInput(index)),
472 (0x0100000000000002, 1) => Ok(DataPieceId::WitnessGroupOutput(index)),
473 _ => Err(format!("Invalid source value: {:#x}", source)),
474 }
475 }
476}
477
478#[derive(Debug, PartialEq, Eq, Clone)]
480pub enum DataGuard {
481 NotLoaded(OutPoint),
483 Loaded(Bytes),
485}
486
487#[derive(Debug, Clone)]
489pub struct LazyData(Arc<RwLock<DataGuard>>);
490
491impl LazyData {
492 fn from_cell_meta(cell_meta: &CellMeta) -> LazyData {
493 match &cell_meta.mem_cell_data {
494 Some(data) => LazyData(Arc::new(RwLock::new(DataGuard::Loaded(data.to_owned())))),
495 None => LazyData(Arc::new(RwLock::new(DataGuard::NotLoaded(
496 cell_meta.out_point.clone(),
497 )))),
498 }
499 }
500
501 fn access<DL: CellDataProvider>(&self, data_loader: &DL) -> Result<Bytes, ScriptError> {
502 let guard = self
503 .0
504 .read()
505 .map_err(|_| ScriptError::Other("RwLock poisoned".into()))?
506 .to_owned();
507 match guard {
508 DataGuard::NotLoaded(out_point) => {
509 let data = data_loader
510 .get_cell_data(&out_point)
511 .ok_or(ScriptError::Other("cell data not found".into()))?;
512 let mut write_guard = self
513 .0
514 .write()
515 .map_err(|_| ScriptError::Other("RwLock poisoned".into()))?;
516 *write_guard = DataGuard::Loaded(data.clone());
517 Ok(data)
518 }
519 DataGuard::Loaded(bytes) => Ok(bytes),
520 }
521 }
522}
523
524#[derive(Debug, Clone)]
526pub enum Binaries {
527 Unique(Byte32, usize, LazyData),
529 Duplicate(Byte32, usize, LazyData),
533 Multiple,
536}
537
538impl Binaries {
539 fn new(data_hash: Byte32, dep_index: usize, data: LazyData) -> Self {
540 Self::Unique(data_hash, dep_index, data)
541 }
542
543 fn merge(&mut self, data_hash: &Byte32) {
544 match self {
545 Self::Unique(hash, dep_index, data) | Self::Duplicate(hash, dep_index, data) => {
546 if hash != data_hash {
547 *self = Self::Multiple;
548 } else {
549 *self = Self::Duplicate(hash.to_owned(), *dep_index, data.to_owned());
550 }
551 }
552 Self::Multiple => {}
553 }
554 }
555}
556
557#[derive(Clone, Debug)]
559pub struct TxData<DL> {
560 pub rtx: Arc<ResolvedTransaction>,
562
563 pub info: Arc<TxInfo<DL>>,
565}
566
567#[derive(Clone, Debug)]
570pub struct TxInfo<DL> {
571 pub data_loader: DL,
573 pub consensus: Arc<Consensus>,
575 pub tx_env: Arc<TxVerifyEnv>,
577
578 pub binaries_by_data_hash: HashMap<Byte32, (usize, LazyData)>,
580 pub binaries_by_type_hash: HashMap<Byte32, Binaries>,
582 pub lock_groups: BTreeMap<Byte32, ScriptGroup>,
584 pub type_groups: BTreeMap<Byte32, ScriptGroup>,
586 pub outputs: Vec<CellMeta>,
588}
589
590impl<DL> TxData<DL>
591where
592 DL: CellDataProvider,
593{
594 pub fn new(
596 rtx: Arc<ResolvedTransaction>,
597 data_loader: DL,
598 consensus: Arc<Consensus>,
599 tx_env: Arc<TxVerifyEnv>,
600 ) -> Self {
601 let tx_hash = rtx.transaction.hash();
602 let resolved_cell_deps = &rtx.resolved_cell_deps;
603 let resolved_inputs = &rtx.resolved_inputs;
604 let outputs = rtx
605 .transaction
606 .outputs_with_data_iter()
607 .enumerate()
608 .map(|(index, (cell_output, data))| {
609 let out_point = OutPoint::new_builder()
610 .tx_hash(tx_hash.clone())
611 .index(index)
612 .build();
613 let data_hash = CellOutput::calc_data_hash(&data);
614 CellMeta {
615 cell_output,
616 out_point,
617 transaction_info: None,
618 data_bytes: data.len() as u64,
619 mem_cell_data: Some(data),
620 mem_cell_data_hash: Some(data_hash),
621 }
622 })
623 .collect();
624
625 let mut binaries_by_data_hash: HashMap<Byte32, (usize, LazyData)> = HashMap::default();
626 let mut binaries_by_type_hash: HashMap<Byte32, Binaries> = HashMap::default();
627 for (i, cell_meta) in resolved_cell_deps.iter().enumerate() {
628 let data_hash = data_loader
629 .load_cell_data_hash(cell_meta)
630 .expect("cell data hash");
631 let lazy = LazyData::from_cell_meta(cell_meta);
632 binaries_by_data_hash.insert(data_hash.to_owned(), (i, lazy.to_owned()));
633
634 if let Some(t) = &cell_meta.cell_output.type_().to_opt() {
635 binaries_by_type_hash
636 .entry(t.calc_script_hash())
637 .and_modify(|bin| bin.merge(&data_hash))
638 .or_insert_with(|| Binaries::new(data_hash.to_owned(), i, lazy.to_owned()));
639 }
640 }
641
642 let mut lock_groups = BTreeMap::default();
643 let mut type_groups = BTreeMap::default();
644 for (i, cell_meta) in resolved_inputs.iter().enumerate() {
645 let output = &cell_meta.cell_output;
648 let lock_group_entry = lock_groups
649 .entry(output.calc_lock_hash())
650 .or_insert_with(|| ScriptGroup::from_lock_script(&output.lock()));
651 lock_group_entry.input_indices.push(i);
652 if let Some(t) = &output.type_().to_opt() {
653 let type_group_entry = type_groups
654 .entry(t.calc_script_hash())
655 .or_insert_with(|| ScriptGroup::from_type_script(t));
656 type_group_entry.input_indices.push(i);
657 }
658 }
659 for (i, output) in rtx.transaction.outputs().into_iter().enumerate() {
660 if let Some(t) = &output.type_().to_opt() {
661 let type_group_entry = type_groups
662 .entry(t.calc_script_hash())
663 .or_insert_with(|| ScriptGroup::from_type_script(t));
664 type_group_entry.output_indices.push(i);
665 }
666 }
667
668 Self {
669 rtx,
670 info: Arc::new(TxInfo {
671 data_loader,
672 consensus,
673 tx_env,
674 binaries_by_data_hash,
675 binaries_by_type_hash,
676 lock_groups,
677 type_groups,
678 outputs,
679 }),
680 }
681 }
682
683 #[inline]
684 pub fn extract_script(&self, script: &Script) -> Result<Bytes, ScriptError> {
686 self.info.extract_script(script)
687 }
688}
689
690impl<DL> TxInfo<DL>
691where
692 DL: CellDataProvider,
693{
694 #[inline]
695 pub fn extract_script(&self, script: &Script) -> Result<Bytes, ScriptError> {
697 let (lazy, _) = self.extract_script_and_dep_index(script)?;
698 lazy.access(&self.data_loader)
699 }
700}
701
702impl<DL> TxData<DL> {
703 #[inline]
704 pub fn tx_hash(&self) -> Byte32 {
706 self.rtx.transaction.hash()
707 }
708
709 #[inline]
710 pub fn extract_referenced_dep_index(&self, script: &Script) -> Result<usize, ScriptError> {
712 self.info.extract_referenced_dep_index(script)
713 }
714
715 #[inline]
716 pub fn find_script_group(
718 &self,
719 script_group_type: ScriptGroupType,
720 script_hash: &Byte32,
721 ) -> Option<&ScriptGroup> {
722 self.info.find_script_group(script_group_type, script_hash)
723 }
724
725 #[inline]
726 pub fn select_version(&self, script: &Script) -> Result<ScriptVersion, ScriptError> {
728 self.info.select_version(script)
729 }
730
731 #[inline]
732 pub fn groups(&self) -> impl Iterator<Item = (&'_ Byte32, &'_ ScriptGroup)> {
734 self.info.groups()
735 }
736
737 #[inline]
738 pub fn groups_with_type(
740 &self,
741 ) -> impl Iterator<Item = (ScriptGroupType, &'_ Byte32, &'_ ScriptGroup)> {
742 self.info.groups_with_type()
743 }
744}
745
746impl<DL> TxInfo<DL> {
747 #[inline]
748 pub fn extract_referenced_dep_index(&self, script: &Script) -> Result<usize, ScriptError> {
750 let (_, dep_index) = self.extract_script_and_dep_index(script)?;
751 Ok(*dep_index)
752 }
753
754 fn extract_script_and_dep_index(
755 &self,
756 script: &Script,
757 ) -> Result<(&LazyData, &usize), ScriptError> {
758 let script_hash_type = ScriptHashType::try_from(script.hash_type())
759 .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?;
760 match script_hash_type {
761 ScriptHashType::Data | ScriptHashType::Data1 | ScriptHashType::Data2 => {
762 if let Some((dep_index, lazy)) = self.binaries_by_data_hash.get(&script.code_hash())
763 {
764 Ok((lazy, dep_index))
765 } else {
766 Err(ScriptError::ScriptNotFound(script.code_hash()))
767 }
768 }
769 ScriptHashType::Type => {
770 if let Some(ref bin) = self.binaries_by_type_hash.get(&script.code_hash()) {
771 match bin {
772 Binaries::Unique(_, dep_index, lazy) => Ok((lazy, dep_index)),
773 Binaries::Duplicate(_, dep_index, lazy) => Ok((lazy, dep_index)),
774 Binaries::Multiple => Err(ScriptError::MultipleMatches),
775 }
776 } else {
777 Err(ScriptError::ScriptNotFound(script.code_hash()))
778 }
779 }
780 hash_type => {
781 return Err(ScriptError::InvalidScriptHashType(format!(
782 "The ScriptHashType/{:?} has not been activated, and is not permitted for use.",
783 hash_type
784 )));
785 }
786 }
787 }
788
789 pub fn find_script_group(
791 &self,
792 script_group_type: ScriptGroupType,
793 script_hash: &Byte32,
794 ) -> Option<&ScriptGroup> {
795 match script_group_type {
796 ScriptGroupType::Lock => self.lock_groups.get(script_hash),
797 ScriptGroupType::Type => self.type_groups.get(script_hash),
798 }
799 }
800
801 fn is_vm_version_1_and_syscalls_2_enabled(&self) -> bool {
802 let epoch_number = self.tx_env.epoch_number_without_proposal_window();
807 let hardfork_switch = self.consensus.hardfork_switch();
808 hardfork_switch
809 .ckb2021
810 .is_vm_version_1_and_syscalls_2_enabled(epoch_number)
811 }
812
813 fn is_vm_version_2_and_syscalls_3_enabled(&self) -> bool {
814 let epoch_number = self.tx_env.epoch_number_without_proposal_window();
819 let hardfork_switch = self.consensus.hardfork_switch();
820 hardfork_switch
821 .ckb2023
822 .is_vm_version_2_and_syscalls_3_enabled(epoch_number)
823 }
824
825 pub fn select_version(&self, script: &Script) -> Result<ScriptVersion, ScriptError> {
827 let is_vm_version_2_and_syscalls_3_enabled = self.is_vm_version_2_and_syscalls_3_enabled();
828 let is_vm_version_1_and_syscalls_2_enabled = self.is_vm_version_1_and_syscalls_2_enabled();
829 let script_hash_type = ScriptHashType::try_from(script.hash_type())
830 .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?;
831 match script_hash_type {
832 ScriptHashType::Data => Ok(ScriptVersion::V0),
833 ScriptHashType::Data1 => {
834 if is_vm_version_1_and_syscalls_2_enabled {
835 Ok(ScriptVersion::V1)
836 } else {
837 Err(ScriptError::InvalidVmVersion(1))
838 }
839 }
840 ScriptHashType::Data2 => {
841 if is_vm_version_2_and_syscalls_3_enabled {
842 Ok(ScriptVersion::V2)
843 } else {
844 Err(ScriptError::InvalidVmVersion(2))
845 }
846 }
847 ScriptHashType::Type => {
848 if is_vm_version_2_and_syscalls_3_enabled {
849 Ok(ScriptVersion::V2)
850 } else if is_vm_version_1_and_syscalls_2_enabled {
851 Ok(ScriptVersion::V1)
852 } else {
853 Ok(ScriptVersion::V0)
854 }
855 }
856 hash_type => {
857 return Err(ScriptError::InvalidScriptHashType(format!(
858 "The ScriptHashType/{:?} has not been activated, and is not permitted for use.",
859 hash_type
860 )));
861 }
862 }
863 }
864
865 pub fn groups(&self) -> impl Iterator<Item = (&'_ Byte32, &'_ ScriptGroup)> {
867 self.lock_groups.iter().chain(self.type_groups.iter())
868 }
869
870 pub fn groups_with_type(
872 &self,
873 ) -> impl Iterator<Item = (ScriptGroupType, &'_ Byte32, &'_ ScriptGroup)> {
874 self.lock_groups
875 .iter()
876 .map(|(hash, group)| (ScriptGroupType::Lock, hash, group))
877 .chain(
878 self.type_groups
879 .iter()
880 .map(|(hash, group)| (ScriptGroupType::Type, hash, group)),
881 )
882 }
883}
884
885#[derive(Clone, Debug)]
887pub struct SgData<DL> {
888 pub rtx: Arc<ResolvedTransaction>,
890
891 pub tx_info: Arc<TxInfo<DL>>,
893
894 pub sg_info: Arc<SgInfo>,
896}
897
898#[derive(Clone, Debug)]
900pub struct SgInfo {
901 pub script_version: ScriptVersion,
903 pub script_group: ScriptGroup,
905 pub script_hash: Byte32,
907 pub program_data_piece_id: DataPieceId,
909}
910
911impl<DL> SgData<DL> {
912 pub fn new(tx_data: &TxData<DL>, script_group: &ScriptGroup) -> Result<Self, ScriptError> {
914 let script_hash = script_group.script.calc_script_hash();
915 let script_version = tx_data.select_version(&script_group.script)?;
916 let dep_index = tx_data
917 .extract_referenced_dep_index(&script_group.script)?
918 .try_into()
919 .map_err(|_| ScriptError::Other("u32 overflow".to_string()))?;
920 Ok(Self {
921 rtx: Arc::clone(&tx_data.rtx),
922 tx_info: Arc::clone(&tx_data.info),
923 sg_info: Arc::new(SgInfo {
924 script_version,
925 script_hash,
926 script_group: script_group.clone(),
927 program_data_piece_id: DataPieceId::CellDep(dep_index),
928 }),
929 })
930 }
931
932 pub fn data_loader(&self) -> &DL {
934 &self.tx_info.data_loader
935 }
936
937 pub fn group_inputs(&self) -> &[usize] {
939 &self.sg_info.script_group.input_indices
940 }
941
942 pub fn group_outputs(&self) -> &[usize] {
944 &self.sg_info.script_group.output_indices
945 }
946
947 pub fn outputs(&self) -> &[CellMeta] {
949 &self.tx_info.outputs
950 }
951}
952
953impl<DL> DataSource<DataPieceId> for SgData<DL>
954where
955 DL: CellDataProvider,
956{
957 fn load_data(&self, id: &DataPieceId, offset: u64, length: u64) -> Option<(Bytes, u64)> {
958 match id {
959 DataPieceId::Input(i) => self
960 .rtx
961 .resolved_inputs
962 .get(*i as usize)
963 .and_then(|cell| self.data_loader().load_cell_data(cell)),
964 DataPieceId::Output(i) => self
965 .rtx
966 .transaction
967 .outputs_data()
968 .get(*i as usize)
969 .map(|data| data.raw_data()),
970 DataPieceId::CellDep(i) => self
971 .rtx
972 .resolved_cell_deps
973 .get(*i as usize)
974 .and_then(|cell| self.data_loader().load_cell_data(cell)),
975 DataPieceId::GroupInput(i) => self
976 .sg_info
977 .script_group
978 .input_indices
979 .get(*i as usize)
980 .and_then(|gi| self.rtx.resolved_inputs.get(*gi))
981 .and_then(|cell| self.data_loader().load_cell_data(cell)),
982 DataPieceId::GroupOutput(i) => self
983 .sg_info
984 .script_group
985 .output_indices
986 .get(*i as usize)
987 .and_then(|gi| self.rtx.transaction.outputs_data().get(*gi))
988 .map(|data| data.raw_data()),
989 DataPieceId::Witness(i) => self
990 .rtx
991 .transaction
992 .witnesses()
993 .get(*i as usize)
994 .map(|data| data.raw_data()),
995 DataPieceId::WitnessGroupInput(i) => self
996 .sg_info
997 .script_group
998 .input_indices
999 .get(*i as usize)
1000 .and_then(|gi| self.rtx.transaction.witnesses().get(*gi))
1001 .map(|data| data.raw_data()),
1002 DataPieceId::WitnessGroupOutput(i) => self
1003 .sg_info
1004 .script_group
1005 .output_indices
1006 .get(*i as usize)
1007 .and_then(|gi| self.rtx.transaction.witnesses().get(*gi))
1008 .map(|data| data.raw_data()),
1009 }
1010 .map(|data| {
1011 let offset = std::cmp::min(offset as usize, data.len());
1012 let full_length = data.len() - offset;
1013 let real_length = if length > 0 {
1014 std::cmp::min(full_length, length as usize)
1015 } else {
1016 full_length
1017 };
1018 (data.slice(offset..offset + real_length), full_length as u64)
1019 })
1020 }
1021}
1022
1023#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1026pub enum VmArgs {
1027 Reader {
1029 vm_id: u64,
1031 argc: u64,
1033 argv: u64,
1035 },
1036 Vector(Vec<Bytes>),
1038}
1039
1040#[derive(Clone)]
1042pub struct VmContext<DL>
1043where
1044 DL: CellDataProvider,
1045{
1046 pub base_cycles: Arc<AtomicU64>,
1048 pub message_box: Arc<Mutex<Vec<Message>>>,
1050 pub snapshot2_context: Arc<Mutex<Snapshot2Context<DataPieceId, SgData<DL>>>>,
1052}
1053
1054impl<DL> VmContext<DL>
1055where
1056 DL: CellDataProvider + Clone,
1057{
1058 pub fn new(sg_data: &SgData<DL>, message_box: &Arc<Mutex<Vec<Message>>>) -> Self {
1062 Self {
1063 base_cycles: Arc::new(AtomicU64::new(0)),
1064 message_box: Arc::clone(message_box),
1065 snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(sg_data.clone()))),
1066 }
1067 }
1068
1069 pub fn set_base_cycles(&mut self, base_cycles: u64) {
1071 self.base_cycles.store(base_cycles, Ordering::Release);
1072 }
1073}
1074
1075#[derive(Clone)]
1077pub enum RunMode {
1078 LimitCycles(Cycle),
1080 Pause(Pause, Cycle),
1082}
1083
1084#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
1086pub struct TerminatedResult {
1087 pub exit_code: i8,
1089 pub consumed_cycles: Cycle,
1092}
1093
1094#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
1096pub struct IterationResult {
1097 pub executed_vm: VmId,
1099 pub terminated_status: Option<TerminatedResult>,
1101}