leo_interpreter/
cursor.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use super::*;
18
19use leo_ast::{
20    ArrayType,
21    AssertVariant,
22    AsyncExpression,
23    BinaryOperation,
24    Block,
25    CoreConstant,
26    CoreFunction,
27    DefinitionPlace,
28    Expression,
29    Function,
30    Location,
31    NodeID,
32    Statement,
33    StructVariableInitializer,
34    Type,
35    UnaryOperation,
36    Variant,
37    interpreter_value::{
38        AsyncExecution,
39        CoreFunctionHelper,
40        Value,
41        evaluate_binary,
42        evaluate_core_function,
43        evaluate_unary,
44        literal_to_value,
45    },
46};
47use leo_errors::{InterpreterHalt, Result};
48use leo_span::{Span, Symbol, sym};
49
50use snarkvm::prelude::{
51    Address,
52    CanaryV0,
53    Closure as SvmClosure,
54    Finalize as SvmFinalize,
55    Function as SvmFunctionParam,
56    MainnetV0,
57    Network,
58    PrivateKey,
59    ProgramID,
60    TestnetV0,
61};
62
63use indexmap::IndexMap;
64use itertools::Itertools;
65use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
66use std::{cmp::Ordering, collections::HashMap, mem, str::FromStr as _};
67
68pub type Closure = SvmClosure<TestnetV0>;
69pub type Finalize = SvmFinalize<TestnetV0>;
70pub type SvmFunction = SvmFunctionParam<TestnetV0>;
71
72/// Names associated to values in a function being executed.
73#[derive(Clone, Debug)]
74pub struct FunctionContext {
75    path: Vec<Symbol>,
76    program: Symbol,
77    pub caller: Value,
78    names: HashMap<Vec<Symbol>, Value>,
79    accumulated_futures: Vec<AsyncExecution>,
80    is_async: bool,
81}
82
83/// A stack of contexts, building with the function call stack.
84#[derive(Clone, Debug, Default)]
85pub struct ContextStack {
86    contexts: Vec<FunctionContext>,
87    current_len: usize,
88}
89
90impl ContextStack {
91    fn len(&self) -> usize {
92        self.current_len
93    }
94
95    fn push(
96        &mut self,
97        path: &[Symbol],
98        program: Symbol,
99        caller: Value,
100        is_async: bool,
101        names: HashMap<Vec<Symbol>, Value>, // a map of variable names that are already known
102    ) {
103        if self.current_len == self.contexts.len() {
104            self.contexts.push(FunctionContext {
105                path: path.to_vec(),
106                program,
107                caller: caller.clone(),
108                names: HashMap::new(),
109                accumulated_futures: Default::default(),
110                is_async,
111            });
112        }
113
114        self.contexts[self.current_len].path = path.to_vec();
115        self.contexts[self.current_len].program = program;
116        self.contexts[self.current_len].caller = caller;
117        self.contexts[self.current_len].names = names;
118        self.contexts[self.current_len].accumulated_futures.clear();
119        self.contexts[self.current_len].is_async = is_async;
120        self.current_len += 1;
121    }
122
123    pub fn pop(&mut self) {
124        // We never actually pop the underlying Vec
125        // so we can reuse the storage of the hash
126        // tables.
127        assert!(self.len() > 0);
128        self.current_len -= 1;
129        self.contexts[self.current_len].names.clear();
130    }
131
132    /// Get the future accumulated by awaiting futures in the current function call.
133    ///
134    /// If the current code being interpreted is not in an async function, this
135    /// will of course be empty.
136    fn get_future(&mut self) -> Vec<AsyncExecution> {
137        assert!(self.len() > 0);
138        mem::take(&mut self.contexts[self.current_len - 1].accumulated_futures)
139    }
140
141    fn set(&mut self, path: &[Symbol], value: Value) {
142        assert!(self.current_len > 0);
143        self.last_mut().unwrap().names.insert(path.to_vec(), value);
144    }
145
146    pub fn add_future(&mut self, future: Vec<AsyncExecution>) {
147        assert!(self.current_len > 0);
148        self.contexts[self.current_len - 1].accumulated_futures.extend(future);
149    }
150
151    /// Are we currently in an async function?
152    fn is_async(&self) -> bool {
153        assert!(self.current_len > 0);
154        self.last().unwrap().is_async
155    }
156
157    pub fn current_program(&self) -> Option<Symbol> {
158        self.last().map(|c| c.program)
159    }
160
161    pub fn last(&self) -> Option<&FunctionContext> {
162        self.len().checked_sub(1).and_then(|i| self.contexts.get(i))
163    }
164
165    fn last_mut(&mut self) -> Option<&mut FunctionContext> {
166        self.len().checked_sub(1).and_then(|i| self.contexts.get_mut(i))
167    }
168}
169
170#[derive(Clone, Debug)]
171pub enum AleoContext {
172    Closure(Closure),
173    Function(SvmFunction),
174    Finalize(Finalize),
175}
176
177/// A Leo construct to be evauated.
178#[derive(Clone, Debug)]
179pub enum Element {
180    /// A Leo statement.
181    Statement(Statement),
182
183    /// A Leo expression. The optional type is an optional "expected type" for the expression. It helps when trying to
184    /// resolve an unsuffixed literal.
185    Expression(Expression, Option<Type>),
186
187    /// A Leo block.
188    ///
189    /// We have a separate variant for Leo blocks for two reasons:
190    /// 1. In a ConditionalExpression, the `then` block is stored
191    ///    as just a Block with no statement, and
192    /// 2. We need to remember if a Block came from a function body,
193    ///    so that if such a block ends, we know to push a `Unit` to
194    ///    the values stack.
195    Block {
196        block: Block,
197        function_body: bool,
198    },
199
200    AleoExecution {
201        context: Box<AleoContext>,
202        registers: IndexMap<u64, Value>,
203        instruction_index: usize,
204    },
205
206    DelayedCall(Location),
207    DelayedAsyncBlock {
208        program: Symbol,
209        block: NodeID,
210        names: HashMap<Vec<Symbol>, Value>,
211    },
212}
213
214impl Element {
215    pub fn span(&self) -> Span {
216        use Element::*;
217        match self {
218            Statement(statement) => statement.span(),
219            Expression(expression, _) => expression.span(),
220            Block { block, .. } => block.span(),
221            AleoExecution { .. } | DelayedCall(..) | DelayedAsyncBlock { .. } => Default::default(),
222        }
223    }
224}
225
226/// A frame of execution, keeping track of the Element next to
227/// be executed and the number of steps we've done so far.
228#[derive(Clone, Debug)]
229pub struct Frame {
230    pub step: usize,
231    pub element: Element,
232    pub user_initiated: bool,
233}
234
235#[derive(Clone, Debug)]
236pub enum FunctionVariant {
237    Leo(Function),
238    AleoClosure(Closure),
239    AleoFunction(SvmFunction),
240}
241
242/// Tracks the current execution state - a cursor into the running program.
243#[derive(Clone, Debug)]
244pub struct Cursor {
245    /// Stack of execution frames, with the one currently to be executed on top.
246    pub frames: Vec<Frame>,
247
248    /// Stack of values from evaluated expressions.
249    ///
250    /// Each time an expression completes evaluation, a value is pushed here.
251    pub values: Vec<Value>,
252
253    /// All functions (or transitions or inlines) in any program being interpreted.
254    pub functions: HashMap<Location, FunctionVariant>,
255
256    /// All the async blocks encountered. We identify them by their `NodeID`.
257    pub async_blocks: HashMap<NodeID, Block>,
258
259    /// Consts are stored here.
260    pub globals: HashMap<Location, Value>,
261
262    pub user_values: HashMap<Vec<Symbol>, Value>,
263
264    pub mappings: HashMap<Location, HashMap<Value, Value>>,
265
266    /// For each struct type, we only need to remember the names of its members, in order.
267    pub structs: HashMap<Vec<Symbol>, IndexMap<Symbol, Type>>,
268
269    /// For each record type, we index by program name and path, and remember its members
270    /// except `owner`.
271    pub records: HashMap<(Symbol, Vec<Symbol>), IndexMap<Symbol, Type>>,
272
273    pub futures: Vec<AsyncExecution>,
274
275    pub contexts: ContextStack,
276
277    // The signer's address.
278    pub signer: Value,
279
280    pub rng: ChaCha20Rng,
281
282    pub block_height: u32,
283
284    pub really_async: bool,
285
286    pub program: Option<Symbol>,
287
288    pub network: NetworkName,
289
290    pub private_key: String,
291}
292
293impl CoreFunctionHelper for Cursor {
294    fn pop_value_impl(&mut self) -> Option<Value> {
295        self.values.pop()
296    }
297
298    fn set_block_height(&mut self, height: u32) {
299        self.block_height = height;
300    }
301
302    fn set_signer(&mut self, private_key: String) -> Result<()> {
303        // Get the address from the private key.
304        let address = match PrivateKey::<TestnetV0>::from_str(&private_key.replace("\"", ""))
305            .and_then(|pk| Address::<TestnetV0>::try_from(&pk))
306        {
307            Ok(address) => address.into(),
308            Err(_) => halt_no_span!("Invalid private key provided for signer."),
309        };
310        // Set the private key
311        self.private_key = private_key;
312        // Set the signer.
313        self.signer = address;
314
315        Ok(())
316    }
317
318    fn lookup_mapping(&self, program: Option<Symbol>, name: Symbol) -> Option<&HashMap<Value, Value>> {
319        Cursor::lookup_mapping(self, program, name)
320    }
321
322    fn lookup_mapping_mut(&mut self, program: Option<Symbol>, name: Symbol) -> Option<&mut HashMap<Value, Value>> {
323        Cursor::lookup_mapping_mut(self, program, name)
324    }
325
326    fn rng(&mut self) -> Option<&mut ChaCha20Rng> {
327        Some(&mut self.rng)
328    }
329}
330
331impl Cursor {
332    /// `really_async` indicates we should really delay execution of async function calls until the user runs them.
333    pub fn new(really_async: bool, private_key: String, block_height: u32, network: NetworkName) -> Self {
334        let mut cursor = Cursor {
335            frames: Default::default(),
336            values: Default::default(),
337            functions: Default::default(),
338            async_blocks: Default::default(),
339            globals: Default::default(),
340            user_values: Default::default(),
341            mappings: Default::default(),
342            structs: Default::default(),
343            records: Default::default(),
344            contexts: Default::default(),
345            futures: Default::default(),
346            rng: ChaCha20Rng::from_entropy(),
347            signer: Default::default(),
348            block_height,
349            really_async,
350            program: None,
351            network,
352            private_key: Default::default(),
353        };
354
355        // Set the default private key.
356        cursor.set_signer(private_key).expect("The default private key should be valid.");
357
358        cursor
359    }
360
361    // Clears the state of the cursor, but keeps the program definitions.
362    pub fn clear(&mut self) {
363        self.frames.clear();
364        self.values.clear();
365        self.mappings.iter_mut().for_each(|(_, map)| map.clear());
366        self.contexts = Default::default();
367        self.futures.clear();
368    }
369
370    fn set_place(
371        new_value: Value,
372        this_value: &mut Value,
373        places: &mut dyn Iterator<Item = &Expression>,
374        indices: &mut dyn Iterator<Item = Value>,
375    ) -> Result<()> {
376        match places.next() {
377            None => *this_value = new_value,
378            Some(Expression::ArrayAccess(_access)) => {
379                let index = indices.next().unwrap();
380                let index = index.as_u32().unwrap() as usize;
381
382                let mut index_value = this_value.array_index(index).expect("Type");
383                Self::set_place(new_value, &mut index_value, places, indices)?;
384
385                if this_value.array_index_set(index, index_value).is_none() {
386                    halt_no_span!("Invalid array assignment");
387                }
388            }
389            Some(Expression::TupleAccess(access)) => {
390                let index = access.index.value();
391                let mut index_value = this_value.tuple_index(index).expect("Type");
392                Self::set_place(new_value, &mut index_value, places, indices)?;
393                if this_value.tuple_index_set(index, index_value).is_none() {
394                    halt_no_span!("Invalid tuple assignment");
395                }
396            }
397            Some(Expression::MemberAccess(access)) => {
398                let mut access_value = this_value.member_access(access.name.name).expect("Type");
399                Self::set_place(new_value, &mut access_value, places, indices)?;
400                if this_value.member_set(access.name.name, access_value).is_none() {
401                    halt_no_span!("Invalid member set");
402                }
403            }
404            Some(Expression::Path(_path)) => {
405                Self::set_place(new_value, this_value, places, indices)?;
406            }
407            Some(place) => halt_no_span!("Invalid place for assignment: {place}"),
408        }
409
410        Ok(())
411    }
412
413    pub fn assign(&mut self, value: Value, place: &Expression, indices: &mut dyn Iterator<Item = Value>) -> Result<()> {
414        let mut places = vec![place];
415        let indices: Vec<Value> = indices.collect();
416
417        let path: &Path;
418
419        loop {
420            match places.last().unwrap() {
421                Expression::ArrayAccess(access) => places.push(&access.array),
422                Expression::TupleAccess(access) => places.push(&access.tuple),
423                Expression::MemberAccess(access) => places.push(&access.inner),
424                Expression::Path(path_) => {
425                    path = path_;
426                    break;
427                }
428                place @ (Expression::AssociatedConstant(..)
429                | Expression::AssociatedFunction(..)
430                | Expression::Async(..)
431                | Expression::Array(..)
432                | Expression::Binary(..)
433                | Expression::Call(..)
434                | Expression::Cast(..)
435                | Expression::Err(..)
436                | Expression::Literal(..)
437                | Expression::Locator(..)
438                | Expression::Repeat(..)
439                | Expression::Struct(..)
440                | Expression::Ternary(..)
441                | Expression::Tuple(..)
442                | Expression::Unary(..)
443                | Expression::Unit(..)) => halt_no_span!("Invalid place for assignment: {place}"),
444            }
445        }
446
447        let full_name = self.to_absolute_path(&path.as_symbols());
448
449        let mut leo_value = self.lookup(&full_name).unwrap_or(Value::make_unit());
450
451        // Do an ad hoc evaluation of the lhs of the assignment to determine its type.
452        let mut temp_value = leo_value.clone();
453        let mut indices_iter = indices.iter();
454
455        for place in places.iter().rev() {
456            match place {
457                Expression::ArrayAccess(_access) => {
458                    let next_index = indices_iter.next().unwrap();
459                    temp_value = temp_value.array_index(next_index.as_u32().unwrap() as usize).unwrap();
460                }
461                Expression::TupleAccess(access) => {
462                    temp_value = temp_value.tuple_index(access.index.value()).unwrap();
463                }
464                Expression::MemberAccess(access) => {
465                    temp_value = temp_value.member_access(access.name.name).unwrap();
466                }
467                Expression::Path(_path) =>
468                    // temp_value is already set to leo_value
469                    {}
470                _ => panic!("Can't happen."),
471            }
472        }
473
474        let ty = temp_value.get_numeric_type();
475        let value = value.resolve_if_unsuffixed(&ty, place.span())?;
476
477        Self::set_place(value, &mut leo_value, &mut places.into_iter().rev(), &mut indices.into_iter())?;
478        self.set_variable(&full_name, leo_value);
479        Ok(())
480    }
481
482    pub fn set_program(&mut self, program: &str) {
483        let p = program.strip_suffix(".aleo").unwrap_or(program);
484        self.program = Some(Symbol::intern(p));
485    }
486
487    pub fn current_program(&self) -> Option<Symbol> {
488        self.contexts.current_program().or(self.program)
489    }
490
491    pub fn increment_step(&mut self) {
492        let Some(Frame { step, .. }) = self.frames.last_mut() else {
493            panic!("frame expected");
494        };
495        *step += 1;
496    }
497
498    fn new_caller(&self) -> Value {
499        if let Some(function_context) = self.contexts.last() {
500            let program_id = ProgramID::<TestnetV0>::from_str(&format!("{}.aleo", function_context.program))
501                .expect("should be able to create ProgramID");
502            program_id.to_address().expect("should be able to convert to address").into()
503        } else {
504            self.signer.clone()
505        }
506    }
507
508    fn pop_value(&mut self) -> Result<Value> {
509        match self.values.pop() {
510            Some(v) => Ok(v),
511            None => {
512                Err(InterpreterHalt::new("value expected - this may be a bug in the Leo interpreter".to_string())
513                    .into())
514            }
515        }
516    }
517
518    fn lookup(&self, name: &[Symbol]) -> Option<Value> {
519        if let Some(context) = self.contexts.last() {
520            let option_value =
521                context.names.get(name).or_else(|| self.globals.get(&Location::new(context.program, name.to_vec())));
522            if option_value.is_some() {
523                return option_value.cloned();
524            }
525        };
526
527        self.user_values.get(name).cloned()
528    }
529
530    pub fn lookup_mapping(&self, program: Option<Symbol>, name: Symbol) -> Option<&HashMap<Value, Value>> {
531        let Some(program) = program.or_else(|| self.current_program()) else {
532            panic!("no program for mapping lookup");
533        };
534        // mappings can only show up in the top level program scope
535        self.mappings.get(&Location::new(program, vec![name]))
536    }
537
538    pub fn lookup_mapping_mut(&mut self, program: Option<Symbol>, name: Symbol) -> Option<&mut HashMap<Value, Value>> {
539        let Some(program) = program.or_else(|| self.current_program()) else {
540            panic!("no program for mapping lookup");
541        };
542        // mappings can only show up in the top level program scope
543        self.mappings.get_mut(&Location::new(program, vec![name]))
544    }
545
546    fn lookup_function(&self, program: Symbol, name: &[Symbol]) -> Option<FunctionVariant> {
547        self.functions.get(&Location::new(program, name.to_vec())).cloned()
548    }
549
550    fn set_variable(&mut self, path: &[Symbol], value: Value) {
551        if self.contexts.len() > 0 {
552            self.contexts.set(path, value);
553        } else {
554            self.user_values.insert(path.to_vec(), value);
555        }
556    }
557
558    /// Execute the whole step of the current Element.
559    ///
560    /// That is, perform a step, and then finish all statements and expressions that have been pushed,
561    /// until we're ready for the next step of the current Element (if there is one).
562    pub fn whole_step(&mut self) -> Result<StepResult> {
563        let frames_len = self.frames.len();
564        let initial_result = self.step()?;
565        if !initial_result.finished {
566            while self.frames.len() > frames_len {
567                self.step()?;
568            }
569        }
570        Ok(initial_result)
571    }
572
573    /// Step `over` the current Element.
574    ///
575    /// That is, continue executing until the current Element is finished.
576    pub fn over(&mut self) -> Result<StepResult> {
577        let frames_len = self.frames.len();
578        loop {
579            match self.frames.len().cmp(&frames_len) {
580                Ordering::Greater => {
581                    self.step()?;
582                }
583                Ordering::Equal => {
584                    let result = self.step()?;
585                    if result.finished {
586                        return Ok(result);
587                    }
588                }
589                Ordering::Less => {
590                    // This can happen if, for instance, a `return` was encountered,
591                    // which means we exited the function we were evaluating and the
592                    // frame stack was truncated.
593                    return Ok(StepResult { finished: true, value: None });
594                }
595            }
596        }
597    }
598
599    pub fn step_block(&mut self, block: &Block, function_body: bool, step: usize) -> bool {
600        let len = self.frames.len();
601
602        let done = match step {
603            0 => {
604                for statement in block.statements.iter().rev() {
605                    self.frames.push(Frame {
606                        element: Element::Statement(statement.clone()),
607                        step: 0,
608                        user_initiated: false,
609                    });
610                }
611                false
612            }
613            1 if function_body => {
614                self.values.push(Value::make_unit());
615                self.contexts.pop();
616                true
617            }
618            1 => true,
619            _ => unreachable!(),
620        };
621
622        if done {
623            assert_eq!(len, self.frames.len());
624            self.frames.pop();
625        } else {
626            self.frames[len - 1].step += 1;
627        }
628
629        done
630    }
631
632    /// Returns the full absolute path by prefixing `name` with the current module path.
633    /// If no context is available, returns `name` as-is.
634    fn to_absolute_path(&self, name: &[Symbol]) -> Vec<Symbol> {
635        if let Some(context) = self.contexts.last() {
636            let mut full_name = context.path.clone();
637            full_name.pop(); // This pops the function name, keeping only the module prefix 
638            full_name.extend(name);
639            full_name
640        } else {
641            name.to_vec()
642        }
643    }
644
645    fn step_statement(&mut self, statement: &Statement, step: usize) -> Result<bool> {
646        let len = self.frames.len();
647        // Push a new expression frame with an optional expected type for the expression
648        let mut push = |expression: &Expression, ty: &Option<Type>| {
649            self.frames.push(Frame {
650                element: Element::Expression(expression.clone(), ty.clone()),
651                step: 0,
652                user_initiated: false,
653            })
654        };
655
656        let done = match statement {
657            Statement::Assert(assert) if step == 0 => {
658                match &assert.variant {
659                    AssertVariant::Assert(x) => push(x, &Some(Type::Boolean)),
660                    AssertVariant::AssertEq(x, y) | AssertVariant::AssertNeq(x, y) => {
661                        push(y, &None);
662                        push(x, &None);
663                    }
664                };
665                false
666            }
667            Statement::Assert(assert) if step == 1 => {
668                match &assert.variant {
669                    AssertVariant::Assert(..) => {
670                        let value = self.pop_value()?;
671                        match value.try_into() {
672                            Ok(true) => {}
673                            Ok(false) => halt!(assert.span(), "assert failure"),
674                            _ => tc_fail!(),
675                        }
676                    }
677                    AssertVariant::AssertEq(..) => {
678                        let x = self.pop_value()?;
679                        let y = self.pop_value()?;
680                        if !x.eq(&y)? {
681                            halt!(assert.span(), "assert failure: {} != {}", y, x);
682                        }
683                    }
684
685                    AssertVariant::AssertNeq(..) => {
686                        let x = self.pop_value()?;
687                        let y = self.pop_value()?;
688                        if x.eq(&y)? {
689                            halt!(assert.span(), "assert failure: {} == {}", y, x);
690                        }
691                    }
692                };
693                true
694            }
695            Statement::Assign(assign) if step == 0 => {
696                // Step 0: push the expression frame and any array index expression frames.
697                push(&assign.value, &None);
698                let mut place = &assign.place;
699                loop {
700                    match place {
701                        leo_ast::Expression::ArrayAccess(access) => {
702                            push(&access.index, &None);
703                            place = &access.array;
704                        }
705                        leo_ast::Expression::Path(..) => break,
706                        leo_ast::Expression::MemberAccess(access) => {
707                            place = &access.inner;
708                        }
709                        leo_ast::Expression::TupleAccess(access) => {
710                            place = &access.tuple;
711                        }
712                        _ => panic!("Can't happen"),
713                    }
714                }
715                false
716            }
717            Statement::Assign(assign) if step == 1 => {
718                // Step 1: set the variable (or place).
719                let mut index_count = 0;
720                let mut place = &assign.place;
721                loop {
722                    match place {
723                        leo_ast::Expression::ArrayAccess(access) => {
724                            index_count += 1;
725                            place = &access.array;
726                        }
727                        leo_ast::Expression::Path(..) => break,
728                        leo_ast::Expression::MemberAccess(access) => {
729                            place = &access.inner;
730                        }
731                        leo_ast::Expression::TupleAccess(access) => {
732                            place = &access.tuple;
733                        }
734                        _ => panic!("Can't happen"),
735                    }
736                }
737
738                // Get the value.
739                let value = self.pop_value()?;
740                let len = self.values.len();
741
742                // Get the indices.
743                let indices: Vec<Value> = self.values.drain(len - index_count..len).collect();
744
745                self.assign(value, &assign.place, &mut indices.into_iter())?;
746
747                true
748            }
749            Statement::Block(block) => return Ok(self.step_block(block, false, step)),
750            Statement::Conditional(conditional) if step == 0 => {
751                push(&conditional.condition, &Some(Type::Boolean));
752                false
753            }
754            Statement::Conditional(conditional) if step == 1 => {
755                match self.pop_value()?.try_into() {
756                    Ok(true) => self.frames.push(Frame {
757                        step: 0,
758                        element: Element::Block { block: conditional.then.clone(), function_body: false },
759                        user_initiated: false,
760                    }),
761                    Ok(false) => {
762                        if let Some(otherwise) = conditional.otherwise.as_ref() {
763                            self.frames.push(Frame {
764                                step: 0,
765                                element: Element::Statement(Statement::clone(otherwise)),
766                                user_initiated: false,
767                            })
768                        }
769                    }
770                    _ => tc_fail!(),
771                };
772                false
773            }
774            Statement::Conditional(_) if step == 2 => true,
775            Statement::Const(const_) if step == 0 => {
776                push(&const_.value, &Some(const_.type_.clone()));
777                false
778            }
779            Statement::Const(const_) if step == 1 => {
780                let value = self.pop_value()?;
781                self.set_variable(&self.to_absolute_path(&[const_.place.name]), value);
782                true
783            }
784            Statement::Definition(definition) if step == 0 => {
785                push(&definition.value, &definition.type_);
786                false
787            }
788            Statement::Definition(definition) if step == 1 => {
789                let value = self.pop_value()?;
790                match &definition.place {
791                    DefinitionPlace::Single(id) => self.set_variable(&self.to_absolute_path(&[id.name]), value),
792                    DefinitionPlace::Multiple(ids) => {
793                        for (i, id) in ids.iter().enumerate() {
794                            self.set_variable(
795                                &self.to_absolute_path(&[id.name]),
796                                value.tuple_index(i).expect("Place for definition should be a tuple."),
797                            );
798                        }
799                    }
800                }
801                true
802            }
803            Statement::Expression(expression) if step == 0 => {
804                push(&expression.expression, &None);
805                false
806            }
807            Statement::Expression(_) if step == 1 => {
808                self.values.pop();
809                true
810            }
811            Statement::Iteration(iteration) if step == 0 => {
812                assert!(!iteration.inclusive);
813                push(&iteration.stop, &iteration.type_.clone());
814                push(&iteration.start, &iteration.type_.clone());
815                false
816            }
817            Statement::Iteration(iteration) => {
818                // Currently there actually isn't a syntax in Leo for inclusive ranges.
819                let stop = self.pop_value()?;
820                let start = self.pop_value()?;
821                if start.eq(&stop)? {
822                    true
823                } else {
824                    let new_start = start.inc_wrapping().expect_tc(iteration.span())?;
825                    self.set_variable(&self.to_absolute_path(&[iteration.variable.name]), start);
826                    self.frames.push(Frame {
827                        step: 0,
828                        element: Element::Block { block: iteration.block.clone(), function_body: false },
829                        user_initiated: false,
830                    });
831                    self.values.push(new_start);
832                    self.values.push(stop);
833                    false
834                }
835            }
836            Statement::Return(return_) if step == 0 => {
837                // We really only need to care about the type of the output for Leo functions. Aleo functions and
838                // closures don't have to worry about unsuffixed literals
839                let output_type = self.contexts.last().and_then(|ctx| {
840                    self.lookup_function(ctx.program, &ctx.path).and_then(|variant| match variant {
841                        FunctionVariant::Leo(function) => Some(function.output_type.clone()),
842                        _ => None,
843                    })
844                });
845
846                self.frames.push(Frame {
847                    element: Element::Expression(return_.expression.clone(), output_type),
848                    step: 0,
849                    user_initiated: false,
850                });
851
852                false
853            }
854            Statement::Return(_) if step == 1 => loop {
855                let last_frame = self.frames.last().expect("a frame should be present");
856                match last_frame.element {
857                    Element::Expression(Expression::Call(_), _) | Element::DelayedCall(_) => {
858                        if self.contexts.is_async() {
859                            // Get rid of the Unit we previously pushed, and replace it with a Future.
860                            self.values.pop();
861                            self.values.push(self.contexts.get_future().into());
862                        }
863                        self.contexts.pop();
864                        return Ok(true);
865                    }
866                    _ => {
867                        self.frames.pop();
868                    }
869                }
870            },
871            _ => unreachable!(),
872        };
873
874        if done {
875            assert_eq!(len, self.frames.len());
876            self.frames.pop();
877        } else {
878            self.frames[len - 1].step += 1;
879        }
880
881        Ok(done)
882    }
883
884    fn step_expression(&mut self, expression: &Expression, expected_ty: &Option<Type>, step: usize) -> Result<bool> {
885        let len = self.frames.len();
886
887        macro_rules! push {
888            () => {
889                |expression: &Expression, expected_ty: &Option<Type>| {
890                    self.frames.push(Frame {
891                        element: Element::Expression(expression.clone(), expected_ty.clone()),
892                        step: 0,
893                        user_initiated: false,
894                    })
895                }
896            };
897        }
898
899        if let Some(value) = match expression {
900            Expression::ArrayAccess(array) if step == 0 => {
901                push!()(&array.index, &None);
902                push!()(&array.array, &None);
903                None
904            }
905            Expression::ArrayAccess(array_expr) if step == 1 => {
906                let span = array_expr.span();
907                let index = self.pop_value()?;
908                let array = self.pop_value()?;
909
910                // Local helper function to convert a Value into usize
911                fn to_usize(value: &Value, span: Span) -> Result<usize> {
912                    let value = value.resolve_if_unsuffixed(&Some(Type::Integer(leo_ast::IntegerType::U32)), span)?;
913                    Ok(value.as_u32().expect_tc(span)? as usize)
914                }
915
916                let index_usize = to_usize(&index, span)?;
917
918                Some(array.array_index(index_usize).expect_tc(span)?)
919            }
920
921            Expression::Async(AsyncExpression { block, .. }) if step == 0 => {
922                // Keep track of the async block, but nothing else to do at this point
923                self.async_blocks.insert(block.id, block.clone());
924                None
925            }
926            Expression::Async(AsyncExpression { block, .. }) if step == 1 => {
927                // Keep track of this block as a `Future` containing an `AsyncExecution` but nothing else to do here.
928                // The block actually executes when an `await` is called on its future.
929                if let Some(context) = self.contexts.last() {
930                    let async_ex = AsyncExecution::AsyncBlock {
931                        containing_function: Location::new(context.program, context.path.clone()),
932                        block: block.id,
933                        names: context.names.clone().into_iter().collect(),
934                    };
935                    self.values.push(vec![async_ex].into());
936                }
937                None
938            }
939            Expression::Async(_) if step == 2 => Some(self.pop_value()?),
940
941            Expression::MemberAccess(access) => match &access.inner {
942                Expression::Path(path) if *path.as_symbols() == vec![sym::SelfLower] => match access.name.name {
943                    sym::signer => Some(self.signer.clone()),
944                    sym::caller => {
945                        if let Some(function_context) = self.contexts.last() {
946                            Some(function_context.caller.clone())
947                        } else {
948                            Some(self.signer.clone())
949                        }
950                    }
951                    sym::address => {
952                        // A helper function to convert a program ID string to an address value.
953                        fn program_to_address<N: Network>(program_id: &str) -> Result<Value> {
954                            let Ok(program_id) = ProgramID::<N>::from_str(&format!("{program_id}.aleo")) else {
955                                halt_no_span!("Failed to parse program ID");
956                            };
957                            let Ok(address) = program_id.to_address() else {
958                                halt_no_span!("Failed to convert program ID to address");
959                            };
960                            let Ok(value) = Value::from_str(&address.to_string()) else {
961                                halt_no_span!("Failed to convert address to value");
962                            };
963                            Ok(value)
964                        }
965                        // Get the current program.
966                        let Some(program) = self.current_program() else {
967                            halt_no_span!("No program context for address");
968                        };
969                        let result = match self.network {
970                            NetworkName::TestnetV0 => program_to_address::<TestnetV0>(&program.to_string())?,
971                            NetworkName::MainnetV0 => program_to_address::<MainnetV0>(&program.to_string())?,
972                            NetworkName::CanaryV0 => program_to_address::<CanaryV0>(&program.to_string())?,
973                        };
974                        Some(result)
975                    }
976                    _ => halt!(access.span(), "unknown member of self"),
977                },
978                Expression::Path(path) if *path.as_symbols() == vec![sym::block] => match access.name.name {
979                    sym::height => Some(self.block_height.into()),
980                    _ => halt!(access.span(), "unknown member of block"),
981                },
982
983                // Otherwise, we just have a normal struct member access.
984                _ if step == 0 => {
985                    push!()(&access.inner, &None);
986                    None
987                }
988                _ if step == 1 => {
989                    let struct_ = self.values.pop().expect_tc(access.span())?;
990                    let value = struct_.member_access(access.name.name).expect_tc(access.span())?;
991                    Some(value)
992                }
993                _ => unreachable!("we've actually covered all possible patterns above"),
994            },
995            Expression::TupleAccess(tuple_access) if step == 0 => {
996                push!()(&tuple_access.tuple, &None);
997                None
998            }
999            Expression::TupleAccess(tuple_access) if step == 1 => {
1000                let Some(value) = self.values.pop() else { tc_fail!() };
1001                if let Some(result) = value.tuple_index(tuple_access.index.value()) {
1002                    Some(result)
1003                } else {
1004                    halt!(tuple_access.span(), "Tuple index out of range");
1005                }
1006            }
1007            Expression::Array(array) if step == 0 => {
1008                let element_type = expected_ty.clone().and_then(|ty| match ty {
1009                    Type::Array(ArrayType { element_type, .. }) => Some(*element_type),
1010                    _ => None,
1011                });
1012
1013                array.elements.iter().rev().for_each(|array| push!()(array, &element_type));
1014                None
1015            }
1016            Expression::Array(array) if step == 1 => {
1017                let len = self.values.len();
1018                let array_values = self.values.drain(len - array.elements.len()..);
1019                Some(Value::make_array(array_values))
1020            }
1021            Expression::Repeat(repeat) if step == 0 => {
1022                let element_type = expected_ty.clone().and_then(|ty| match ty {
1023                    Type::Array(ArrayType { element_type, .. }) => Some(*element_type),
1024                    _ => None,
1025                });
1026
1027                push!()(&repeat.count, &None);
1028                push!()(&repeat.expr, &element_type);
1029                None
1030            }
1031            Expression::Repeat(repeat) if step == 1 => {
1032                let count = self.pop_value()?;
1033                let expr = self.pop_value()?;
1034                let count_resolved = count
1035                    .resolve_if_unsuffixed(&Some(Type::Integer(leo_ast::IntegerType::U32)), repeat.count.span())?;
1036                Some(Value::make_array(std::iter::repeat_n(
1037                    expr,
1038                    count_resolved.as_u32().expect_tc(repeat.span())? as usize,
1039                )))
1040            }
1041            Expression::AssociatedConstant(constant) if step == 0 => {
1042                let Type::Identifier(type_ident) = constant.ty else {
1043                    tc_fail!();
1044                };
1045                let Some(core_constant) = CoreConstant::from_symbols(type_ident.name, constant.name.name) else {
1046                    halt!(constant.span(), "Unknown constant {constant}");
1047                };
1048                match core_constant {
1049                    CoreConstant::GroupGenerator => Some(Value::generator()),
1050                }
1051            }
1052            Expression::AssociatedFunction(function) if step == 0 => {
1053                let Some(core_function) = CoreFunction::try_from(function).ok() else {
1054                    halt!(function.span(), "Unkown core function {function}");
1055                };
1056
1057                // We want to push expressions for each of the arguments... except for mappings,
1058                // because we don't look them up as Values.
1059                match core_function {
1060                    CoreFunction::Get | CoreFunction::MappingRemove | CoreFunction::MappingContains => {
1061                        push!()(&function.arguments[1], &None);
1062                    }
1063                    CoreFunction::MappingGetOrUse | CoreFunction::Set => {
1064                        push!()(&function.arguments[2], &None);
1065                        push!()(&function.arguments[1], &None);
1066                    }
1067                    CoreFunction::CheatCodePrintMapping => {
1068                        // Do nothing, as we don't need to evaluate the mapping.
1069                    }
1070                    _ => function.arguments.iter().rev().for_each(|arg| push!()(arg, &None)),
1071                }
1072                None
1073            }
1074            Expression::AssociatedFunction(function) if step == 1 => {
1075                let Some(core_function) = CoreFunction::try_from(function).ok() else {
1076                    halt!(function.span(), "Unkown core function {function}");
1077                };
1078
1079                let span = function.span();
1080
1081                if let CoreFunction::FutureAwait = core_function {
1082                    let value = self.pop_value()?;
1083                    let Some(asyncs) = value.as_future() else {
1084                        halt!(span, "Invalid value for await: {value}");
1085                    };
1086                    for async_execution in asyncs {
1087                        match async_execution {
1088                            AsyncExecution::AsyncFunctionCall { function, arguments } => {
1089                                self.values.extend(arguments.iter().cloned());
1090                                self.frames.push(Frame {
1091                                    step: 0,
1092                                    element: Element::DelayedCall(function.clone()),
1093                                    user_initiated: false,
1094                                });
1095                            }
1096                            AsyncExecution::AsyncBlock { containing_function, block, names, .. } => {
1097                                self.frames.push(Frame {
1098                                    step: 0,
1099                                    element: Element::DelayedAsyncBlock {
1100                                        program: containing_function.program,
1101                                        block: *block,
1102                                        // Keep track of all the known variables up to this point.
1103                                        // These are available to use inside the block when we actually execute it.
1104                                        names: names.clone().into_iter().collect(),
1105                                    },
1106                                    user_initiated: false,
1107                                });
1108                            }
1109                        }
1110                    }
1111                    // For an await, we have one extra step - first we must evaluate the delayed call.
1112                    None
1113                } else {
1114                    let value = evaluate_core_function(self, core_function.clone(), &function.arguments, span)?;
1115                    assert!(value.is_some());
1116                    value
1117                }
1118            }
1119            Expression::AssociatedFunction(function) if step == 2 => {
1120                let Some(core_function) = CoreFunction::try_from(function).ok() else {
1121                    halt!(function.span(), "Unkown core function {function}");
1122                };
1123                assert!(core_function == CoreFunction::FutureAwait);
1124                Some(Value::make_unit())
1125            }
1126            Expression::Binary(binary) if step == 0 => {
1127                use BinaryOperation::*;
1128
1129                // Determine the expected types for the right and left operands based on the operation
1130                let (right_ty, left_ty) = match binary.op {
1131                    // Multiplications that return a `Group` can take `Scalar * Group` or `Group * Scalar`.
1132                    // No way to know at this stage.
1133                    Mul if matches!(expected_ty, Some(Type::Group)) => (None, None),
1134
1135                    // Boolean operations don't require expected type propagation
1136                    And | Or | Nand | Nor | Eq | Neq | Lt | Gt | Lte | Gte => (None, None),
1137
1138                    // Exponentiation (Pow) may require specific typing if expected to be a Field
1139                    Pow => {
1140                        let right_ty = if matches!(expected_ty, Some(Type::Field)) {
1141                            Some(Type::Field) // Enforce Field type on exponent if expected
1142                        } else {
1143                            None // Otherwise, don't constrain the exponent
1144                        };
1145                        (right_ty, expected_ty.clone()) // Pass the expected type to the base
1146                    }
1147
1148                    // Bitwise shifts and wrapped exponentiation:
1149                    // Typically only the left operand should conform to the expected type
1150                    Shl | ShlWrapped | Shr | ShrWrapped | PowWrapped => (None, expected_ty.clone()),
1151
1152                    // Default case: propagate expected type to both operands
1153                    _ => (expected_ty.clone(), expected_ty.clone()),
1154                };
1155
1156                // Push operands onto the stack for evaluation in right-to-left order
1157                push!()(&binary.right, &right_ty);
1158                push!()(&binary.left, &left_ty);
1159
1160                None
1161            }
1162            Expression::Binary(binary) if step == 1 => {
1163                let rhs = self.pop_value()?;
1164                let lhs = self.pop_value()?;
1165                Some(evaluate_binary(binary.span, binary.op, &lhs, &rhs, expected_ty)?)
1166            }
1167
1168            Expression::Call(call) if step == 0 => {
1169                // Resolve the function's program and name
1170                let (function_program, function_path) = {
1171                    let maybe_program = call.program.or_else(|| self.current_program());
1172                    if let Some(program) = maybe_program {
1173                        (program, self.to_absolute_path(&call.function.as_symbols()))
1174                    } else {
1175                        halt!(call.span, "No current program");
1176                    }
1177                };
1178
1179                // Look up the function variant (Leo, AleoClosure, or AleoFunction)
1180                let Some(function_variant) = self.lookup_function(function_program, &function_path) else {
1181                    halt!(call.span, "unknown function {function_program}.aleo/{}", function_path.iter().format("::"));
1182                };
1183
1184                // Extract const parameter and input types based on the function variant
1185                let (const_param_types, input_types) = match function_variant {
1186                    FunctionVariant::Leo(function) => (
1187                        function.const_parameters.iter().map(|p| p.type_.clone()).collect::<Vec<_>>(),
1188                        function.input.iter().map(|p| p.type_.clone()).collect::<Vec<_>>(),
1189                    ),
1190                    FunctionVariant::AleoClosure(closure) => {
1191                        let function = leo_ast::FunctionStub::from_closure(&closure, function_program);
1192                        (vec![], function.input.iter().map(|p| p.type_.clone()).collect::<Vec<_>>())
1193                    }
1194                    FunctionVariant::AleoFunction(svm_function) => {
1195                        let function = leo_ast::FunctionStub::from_function_core(&svm_function, function_program);
1196                        (vec![], function.input.iter().map(|p| p.type_.clone()).collect::<Vec<_>>())
1197                    }
1198                };
1199
1200                // Push arguments (in reverse order) with corresponding input types
1201                call.arguments
1202                    .iter()
1203                    .rev()
1204                    .zip(input_types.iter().rev())
1205                    .for_each(|(arg, ty)| push!()(arg, &Some(ty.clone())));
1206
1207                // Push const arguments (in reverse order) with corresponding const param types
1208                call.const_arguments
1209                    .iter()
1210                    .rev()
1211                    .zip(const_param_types.iter().rev())
1212                    .for_each(|(arg, ty)| push!()(arg, &Some(ty.clone())));
1213
1214                None
1215            }
1216
1217            Expression::Call(call) if step == 1 => {
1218                let len = self.values.len();
1219                let (program, path) = {
1220                    let maybe_program = call.program.or_else(|| self.current_program());
1221                    if let Some(program) = maybe_program {
1222                        (program, call.function.as_symbols())
1223                    } else {
1224                        halt!(call.span, "No current program");
1225                    }
1226                };
1227                // It's a bit cheesy to collect the arguments into a Vec first, but it's the easiest way
1228                // to handle lifetimes here.
1229                let arguments: Vec<Value> =
1230                    self.values.drain(len - call.arguments.len() - call.const_arguments.len()..).collect();
1231                self.do_call(
1232                    program,
1233                    &self.to_absolute_path(&path),
1234                    arguments.into_iter(),
1235                    false, // finalize
1236                    call.span(),
1237                )?;
1238                None
1239            }
1240            Expression::Call(_call) if step == 2 => Some(self.pop_value()?),
1241            Expression::Cast(cast) if step == 0 => {
1242                push!()(&cast.expression, &None);
1243                None
1244            }
1245            Expression::Cast(cast) if step == 1 => {
1246                let span = cast.span();
1247                let arg = self.pop_value()?;
1248                match arg.cast(&cast.type_) {
1249                    Some(value) => Some(value),
1250                    None => return Err(InterpreterHalt::new_spanned("cast failure".to_string(), span).into()),
1251                }
1252            }
1253            Expression::Err(_) => todo!(),
1254            Expression::Path(path) if step == 0 => {
1255                Some(self.lookup(&self.to_absolute_path(&path.as_symbols())).expect_tc(path.span())?)
1256            }
1257            Expression::Literal(literal) if step == 0 => Some(literal_to_value(literal, expected_ty)?),
1258            Expression::Locator(_locator) => todo!(),
1259            Expression::Struct(struct_) if step == 0 => {
1260                let members =
1261                    self.structs.get(&self.to_absolute_path(&struct_.path.as_symbols())).expect_tc(struct_.span())?;
1262                for StructVariableInitializer { identifier: field_init_name, expression: init, .. } in &struct_.members
1263                {
1264                    let Some(type_) = members.get(&field_init_name.name) else { tc_fail!() };
1265                    push!()(
1266                        init.as_ref().unwrap_or(&Expression::Path(Path::from(*field_init_name))),
1267                        &Some(type_.clone()),
1268                    )
1269                }
1270
1271                None
1272            }
1273            Expression::Struct(struct_) if step == 1 => {
1274                // Collect all the key/value pairs into a HashMap.
1275                let mut contents_tmp = HashMap::with_capacity(struct_.members.len());
1276                for initializer in struct_.members.iter() {
1277                    let name = initializer.identifier.name;
1278                    let value = self.pop_value()?;
1279                    contents_tmp.insert(name, value);
1280                }
1281
1282                // And now put them into an IndexMap in the correct order.
1283                let members =
1284                    self.structs.get(&self.to_absolute_path(&struct_.path.as_symbols())).expect_tc(struct_.span())?;
1285                let contents = members.iter().map(|(identifier, _)| {
1286                    (*identifier, contents_tmp.remove(identifier).expect("we just inserted this"))
1287                });
1288
1289                // TODO: this only works for structs defined in the top level module.. must figure
1290                // something out for structs defined in modules
1291                Some(Value::make_struct(contents, self.current_program().unwrap(), struct_.path.as_symbols()))
1292            }
1293            Expression::Ternary(ternary) if step == 0 => {
1294                push!()(&ternary.condition, &None);
1295                None
1296            }
1297            Expression::Ternary(ternary) if step == 1 => {
1298                let condition = self.pop_value()?;
1299                match condition.try_into() {
1300                    Ok(true) => push!()(&ternary.if_true, &None),
1301                    Ok(false) => push!()(&ternary.if_false, &None),
1302                    _ => halt!(ternary.span(), "Invalid type for ternary expression {ternary}"),
1303                }
1304                None
1305            }
1306            Expression::Ternary(_) if step == 2 => Some(self.pop_value()?),
1307            Expression::Tuple(tuple) if step == 0 => {
1308                tuple.elements.iter().rev().for_each(|t| push!()(t, &None));
1309                None
1310            }
1311            Expression::Tuple(tuple) if step == 1 => {
1312                let len = self.values.len();
1313                let tuple_values = self.values.drain(len - tuple.elements.len()..);
1314                Some(Value::make_tuple(tuple_values))
1315            }
1316            Expression::Unary(unary) if step == 0 => {
1317                use UnaryOperation::*;
1318
1319                // Determine the expected type based on the unary operation
1320                let ty = match unary.op {
1321                    Inverse | Square | SquareRoot => Some(Type::Field), // These ops require Field operands
1322                    ToXCoordinate | ToYCoordinate => Some(Type::Group), // These ops apply to Group elements
1323                    _ => expected_ty.clone(),                           // Fallback to the externally expected type
1324                };
1325
1326                // Push the receiver expression with the computed type
1327                push!()(&unary.receiver, &ty);
1328
1329                None
1330            }
1331            Expression::Unary(unary) if step == 1 => {
1332                let value = self.pop_value()?;
1333                Some(evaluate_unary(unary.span, unary.op, &value, expected_ty)?)
1334            }
1335            Expression::Unit(_) if step == 0 => Some(Value::make_unit()),
1336            x => unreachable!("Unexpected expression {x}"),
1337        } {
1338            assert_eq!(self.frames.len(), len);
1339            self.frames.pop();
1340            self.values.push(value);
1341            Ok(true)
1342        } else {
1343            self.frames[len - 1].step += 1;
1344            Ok(false)
1345        }
1346    }
1347
1348    /// Execute one step of the current element.
1349    ///
1350    /// Many Leo constructs require multiple steps. For instance, when executing a conditional,
1351    /// the first step will push the condition expression to the stack. Once that has executed
1352    /// and we've returned to the conditional, we push the `then` or `otherwise` block to the
1353    /// stack. Once that has executed and we've returned to the conditional, the final step
1354    /// does nothing.
1355    pub fn step(&mut self) -> Result<StepResult> {
1356        if self.frames.is_empty() {
1357            return Err(InterpreterHalt::new("no execution frames available".into()).into());
1358        }
1359
1360        let Frame { element, step, user_initiated } = self.frames.last().expect("there should be a frame").clone();
1361        match element {
1362            Element::Block { block, function_body } => {
1363                let finished = self.step_block(&block, function_body, step);
1364                Ok(StepResult { finished, value: None })
1365            }
1366            Element::Statement(statement) => {
1367                let finished = self.step_statement(&statement, step)?;
1368                Ok(StepResult { finished, value: None })
1369            }
1370            Element::Expression(expression, expected_ty) => {
1371                let finished = self.step_expression(&expression, &expected_ty, step)?;
1372                let value = match (finished, user_initiated) {
1373                    (false, _) => None,
1374                    (true, false) => self.values.last().cloned(),
1375                    (true, true) => self.values.pop(),
1376                };
1377
1378                let maybe_future = if let Some(len) = value.as_ref().and_then(|val| val.tuple_len()) {
1379                    value.as_ref().unwrap().tuple_index(len - 1)
1380                } else {
1381                    value.clone()
1382                };
1383
1384                if let Some(asyncs) = maybe_future.as_ref().and_then(|fut| fut.as_future())
1385                    && user_initiated
1386                {
1387                    self.futures.extend(asyncs.iter().cloned());
1388                }
1389
1390                Ok(StepResult { finished, value })
1391            }
1392            Element::AleoExecution { .. } => {
1393                self.step_aleo()?;
1394                Ok(StepResult { finished: true, value: None })
1395            }
1396            Element::DelayedCall(gid) if step == 0 => {
1397                match self.lookup_function(gid.program, &gid.path).expect("function should exist") {
1398                    FunctionVariant::Leo(function) => {
1399                        assert!(function.variant == Variant::AsyncFunction);
1400                        let len = self.values.len();
1401                        let values: Vec<Value> = self.values.drain(len - function.input.len()..).collect();
1402                        self.contexts.push(
1403                            &gid.path,
1404                            gid.program,
1405                            self.signer.clone(),
1406                            true, // is_async
1407                            HashMap::new(),
1408                        );
1409                        let param_names = function.input.iter().map(|input| input.identifier.name);
1410                        for (name, value) in param_names.zip(values) {
1411                            self.set_variable(&self.to_absolute_path(&[name]), value);
1412                        }
1413                        self.frames.last_mut().unwrap().step = 1;
1414                        self.frames.push(Frame {
1415                            step: 0,
1416                            element: Element::Block { block: function.block.clone(), function_body: true },
1417                            user_initiated: false,
1418                        });
1419                        Ok(StepResult { finished: false, value: None })
1420                    }
1421                    FunctionVariant::AleoFunction(function) => {
1422                        let Some(finalize_f) = function.finalize_logic() else {
1423                            panic!("must have finalize logic for a delayed call");
1424                        };
1425                        let len = self.values.len();
1426                        let values_iter = self.values.drain(len - finalize_f.inputs().len()..);
1427                        self.contexts.push(
1428                            &gid.path,
1429                            gid.program,
1430                            self.signer.clone(),
1431                            true, // is_async
1432                            HashMap::new(),
1433                        );
1434                        self.frames.last_mut().unwrap().step = 1;
1435                        self.frames.push(Frame {
1436                            step: 0,
1437                            element: Element::AleoExecution {
1438                                context: AleoContext::Finalize(finalize_f.clone()).into(),
1439                                registers: values_iter.enumerate().map(|(i, v)| (i as u64, v)).collect(),
1440                                instruction_index: 0,
1441                            },
1442                            user_initiated: false,
1443                        });
1444                        Ok(StepResult { finished: false, value: None })
1445                    }
1446                    FunctionVariant::AleoClosure(..) => panic!("A call to a closure can't be delayed"),
1447                }
1448            }
1449            Element::DelayedCall(_gid) => {
1450                assert_eq!(step, 1);
1451                let value = self.values.pop();
1452                self.frames.pop();
1453                Ok(StepResult { finished: true, value })
1454            }
1455            Element::DelayedAsyncBlock { program, block, names } if step == 0 => {
1456                self.contexts.push(
1457                    &[Symbol::intern("")],
1458                    program,
1459                    self.signer.clone(),
1460                    true,
1461                    names.clone().into_iter().collect(), // Set the known names to the previously preserved `names`.
1462                );
1463                self.frames.last_mut().unwrap().step = 1;
1464                self.frames.push(Frame {
1465                    step: 0,
1466                    element: Element::Block {
1467                        block: self.async_blocks.get(&block).unwrap().clone(),
1468                        function_body: true,
1469                    },
1470                    user_initiated: false,
1471                });
1472                Ok(StepResult { finished: false, value: None })
1473            }
1474            Element::DelayedAsyncBlock { .. } => {
1475                assert_eq!(step, 1);
1476                let value = self.values.pop();
1477                self.frames.pop();
1478                Ok(StepResult { finished: true, value })
1479            }
1480        }
1481    }
1482
1483    pub fn do_call(
1484        &mut self,
1485        function_program: Symbol,
1486        function_path: &[Symbol],
1487        arguments: impl Iterator<Item = Value>,
1488        finalize: bool,
1489        span: Span,
1490    ) -> Result<()> {
1491        let Some(function_variant) = self.lookup_function(function_program, function_path) else {
1492            halt!(span, "unknown function {function_program}.aleo/{}", function_path.iter().format("::"));
1493        };
1494        match function_variant {
1495            FunctionVariant::Leo(function) => {
1496                let caller = if matches!(function.variant, Variant::Transition | Variant::AsyncTransition) {
1497                    self.new_caller()
1498                } else {
1499                    self.signer.clone()
1500                };
1501                if self.really_async && function.variant == Variant::AsyncFunction {
1502                    // Don't actually run the call now.
1503                    let async_ex = AsyncExecution::AsyncFunctionCall {
1504                        function: Location::new(function_program, function_path.to_vec()),
1505                        arguments: arguments.collect(),
1506                    };
1507                    self.values.push(vec![async_ex].into());
1508                } else {
1509                    let is_async = function.variant == Variant::AsyncFunction;
1510                    self.contexts.push(function_path, function_program, caller, is_async, HashMap::new());
1511                    // Treat const generic parameters as regular inputs
1512                    let param_names = function
1513                        .const_parameters
1514                        .iter()
1515                        .map(|param| param.identifier.name)
1516                        .chain(function.input.iter().map(|input| input.identifier.name));
1517                    for (name, value) in param_names.zip(arguments) {
1518                        self.set_variable(&self.to_absolute_path(&[name]), value);
1519                    }
1520                    self.frames.push(Frame {
1521                        step: 0,
1522                        element: Element::Block { block: function.block.clone(), function_body: true },
1523                        user_initiated: false,
1524                    });
1525                }
1526            }
1527            FunctionVariant::AleoClosure(closure) => {
1528                self.contexts.push(function_path, function_program, self.signer.clone(), false, HashMap::new());
1529                let context = AleoContext::Closure(closure);
1530                self.frames.push(Frame {
1531                    step: 0,
1532                    element: Element::AleoExecution {
1533                        context: context.into(),
1534                        registers: arguments.enumerate().map(|(i, v)| (i as u64, v)).collect(),
1535                        instruction_index: 0,
1536                    },
1537                    user_initiated: false,
1538                });
1539            }
1540            FunctionVariant::AleoFunction(function) => {
1541                let caller = self.new_caller();
1542                self.contexts.push(function_path, function_program, caller, false, HashMap::new());
1543                let context = if finalize {
1544                    let Some(finalize_f) = function.finalize_logic() else {
1545                        panic!("finalize call with no finalize logic");
1546                    };
1547                    AleoContext::Finalize(finalize_f.clone())
1548                } else {
1549                    AleoContext::Function(function)
1550                };
1551                self.frames.push(Frame {
1552                    step: 0,
1553                    element: Element::AleoExecution {
1554                        context: context.into(),
1555                        registers: arguments.enumerate().map(|(i, v)| (i as u64, v)).collect(),
1556                        instruction_index: 0,
1557                    },
1558                    user_initiated: false,
1559                });
1560            }
1561        }
1562
1563        Ok(())
1564    }
1565}
1566
1567#[derive(Clone, Debug)]
1568pub struct StepResult {
1569    /// Has this element completely finished running?
1570    pub finished: bool,
1571
1572    /// If the element was an expression, here's its value.
1573    pub value: Option<Value>,
1574}