1use anyhow::Result;
2use bitflags::bitflags;
3use num_bigint::BigInt;
4#[cfg(feature = "tracing")]
5use tracing::instrument;
6use tycho_types::cell::*;
7use tycho_types::error::Error;
8use tycho_types::models::SignatureDomain;
9
10use crate::cont::{
11 AgainCont, ArgContExt, ControlData, ControlRegs, ExcQuitCont, OrdCont, QuitCont, RcCont,
12 RepeatCont, UntilCont, WhileCont,
13};
14use crate::dispatch::DispatchTable;
15use crate::error::{VmException, VmResult};
16use crate::gas::{GasConsumer, GasParams, LibraryProvider, NoLibraries, ParentGasConsumer};
17use crate::instr::{codepage, codepage0};
18use crate::saferc::SafeRc;
19use crate::smc_info::{SmcInfo, VmVersion};
20use crate::stack::{RcStackValue, Stack};
21use crate::util::OwnedCellSlice;
22
23#[derive(Default)]
25pub struct VmStateBuilder<'a> {
26 pub code: Option<OwnedCellSlice>,
27 pub data: Option<Cell>,
28 pub stack: SafeRc<Stack>,
29 pub libraries: Option<&'a dyn LibraryProvider>,
30 pub c7: Option<SafeRc<Vec<RcStackValue>>>,
31 pub gas: GasParams,
32 pub init_selector: InitSelectorParams,
33 pub version: Option<VmVersion>,
34 pub modifiers: BehaviourModifiers,
35 pub debug: Option<&'a mut dyn std::fmt::Write>,
36}
37
38impl<'a> VmStateBuilder<'a> {
39 pub fn new() -> Self {
40 Self::default()
41 }
42
43 pub fn build(mut self) -> VmState<'a> {
44 static NO_LIBRARIES: NoLibraries = NoLibraries;
45
46 let quit0 = QUIT0.with(SafeRc::clone);
47 let quit1 = QUIT1.with(SafeRc::clone);
48 let cp = codepage0();
49
50 let (code, throw_on_code_access) = match self.code {
51 Some(code) => (code, false),
52 None => (Default::default(), true),
53 };
54
55 let c3 = match self.init_selector {
56 InitSelectorParams::None => QUIT11.with(SafeRc::clone).into_dyn_cont(),
57 InitSelectorParams::UseCode { push0 } => {
58 if push0 {
59 vm_log_trace!("implicit PUSH 0 at start");
60 SafeRc::make_mut(&mut self.stack)
61 .items
62 .push(Stack::make_zero());
63 }
64 SafeRc::from(OrdCont::simple(code.clone(), cp.id()))
65 }
66 };
67
68 let signature_domain_stack =
69 SafeRc::new(if let Some(global_id) = self.modifiers.signature_with_id {
70 vec![SignatureDomain::L2 { global_id }]
71 } else {
72 Vec::new()
73 });
74
75 VmState {
76 cr: ControlRegs {
77 c: [
78 Some(quit0.clone().into_dyn_cont()),
79 Some(quit1.clone().into_dyn_cont()),
80 Some(EXC_QUIT.with(SafeRc::clone).into_dyn_cont()),
81 Some(c3),
82 ],
83 d: [
84 Some(self.data.unwrap_or_default()),
85 Some(Cell::empty_cell()),
86 ],
87 c7: Some(self.c7.unwrap_or_default()),
88 },
89 code,
90 throw_on_code_access,
91 stack: self.stack,
92 signature_domains: signature_domain_stack,
93 committed_state: None,
94 steps: 0,
95 quit0,
96 quit1,
97 gas: GasConsumer::with_libraries(self.gas, self.libraries.unwrap_or(&NO_LIBRARIES)),
98 cp,
99 debug: self.debug,
100 modifiers: self.modifiers,
101 version: self.version.unwrap_or(VmState::DEFAULT_VERSION),
102 parent: None,
103 }
104 }
105
106 pub fn with_libraries<T: LibraryProvider>(mut self, libraries: &'a T) -> Self {
107 self.libraries = Some(libraries);
108 self
109 }
110
111 pub fn with_gas(mut self, gas: GasParams) -> Self {
112 self.gas = gas;
113 self
114 }
115
116 pub fn with_debug<T: std::fmt::Write>(mut self, stderr: &'a mut T) -> Self {
117 self.debug = Some(stderr);
118 self
119 }
120
121 pub fn with_code<T: IntoCode>(mut self, code: T) -> Self {
122 self.code = code.into_code().ok();
123 self
124 }
125
126 pub fn with_data(mut self, data: Cell) -> Self {
127 self.data = Some(data);
128 self
129 }
130
131 pub fn with_init_selector(mut self, push0: bool) -> Self {
132 self.init_selector = InitSelectorParams::UseCode { push0 };
133 self
134 }
135
136 pub fn with_stack<I: IntoIterator<Item = RcStackValue>>(mut self, values: I) -> Self {
137 self.stack = SafeRc::new(values.into_iter().collect());
138 self
139 }
140
141 pub fn with_raw_stack(mut self, stack: SafeRc<Stack>) -> Self {
142 self.stack = stack;
143 self
144 }
145
146 pub fn with_smc_info<T: SmcInfo>(mut self, info: T) -> Self {
147 if self.version.is_none() {
148 self.version = Some(info.version());
149 }
150 self.c7 = Some(info.build_c7());
151 self
152 }
153
154 pub fn with_modifiers(mut self, modifiers: BehaviourModifiers) -> Self {
155 self.modifiers = modifiers;
156 self
157 }
158
159 pub fn with_version(mut self, version: VmVersion) -> Self {
160 self.version = Some(version);
161 self
162 }
163}
164
165pub trait IntoCode {
167 fn into_code(self) -> Result<OwnedCellSlice, Error>;
168}
169
170impl<T: IntoCode> IntoCode for Option<T> {
171 fn into_code(self) -> Result<OwnedCellSlice, Error> {
172 match self {
173 Some(code) => code.into_code(),
174 None => Err(Error::CellUnderflow),
175 }
176 }
177}
178
179impl IntoCode for CellSliceParts {
180 #[inline]
181 fn into_code(self) -> Result<OwnedCellSlice, Error> {
182 Ok(OwnedCellSlice::from(self))
183 }
184}
185
186impl IntoCode for OwnedCellSlice {
187 #[inline]
188 fn into_code(self) -> Result<OwnedCellSlice, Error> {
189 Ok(self)
190 }
191}
192
193impl IntoCode for Cell {
194 fn into_code(mut self) -> Result<OwnedCellSlice, Error> {
195 let descriptor = self.descriptor();
196 if descriptor.is_exotic() {
197 if descriptor.is_library() {
198 self = CellBuilder::build_from(self).unwrap();
200 } else {
201 return Err(Error::UnexpectedExoticCell);
203 }
204 }
205
206 Ok(OwnedCellSlice::new_allow_exotic(self))
207 }
208}
209
210#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
212pub enum InitSelectorParams {
213 #[default]
214 None,
215 UseCode {
216 push0: bool,
217 },
218}
219
220pub struct VmState<'a> {
222 pub code: OwnedCellSlice,
224 pub throw_on_code_access: bool,
226 pub stack: SafeRc<Stack>,
228 pub signature_domains: SafeRc<Vec<SignatureDomain>>,
230 pub cr: ControlRegs,
232 pub committed_state: Option<CommittedState>,
234 pub steps: u64,
236 pub quit0: SafeRc<QuitCont>,
238 pub quit1: SafeRc<QuitCont>,
240 pub gas: GasConsumer<'a>,
242 pub cp: &'static DispatchTable,
244 pub debug: Option<&'a mut dyn std::fmt::Write>,
246 pub modifiers: BehaviourModifiers,
248 pub version: VmVersion,
250 pub parent: Option<Box<ParentVmState<'a>>>,
252}
253
254pub struct ParentVmState<'a> {
256 pub code: OwnedCellSlice,
258 pub stack: SafeRc<Stack>,
260 pub signature_domains: SafeRc<Vec<SignatureDomain>>,
262 pub cr: ControlRegs,
264 pub committed_state: Option<CommittedState>,
266 pub steps: u64,
268 pub quit0: SafeRc<QuitCont>,
270 pub quit1: SafeRc<QuitCont>,
272 pub gas: ParentGasConsumer<'a>,
274 pub cp: &'static DispatchTable,
276
277 pub return_data: bool,
279 pub return_actions: bool,
281 pub return_gas: bool,
283 pub return_values: Option<u32>,
287
288 pub parent: Option<Box<ParentVmState<'a>>>,
290}
291
292impl<'a> VmState<'a> {
293 pub const DEFAULT_VERSION: VmVersion = VmVersion::LATEST_TON;
294
295 pub const MAX_DATA_DEPTH: u16 = 512;
296
297 thread_local! {
298 static EMPTY_STACK: SafeRc<Stack> = SafeRc::new(Default::default());
299 }
300
301 pub fn builder() -> VmStateBuilder<'a> {
302 VmStateBuilder::default()
303 }
304
305 #[cfg_attr(
306 feature = "tracing",
307 instrument(
308 level = "trace",
309 name = "vm_step",
310 fields(n = self.steps),
311 skip_all,
312 )
313 )]
314 pub fn step(&mut self) -> VmResult<i32> {
315 #[cfg(feature = "tracing")]
316 if self
317 .modifiers
318 .log_mask
319 .intersects(VmLogMask::DUMP_STACK.union(VmLogMask::DUMP_STACK_VERBOSE))
320 {
321 vm_log_stack!(
322 self.stack,
323 self.modifiers
324 .log_mask
325 .contains(VmLogMask::DUMP_STACK_VERBOSE)
326 );
327 }
328
329 self.steps += 1;
330 if !self.code.range().is_data_empty() {
331 #[cfg(feature = "tracing")]
332 if self.modifiers.log_mask.contains(VmLogMask::EXEC_LOCATION) {
333 let Size { bits, refs } = self.code.range().offset();
334 vm_log_exec_location!(self.code.cell(), bits, refs);
335 }
336
337 self.cp.dispatch(self)
338 } else if !self.code.range().is_refs_empty() {
339 vm_log_op!("implicit JMPREF");
340
341 let next_cell = self.code.apply().get_reference_cloned(0)?;
342
343 #[cfg(feature = "tracing")]
344 if self.modifiers.log_mask.contains(VmLogMask::EXEC_LOCATION) {
345 vm_log_exec_location!(next_cell, 0u16, 0u8);
346 }
347
348 self.gas.try_consume_implicit_jmpref_gas()?;
349 let code = self.gas.load_cell_as_slice(next_cell, LoadMode::Full)?;
350
351 let cont = SafeRc::from(OrdCont::simple(code, self.cp.id()));
352 self.jump(cont)
353 } else {
354 vm_log_op!("implicit RET");
355
356 self.gas.try_consume_implicit_ret_gas()?;
357 self.ret()
358 }
359 }
360
361 pub fn run(&mut self) -> i32 {
362 if self.throw_on_code_access {
363 return VmException::Fatal as u8 as i32;
365 }
366
367 let mut res = 0;
368 loop {
369 res = match self.restore_parent(!res) {
370 Ok(()) => self.run_inner(),
371 Err(OutOfGas) => {
372 self.steps += 1;
373 self.throw_out_of_gas()
374 }
375 };
376
377 if self.parent.is_none() {
378 #[cfg(feature = "tracing")]
379 if self.modifiers.log_mask.contains(VmLogMask::DUMP_C5)
380 && let Some(committed) = &self.committed_state
381 {
382 vm_log_c5!(committed.c5.as_ref());
383 }
384 break res;
385 }
386 }
387 }
388
389 fn run_inner(&mut self) -> i32 {
390 let mut res = 0;
391 while res == 0 {
392 let step_res = self.step();
393
394 #[cfg(feature = "tracing")]
395 if self.modifiers.log_mask.contains(VmLogMask::GAS_REMAINING) {
396 vm_log_gas_remaining!(self.gas.remaining());
397 }
398
399 #[cfg(feature = "tracing")]
400 if self.modifiers.log_mask.contains(VmLogMask::GAS_CONSUMED) {
401 vm_log_gas_consumed!(self.gas.consumed());
402 }
403
404 res = match step_res {
405 Ok(res) => res,
406 Err(e) if e.is_out_of_gas() => {
407 self.steps += 1;
408 self.throw_out_of_gas()
409 }
410 Err(e) => {
411 let exception = e.as_exception();
412 vm_log_trace!("handling exception {exception:?}: {e:?}");
413
414 self.steps += 1;
415 match self.throw_exception(exception as i32) {
416 Ok(res) => res,
417 Err(e) if e.is_out_of_gas() => {
418 self.steps += 1;
419 self.throw_out_of_gas()
420 }
421 Err(e) => {
422 vm_log_trace!("double exception {exception:?}: {e:?}");
423 return exception.as_exit_code();
424 }
425 }
426 }
427 };
428 }
429
430 if res | 1 == -1 && !self.try_commit() {
432 vm_log_trace!("automatic commit failed");
433 self.stack = SafeRc::new(Stack {
434 items: vec![Stack::make_zero()],
435 });
436 return VmException::CellOverflow.as_exit_code();
437 }
438
439 res
440 }
441
442 pub fn try_commit(&mut self) -> bool {
443 if let (Some(c4), Some(c5)) = (&self.cr.d[0], &self.cr.d[1])
444 && c4.level() == 0
445 && c5.level() == 0
446 && c4.repr_depth() <= Self::MAX_DATA_DEPTH
447 && c5.repr_depth() <= Self::MAX_DATA_DEPTH
448 {
449 self.committed_state = Some(CommittedState {
450 c4: c4.clone(),
451 c5: c5.clone(),
452 });
453 return true;
454 }
455
456 false
457 }
458
459 pub fn force_commit(&mut self) -> Result<(), Error> {
460 if self.try_commit() {
461 Ok(())
462 } else {
463 Err(Error::CellOverflow)
464 }
465 }
466
467 pub fn take_stack(&mut self) -> SafeRc<Stack> {
468 std::mem::replace(&mut self.stack, Self::EMPTY_STACK.with(SafeRc::clone))
469 }
470
471 pub fn ref_to_cont(&mut self, code: Cell) -> VmResult<RcCont> {
472 let code = self.gas.load_cell_as_slice(code, LoadMode::Full)?;
473 Ok(SafeRc::from(OrdCont::simple(code, self.cp.id())))
474 }
475
476 pub fn c1_envelope_if(&mut self, cond: bool, cont: RcCont, save: bool) -> RcCont {
477 if cond {
478 self.c1_envelope(cont, save)
479 } else {
480 cont
481 }
482 }
483
484 pub fn c1_envelope(&mut self, mut cont: RcCont, save: bool) -> RcCont {
485 if save {
486 if cont.get_control_data().is_none() {
487 let mut c = ArgContExt {
488 data: Default::default(),
489 ext: cont,
490 };
491 c.data.save.define_c0(&self.cr.c[0]);
492 c.data.save.define_c1(&self.cr.c[1]);
493
494 cont = SafeRc::from(c);
495 } else {
496 let cont = SafeRc::make_mut(&mut cont);
497 if let Some(data) = cont.get_control_data_mut() {
498 data.save.define_c0(&self.cr.c[0]);
499 data.save.define_c1(&self.cr.c[1]);
500 }
501 }
502 }
503 self.cr.c[1] = Some(cont.clone());
504 cont
505 }
506
507 pub fn c1_save_set(&mut self) {
508 let [c0, c1, ..] = &mut self.cr.c;
509
510 if let Some(c0) = c0 {
511 if c0.get_control_data().is_none() {
512 let mut c = ArgContExt {
513 data: Default::default(),
514 ext: c0.clone(),
515 };
516 c.data.save.define_c1(c1);
517 *c0 = SafeRc::from(c);
518 } else {
519 let c0 = SafeRc::make_mut(c0);
520 if let Some(data) = c0.get_control_data_mut() {
521 data.save.define_c1(c1);
522 }
523 }
524 }
525
526 c1.clone_from(c0);
527 }
528
529 pub fn extract_cc(
530 &mut self,
531 mode: SaveCr,
532 stack_copy: Option<u16>,
533 nargs: Option<u16>,
534 ) -> VmResult<RcCont> {
535 let new_stack = match stack_copy {
536 Some(0) => None,
537 Some(n) if (n as usize) != self.stack.depth() => {
538 let stack = ok!(SafeRc::make_mut(&mut self.stack)
539 .split_top(n as _)
540 .map(Some));
541 self.gas.try_consume_stack_gas(stack.as_ref())?;
542 stack
543 }
544 _ => Some(self.take_stack()),
545 };
546
547 let mut res = OrdCont {
548 code: std::mem::take(&mut self.code),
549 data: ControlData {
550 nargs,
551 stack: Some(self.take_stack()),
552 save: Default::default(),
553 cp: Some(self.cp.id()),
554 },
555 };
556 if let Some(new_stack) = new_stack {
557 self.stack = new_stack;
558 }
559
560 if mode.contains(SaveCr::C0) {
561 res.data.save.c[0] = self.cr.c[0].replace(self.quit0.clone().into_dyn_cont());
562 }
563 if mode.contains(SaveCr::C1) {
564 res.data.save.c[1] = self.cr.c[1].replace(self.quit1.clone().into_dyn_cont());
565 }
566 if mode.contains(SaveCr::C2) {
567 res.data.save.c[2] = self.cr.c[2].take();
568 }
569
570 Ok(SafeRc::from(res))
571 }
572
573 pub fn throw_exception(&mut self, n: i32) -> VmResult<i32> {
574 self.stack = SafeRc::new(Stack {
575 items: vec![Stack::make_zero(), SafeRc::new_dyn_value(BigInt::from(n))],
576 });
577 self.code = Default::default();
578 self.gas.try_consume_exception_gas()?;
579 let Some(c2) = self.cr.c[2].clone() else {
580 vm_bail!(InvalidOpcode);
581 };
582 self.jump(c2)
583 }
584
585 pub fn throw_exception_with_arg(&mut self, n: i32, arg: RcStackValue) -> VmResult<i32> {
586 self.stack = SafeRc::new(Stack {
587 items: vec![arg, SafeRc::new_dyn_value(BigInt::from(n))],
588 });
589 self.code = Default::default();
590 self.gas.try_consume_exception_gas()?;
591 let Some(c2) = self.cr.c[2].clone() else {
592 vm_bail!(InvalidOpcode);
593 };
594 self.jump(c2)
595 }
596
597 pub fn throw_out_of_gas(&mut self) -> i32 {
598 let consumed = self.gas.consumed();
599 vm_log_trace!(
600 "out of gas: consumed={consumed}, limit={}",
601 self.gas.limit(),
602 );
603 self.stack = SafeRc::new(Stack {
604 items: vec![SafeRc::new_dyn_value(BigInt::from(consumed))],
605 });
606
607 VmException::OutOfGas as u8 as i32
609 }
610
611 pub fn call(&mut self, cont: RcCont) -> VmResult<i32> {
612 if let Some(control_data) = cont.get_control_data() {
613 if control_data.save.c[0].is_some() {
614 return self.jump(cont);
616 }
617 if control_data.stack.is_some() || control_data.nargs.is_some() {
618 return self.call_ext(cont, None, None);
621 }
622 }
623
624 let mut ret = OrdCont::simple(std::mem::take(&mut self.code), self.cp.id());
626 ret.data.save.c[0] = self.cr.c[0].take();
627 self.cr.c[0] = Some(SafeRc::from(ret));
628
629 self.do_jump_to(cont)
631 }
632
633 pub fn call_ext(
634 &mut self,
635 mut cont: RcCont,
636 pass_args: Option<u16>,
637 ret_args: Option<u16>,
638 ) -> VmResult<i32> {
639 let (new_stack, c0) = if let Some(control_data) = cont.get_control_data() {
640 if control_data.save.c[0].is_some() {
641 return self.jump_ext(cont, pass_args);
643 }
644
645 let current_depth = self.stack.depth();
646 vm_ensure!(
647 pass_args.unwrap_or_default() as usize <= current_depth
648 && control_data.nargs.unwrap_or_default() as usize <= current_depth,
649 StackUnderflow(std::cmp::max(
650 pass_args.unwrap_or_default(),
651 control_data.nargs.unwrap_or_default()
652 ) as _)
653 );
654
655 if let Some(pass_args) = pass_args {
656 vm_ensure!(
657 control_data.nargs.unwrap_or_default() <= pass_args,
658 StackUnderflow(pass_args as _)
659 );
660 }
661
662 let old_c0 = self.cr.c[0].take();
663 self.cr.preclear(&control_data.save);
664
665 let (copy, skip) = match (pass_args, control_data.nargs) {
666 (Some(pass_args), Some(copy)) => (Some(copy as usize), (pass_args - copy) as usize),
667 (Some(pass_args), None) => (Some(pass_args as usize), 0),
668 _ => (None, 0),
669 };
670
671 let new_stack = match SafeRc::get_mut(&mut cont) {
672 Some(cont) => cont
673 .get_control_data_mut()
674 .and_then(|control_data| control_data.stack.take()),
675 None => cont
676 .get_control_data()
677 .and_then(|control_data| control_data.stack.clone()),
678 };
679
680 let new_stack = match new_stack {
681 Some(mut new_stack) if !new_stack.items.is_empty() => {
682 let copy = copy.unwrap_or(current_depth);
683
684 let current_stack = SafeRc::make_mut(&mut self.stack);
685 ok!(SafeRc::make_mut(&mut new_stack).move_from_stack(current_stack, copy));
686 ok!(current_stack.pop_many(skip));
687
688 self.gas.try_consume_stack_gas(Some(&new_stack))?;
689
690 new_stack
691 }
692 _ => {
693 if let Some(copy) = copy {
694 let new_stack =
695 ok!(SafeRc::make_mut(&mut self.stack).split_top_ext(copy, skip));
696
697 self.gas.try_consume_stack_gas(Some(&new_stack))?;
698
699 new_stack
700 } else {
701 self.take_stack()
702 }
703 }
704 };
705
706 (new_stack, old_c0)
707 } else {
708 let new_stack = if let Some(pass_args) = pass_args {
710 let new_stack = ok!(SafeRc::make_mut(&mut self.stack).split_top(pass_args as _));
711 self.gas.try_consume_stack_gas(Some(&new_stack))?;
712 new_stack
713 } else {
714 self.take_stack()
715 };
716
717 (new_stack, self.cr.c[0].take())
718 };
719
720 let mut ret = OrdCont {
722 code: std::mem::take(&mut self.code),
723 data: ControlData {
724 save: Default::default(),
725 nargs: ret_args,
726 stack: Some(std::mem::replace(&mut self.stack, new_stack)),
727 cp: Some(self.cp.id()),
728 },
729 };
730 ret.data.save.c[0] = c0;
731 self.cr.c[0] = Some(SafeRc::from(ret));
732
733 self.do_jump_to(cont)
734 }
735
736 pub fn jump(&mut self, cont: RcCont) -> VmResult<i32> {
737 if let Some(cont_data) = cont.get_control_data()
738 && (cont_data.stack.is_some() || cont_data.nargs.is_some())
739 {
740 return self.jump_ext(cont, None);
742 }
743
744 self.do_jump_to(cont)
750 }
751
752 pub fn jump_ext(&mut self, cont: RcCont, pass_args: Option<u16>) -> VmResult<i32> {
753 let cont = ok!(self.adjust_jump_cont(cont, pass_args));
757
758 self.do_jump_to(cont)
760 }
761
762 fn adjust_jump_cont(&mut self, mut cont: RcCont, pass_args: Option<u16>) -> VmResult<RcCont> {
763 if let Some(control_data) = cont.get_control_data() {
764 let current_depth = self.stack.depth();
779 vm_ensure!(
780 pass_args.unwrap_or_default() as usize <= current_depth
781 && control_data.nargs.unwrap_or_default() as usize <= current_depth,
782 StackUnderflow(std::cmp::max(
783 pass_args.unwrap_or_default(),
784 control_data.nargs.unwrap_or_default()
785 ) as usize)
786 );
787
788 if let Some(pass_args) = pass_args {
789 vm_ensure!(
790 control_data.nargs.unwrap_or_default() <= pass_args,
791 StackUnderflow(pass_args as usize)
792 );
793 }
794
795 self.preclear_cr(&control_data.save);
797
798 let next_depth = control_data
800 .nargs
801 .or(pass_args)
802 .map(|n| n as usize)
803 .unwrap_or(current_depth);
804
805 let cont_stack = match SafeRc::get_mut(&mut cont) {
807 None => cont
808 .get_control_data()
809 .and_then(|control_data| control_data.stack.clone()),
810 Some(cont) => cont
811 .get_control_data_mut()
812 .and_then(|control_data| control_data.stack.take()),
813 };
814
815 match cont_stack {
816 Some(mut cont_stack) if !cont_stack.items.is_empty() => {
818 ok!(SafeRc::make_mut(&mut cont_stack)
820 .move_from_stack(SafeRc::make_mut(&mut self.stack), next_depth));
821 self.gas.try_consume_stack_gas(Some(&cont_stack))?;
822
823 self.stack = cont_stack;
824 }
825 _ if next_depth < current_depth => {
827 ok!(SafeRc::make_mut(&mut self.stack).drop_bottom(current_depth - next_depth));
828 self.gas.try_consume_stack_depth_gas(next_depth as _)?;
829 }
830 _ => {}
832 }
833 } else if let Some(pass_args) = pass_args {
834 let Some(depth_diff) = self.stack.depth().checked_sub(pass_args as _) else {
836 vm_bail!(StackUnderflow(pass_args as _));
837 };
838
839 if depth_diff > 0 {
840 ok!(SafeRc::make_mut(&mut self.stack).drop_bottom(depth_diff));
842 self.gas.try_consume_stack_depth_gas(pass_args as _)?;
843 }
844 }
845
846 Ok(cont)
847 }
848
849 fn do_jump_to(&mut self, mut cont: RcCont) -> VmResult<i32> {
850 let mut exit_code = 0;
851 let mut count = 0;
852 while let Some(next) = ok!(SafeRc::into_inner(cont).jump(self, &mut exit_code)) {
853 cont = next;
854 count += 1;
855
856 if count > GasConsumer::FREE_NESTED_CONT_JUMP {
858 self.gas.try_consume(1)?;
859 }
860
861 if let Some(cont_data) = cont.get_control_data()
862 && (cont_data.stack.is_some() || cont_data.nargs.is_some())
863 {
864 cont = ok!(self.adjust_jump_cont(cont, None));
866 }
867 }
868
869 Ok(exit_code)
870 }
871
872 pub fn ret(&mut self) -> VmResult<i32> {
873 let cont = ok!(self.take_c0());
874 self.jump(cont)
875 }
876
877 pub fn ret_ext(&mut self, ret_args: Option<u16>) -> VmResult<i32> {
878 let cont = ok!(self.take_c0());
879 self.jump_ext(cont, ret_args)
880 }
881
882 pub fn ret_alt(&mut self) -> VmResult<i32> {
883 let cont = ok!(self.take_c1());
884 self.jump(cont)
885 }
886
887 pub fn ret_alt_ext(&mut self, ret_args: Option<u16>) -> VmResult<i32> {
888 let cont = ok!(self.take_c1());
889 self.jump_ext(cont, ret_args)
890 }
891
892 pub fn repeat(&mut self, body: RcCont, after: RcCont, n: u32) -> VmResult<i32> {
893 self.jump(if n == 0 {
894 drop(body);
895 after
896 } else {
897 SafeRc::from(RepeatCont {
898 count: n as _,
899 body,
900 after,
901 })
902 })
903 }
904
905 pub fn until(&mut self, body: RcCont, after: RcCont) -> VmResult<i32> {
906 if !body.has_c0() {
907 self.cr.c[0] = Some(SafeRc::from(UntilCont {
908 body: body.clone(),
909 after,
910 }))
911 }
912 self.jump(body)
913 }
914
915 pub fn loop_while(&mut self, cond: RcCont, body: RcCont, after: RcCont) -> VmResult<i32> {
916 if !cond.has_c0() {
917 self.cr.c[0] = Some(SafeRc::from(WhileCont {
918 check_cond: true,
919 cond: cond.clone(),
920 body,
921 after,
922 }));
923 }
924 self.jump(cond)
925 }
926
927 pub fn again(&mut self, body: RcCont) -> VmResult<i32> {
928 self.jump(SafeRc::from(AgainCont { body }))
929 }
930
931 pub fn adjust_cr(&mut self, save: &ControlRegs) {
932 self.cr.merge(save)
933 }
934
935 pub fn preclear_cr(&mut self, save: &ControlRegs) {
936 self.cr.preclear(save)
937 }
938
939 pub fn set_c0(&mut self, cont: RcCont) {
940 self.cr.c[0] = Some(cont);
941 }
942
943 pub fn set_code(&mut self, code: OwnedCellSlice, cp: u16) -> VmResult<()> {
944 self.code = code;
945 self.force_cp(cp)
946 }
947
948 pub fn force_cp(&mut self, cp: u16) -> VmResult<()> {
949 let Some(cp) = codepage(cp) else {
950 vm_bail!(InvalidOpcode);
951 };
952 self.cp = cp;
953 Ok(())
954 }
955
956 fn take_c0(&mut self) -> VmResult<RcCont> {
957 let Some(cont) = self.cr.c[0].replace(self.quit0.clone().into_dyn_cont()) else {
958 vm_bail!(InvalidOpcode);
959 };
960 Ok(cont)
961 }
962
963 fn take_c1(&mut self) -> VmResult<RcCont> {
964 let Some(cont) = self.cr.c[1].replace(self.quit1.clone().into_dyn_cont()) else {
965 vm_bail!(InvalidOpcode);
966 };
967 Ok(cont)
968 }
969
970 fn restore_parent(&mut self, mut res: i32) -> Result<(), OutOfGas> {
971 let Some(parent) = self.parent.take() else {
972 return Ok(());
973 };
974
975 let steps = self.steps;
976
977 self.code = parent.code;
979 let child_stack = std::mem::replace(&mut self.stack, parent.stack);
980 self.signature_domains = parent.signature_domains;
981 self.cr = parent.cr;
982 let child_committed_state =
983 std::mem::replace(&mut self.committed_state, parent.committed_state);
984 self.steps += parent.steps;
985 self.quit0 = parent.quit0;
986 self.quit1 = parent.quit1;
987 let child_gas = self.gas.restore(parent.gas);
988 self.cp = parent.cp;
989 self.parent = parent.parent;
990
991 vm_log_trace!(
992 "child vm finished: res={res}, steps={steps}, gas={}",
993 child_gas.gas_consumed
994 );
995
996 let amount = std::cmp::min(
1004 child_gas.gas_consumed,
1005 child_gas.gas_limit.saturating_add(1),
1006 );
1007 if self.gas.try_consume(amount).is_err() {
1008 return Err(OutOfGas);
1009 }
1010
1011 let stack = SafeRc::make_mut(&mut self.stack);
1012 let stack = &mut stack.items;
1013
1014 let returned_values = parent.return_values;
1015 let returned_values = if res == 0 || res == 1 {
1016 match returned_values {
1017 Some(n) if child_stack.depth() < n as usize => {
1018 res = VmException::StackUnderflow.as_exit_code();
1019 stack.push(Stack::make_zero());
1020 0
1021 }
1022 Some(n) => n as usize,
1023 None => child_stack.depth(),
1024 }
1025 } else {
1026 std::cmp::min(child_stack.depth(), 1)
1027 };
1028
1029 let gas = &mut self.gas;
1030 if gas.try_consume_stack_depth_gas(returned_values).is_err() {
1031 return Err(OutOfGas);
1032 }
1033
1034 stack.extend_from_slice(&child_stack.items[child_stack.depth() - returned_values..]);
1035
1036 stack.push(SafeRc::new_dyn_value(BigInt::from(res)));
1037
1038 let (committed_c4, committed_c5) = match child_committed_state {
1039 Some(CommittedState { c4, c5 }) => (Some(c4), Some(c5)),
1040 None => (None, None),
1041 };
1042 if parent.return_data {
1043 stack.push(match committed_c4 {
1044 None => Stack::make_null(),
1045 Some(cell) => SafeRc::new_dyn_value(cell),
1046 })
1047 }
1048 if parent.return_actions {
1049 stack.push(match committed_c5 {
1050 None => Stack::make_null(),
1051 Some(cell) => SafeRc::new_dyn_value(cell),
1052 })
1053 }
1054 if parent.return_gas {
1055 stack.push(SafeRc::new_dyn_value(BigInt::from(child_gas.gas_consumed)));
1056 }
1057
1058 Ok(())
1059 }
1060}
1061
1062struct OutOfGas;
1063
1064#[derive(Default, Debug, Clone, Copy)]
1066pub struct BehaviourModifiers {
1067 pub stop_on_accept: bool,
1068 pub chksig_always_succeed: bool,
1069 pub enable_signature_domains: bool,
1070 pub signature_with_id: Option<i32>,
1071 #[cfg(feature = "tracing")]
1072 pub log_mask: VmLogMask,
1073}
1074
1075#[cfg(feature = "tracing")]
1076bitflags! {
1077 #[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
1079 pub struct VmLogMask: u8 {
1080 const MESSAGE = 1 << 0;
1081 const DUMP_STACK = 1 << 1;
1082 const EXEC_LOCATION = 1 << 2;
1083 const GAS_REMAINING = 1 << 3;
1084 const GAS_CONSUMED = 1 << 4;
1085 const DUMP_STACK_VERBOSE = 1 << 5;
1086 const DUMP_C5 = 1 << 6;
1087
1088 const FULL = 0b111111;
1089 }
1090}
1091
1092pub struct CommittedState {
1094 pub c4: Cell,
1096 pub c5: Cell,
1098}
1099
1100bitflags! {
1101 pub struct SaveCr: u8 {
1103 const NONE = 0;
1104
1105 const C0 = 1;
1106 const C1 = 1 << 1;
1107 const C2 = 1 << 2;
1108
1109 const C0_C1 = SaveCr::C0.bits() | SaveCr::C1.bits();
1110 const FULL = SaveCr::C0_C1.bits() | SaveCr::C2.bits();
1111 }
1112}
1113
1114thread_local! {
1115 pub(crate) static QUIT0: SafeRc<QuitCont> = SafeRc::new(QuitCont { exit_code: 0 });
1116 pub(crate) static QUIT1: SafeRc<QuitCont> = SafeRc::new(QuitCont { exit_code: 1 });
1117 pub(crate) static QUIT11: SafeRc<QuitCont> = SafeRc::new(QuitCont { exit_code: 11 });
1118 pub(crate) static EXC_QUIT: SafeRc<ExcQuitCont> = SafeRc::new(ExcQuitCont);
1119}