snarkvm_synthesizer_process/stack/
evaluate.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::*;
17
18impl<N: Network> StackEvaluate<N> for Stack<N> {
19    /// Evaluates a program closure on the given inputs.
20    ///
21    /// # Errors
22    /// This method will halt if the given inputs are not the same length as the input statements.
23    #[inline]
24    fn evaluate_closure<A: circuit::Aleo<Network = N>>(
25        &self,
26        closure: &Closure<N>,
27        inputs: &[Value<N>],
28        call_stack: CallStack<N>,
29        signer: Address<N>,
30        caller: Address<N>,
31        tvk: Field<N>,
32    ) -> Result<Vec<Value<N>>> {
33        let timer = timer!("Stack::evaluate_closure");
34
35        // Ensure the number of inputs matches the number of input statements.
36        if closure.inputs().len() != inputs.len() {
37            bail!("Expected {} inputs, found {}", closure.inputs().len(), inputs.len())
38        }
39
40        // Initialize the registers.
41        let mut registers = Registers::<N, A>::new(call_stack, self.get_register_types(closure.name())?.clone());
42        // Set the transition signer.
43        registers.set_signer(signer);
44        // Set the transition caller.
45        registers.set_caller(caller);
46        // Set the transition view key.
47        registers.set_tvk(tvk);
48        lap!(timer, "Initialize the registers");
49
50        // Store the inputs.
51        closure.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| {
52            // Assign the input value to the register.
53            registers.store(self, register, input.clone())
54        })?;
55        lap!(timer, "Store the inputs");
56
57        // Evaluate the instructions.
58        for instruction in closure.instructions() {
59            // If the evaluation fails, bail and return the error.
60            if let Err(error) = instruction.evaluate(self, &mut registers) {
61                bail!("Failed to evaluate instruction ({instruction}): {error}");
62            }
63        }
64        lap!(timer, "Evaluate the instructions");
65
66        // Load the outputs.
67        let outputs = closure
68            .outputs()
69            .iter()
70            .map(|output| {
71                match output.operand() {
72                    // If the operand is a literal, use the literal directly.
73                    Operand::Literal(literal) => Ok(Value::Plaintext(Plaintext::from(literal))),
74                    // If the operand is a register, retrieve the stack value from the register.
75                    Operand::Register(register) => registers.load(self, &Operand::Register(register.clone())),
76                    // If the operand is the program ID, convert the program ID into an address.
77                    Operand::ProgramID(program_id) => {
78                        Ok(Value::Plaintext(Plaintext::from(Literal::Address(program_id.to_address()?))))
79                    }
80                    // If the operand is the signer, retrieve the signer from the registers.
81                    Operand::Signer => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.signer()?)))),
82                    // If the operand is the caller, retrieve the caller from the registers.
83                    Operand::Caller => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.caller()?)))),
84                    // If the operand is the block height, throw an error.
85                    Operand::BlockHeight => bail!("Cannot retrieve the block height from a closure scope."),
86                    // If the operand is the network id, throw an error.
87                    Operand::NetworkID => bail!("Cannot retrieve the network ID from a closure scope."),
88                }
89            })
90            .collect();
91        lap!(timer, "Load the outputs");
92
93        finish!(timer);
94        outputs
95    }
96
97    /// Evaluates a program function on the given inputs.
98    ///
99    /// # Errors
100    /// This method will halt if the given inputs are not the same length as the input statements.
101    #[inline]
102    fn evaluate_function<A: circuit::Aleo<Network = N>>(
103        &self,
104        call_stack: CallStack<N>,
105        caller: Option<ProgramID<N>>,
106    ) -> Result<Response<N>> {
107        let timer = timer!("Stack::evaluate_function");
108
109        // Retrieve the next request, based on the call stack mode.
110        let (request, call_stack) = match &call_stack {
111            CallStack::Evaluate(authorization) => (authorization.next()?, call_stack),
112            // If the evaluation is performed in the `Execute` mode, create a new `Evaluate` mode.
113            // This is done to ensure that evaluation during execution is performed consistently.
114            CallStack::Execute(authorization, _) => {
115                // Note: We need to replicate the authorization, so that 'execute' can call 'authorization.next()?'.
116                // This way, the authorization remains unmodified in this 'evaluate' scope.
117                let authorization = authorization.replicate();
118                let request = authorization.next()?;
119                let call_stack = CallStack::Evaluate(authorization);
120                (request, call_stack)
121            }
122            _ => bail!("Illegal operation: call stack must be `Evaluate` or `Execute` in `evaluate_function`."),
123        };
124        lap!(timer, "Retrieve the next request");
125
126        // Ensure the network ID matches.
127        ensure!(
128            **request.network_id() == N::ID,
129            "Network ID mismatch. Expected {}, but found {}",
130            N::ID,
131            request.network_id()
132        );
133
134        // Retrieve the function, inputs, and transition view key.
135        let function = self.get_function(request.function_name())?;
136        let inputs = request.inputs();
137        let signer = *request.signer();
138        let (is_root, caller) = match caller {
139            // If a caller is provided, then this is an evaluation of a child function.
140            Some(caller) => (false, caller.to_address()?),
141            // If no caller is provided, then this is an evaluation of a top-level function.
142            None => (true, signer),
143        };
144        let tvk = *request.tvk();
145
146        // Ensure the number of inputs matches.
147        if function.inputs().len() != inputs.len() {
148            bail!(
149                "Function '{}' in the program '{}' expects {} inputs, but {} were provided.",
150                function.name(),
151                self.program.id(),
152                function.inputs().len(),
153                inputs.len()
154            )
155        }
156        lap!(timer, "Perform input checks");
157
158        // Initialize the registers.
159        let mut registers = Registers::<N, A>::new(call_stack, self.get_register_types(function.name())?.clone());
160        // Set the transition signer.
161        registers.set_signer(signer);
162        // Set the transition caller.
163        registers.set_caller(caller);
164        // Set the transition view key.
165        registers.set_tvk(tvk);
166        lap!(timer, "Initialize the registers");
167
168        // Ensure the request is well-formed.
169        ensure!(request.verify(&function.input_types(), is_root), "Request is invalid");
170        lap!(timer, "Verify the request");
171
172        // Store the inputs.
173        function.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| {
174            // Assign the input value to the register.
175            registers.store(self, register, input.clone())
176        })?;
177        lap!(timer, "Store the inputs");
178
179        // Evaluate the instructions.
180        // Note: We handle the `call` instruction separately, as it requires special handling.
181        for instruction in function.instructions() {
182            // Evaluate the instruction.
183            let result = match instruction {
184                // If the instruction is a `call` instruction, we need to handle it separately.
185                Instruction::Call(call) => CallTrait::evaluate(call, self, &mut registers),
186                // Otherwise, evaluate the instruction normally.
187                _ => instruction.evaluate(self, &mut registers),
188            };
189            // If the evaluation fails, bail and return the error.
190            if let Err(error) = result {
191                bail!("Failed to evaluate instruction ({instruction}): {error}");
192            }
193        }
194        lap!(timer, "Evaluate the instructions");
195
196        // Retrieve the output operands.
197        let output_operands = &function.outputs().iter().map(|output| output.operand()).collect::<Vec<_>>();
198        lap!(timer, "Retrieve the output operands");
199
200        // Load the outputs.
201        let outputs = output_operands
202            .iter()
203            .map(|operand| {
204                match operand {
205                    // If the operand is a literal, use the literal directly.
206                    Operand::Literal(literal) => Ok(Value::Plaintext(Plaintext::from(literal))),
207                    // If the operand is a register, retrieve the stack value from the register.
208                    Operand::Register(register) => registers.load(self, &Operand::Register(register.clone())),
209                    // If the operand is the program ID, convert the program ID into an address.
210                    Operand::ProgramID(program_id) => {
211                        Ok(Value::Plaintext(Plaintext::from(Literal::Address(program_id.to_address()?))))
212                    }
213                    // If the operand is the signer, retrieve the signer from the registers.
214                    Operand::Signer => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.signer()?)))),
215                    // If the operand is the caller, retrieve the caller from the registers.
216                    Operand::Caller => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.caller()?)))),
217                    // If the operand is the block height, throw an error.
218                    Operand::BlockHeight => bail!("Cannot retrieve the block height from a function scope."),
219                    // If the operand is the network id, throw an error.
220                    Operand::NetworkID => bail!("Cannot retrieve the network ID from a function scope."),
221                }
222            })
223            .collect::<Result<Vec<_>>>()?;
224        lap!(timer, "Load the outputs");
225
226        // Map the output operands to registers.
227        let output_registers = output_operands
228            .iter()
229            .map(|operand| match operand {
230                Operand::Register(register) => Some(register.clone()),
231                _ => None,
232            })
233            .collect::<Vec<_>>();
234        lap!(timer, "Loaded the output registers");
235
236        // Compute the response.
237        let response = Response::new(
238            request.network_id(),
239            self.program.id(),
240            function.name(),
241            request.inputs().len(),
242            request.tvk(),
243            request.tcm(),
244            outputs,
245            &function.output_types(),
246            &output_registers,
247        );
248        finish!(timer);
249
250        response
251    }
252}