Skip to main content

snarkvm_synthesizer_process/stack/
evaluate.rs

1// Copyright (c) 2019-2026 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::*;
17use snarkvm_synthesizer_error::*;
18
19use std::sync::OnceLock;
20
21impl<N: Network> Stack<N> {
22    /// Evaluates a program closure on the given inputs.
23    ///
24    /// # Errors
25    /// This method will halt if the given inputs are not the same length as the input statements.
26    pub fn evaluate_closure<A: circuit::Aleo<Network = N>>(
27        &self,
28        closure: &Closure<N>,
29        inputs: &[Value<N>],
30        call_stack: CallStack<N>,
31        signer: Address<N>,
32        caller: Address<N>,
33        tvk: Field<N>,
34    ) -> Result<Vec<Value<N>>, StackEvalError> {
35        let timer = timer!("Stack::evaluate_closure");
36
37        // Ensure the number of inputs matches the number of input statements.
38        if closure.inputs().len() != inputs.len() {
39            return Err(anyhow!("Expected {} inputs, found {}", closure.inputs().len(), inputs.len()).into());
40        }
41
42        // Initialize the registers.
43        let mut registers =
44            Registers::<N, A>::new(call_stack.clone(), self.get_register_types(closure.name())?.clone());
45        // Set the transition signer.
46        registers.set_signer(signer);
47        // Set the transition caller.
48        registers.set_caller(caller);
49        // Set the transition view key.
50        registers.set_tvk(tvk);
51        lap!(timer, "Initialize the registers");
52
53        // Store the inputs.
54        closure.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| {
55            // Assign the input value to the register.
56            registers.store(self, register, input.clone())
57        })?;
58        lap!(timer, "Store the inputs");
59
60        // Evaluate the instructions.
61        for (ix, instruction) in closure.instructions().iter().enumerate() {
62            // If the evaluation fails, bail and return the error.
63            if let Err(error) = instruction.evaluate(self, &mut registers) {
64                return Err(IndexedInstructionError::new(ix, format!("{instruction}"), error.into()).into());
65            }
66        }
67        lap!(timer, "Evaluate the instructions");
68
69        // Load the outputs.
70        let outputs = closure
71            .outputs()
72            .iter()
73            .map(|output| -> Result<_> {
74                match output.operand() {
75                    // If the operand is a literal, use the literal directly.
76                    Operand::Literal(literal) => Ok(Value::Plaintext(Plaintext::from(literal))),
77                    // If the operand is a register, retrieve the stack value from the register.
78                    Operand::Register(register) => registers.load(self, &Operand::Register(register.clone())),
79                    // If the operand is the program ID, convert the program ID into an address.
80                    Operand::ProgramID(program_id) => {
81                        Ok(Value::Plaintext(Plaintext::from(Literal::Address(program_id.to_address()?))))
82                    }
83                    // If the operand is the signer, retrieve the signer from the registers.
84                    Operand::Signer => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.signer()?)))),
85                    // If the operand is the caller, retrieve the caller from the registers.
86                    Operand::Caller => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.caller()?)))),
87                    // If the operand is the generator, retrieve the Aleo generator.
88                    Operand::AleoGenerator => N::g_powers()
89                        .first()
90                        .map(|element| Value::Plaintext(Plaintext::from(Literal::Group(*element))))
91                        .ok_or_else(|| anyhow!("Failed to retrieve the Aleo generator.")),
92                    // If the operand is the generator powers, retrieve the generator powers or the indexed group.
93                    Operand::AleoGeneratorPowers(index) => match index {
94                        None => Ok(Value::Plaintext(Plaintext::Array(
95                            N::g_powers().iter().map(|element| Plaintext::from(Literal::Group(*element))).collect(),
96                            OnceLock::new(),
97                        ))),
98                        Some(index) => N::g_powers()
99                            .get(**index as usize)
100                            .map(|element| Value::Plaintext(Plaintext::from(Literal::Group(*element))))
101                            .ok_or_else(|| anyhow!("Index {index} out of bounds for Aleo generator")),
102                    },
103                    // If the operand is the block height, throw an error.
104                    Operand::BlockHeight => bail!("Cannot retrieve the block height from a closure scope."),
105                    // If the operand is the block timestamp, throw an error.
106                    Operand::BlockTimestamp => bail!("Cannot retrieve the block timestamp from a closure scope."),
107                    // If the operand is the network id, throw an error.
108                    Operand::NetworkID => bail!("Cannot retrieve the network ID from a closure scope."),
109                    // If the operand is the program checksum, throw an error.
110                    Operand::Checksum(_) => bail!("Cannot retrieve the program checksum from a closure scope."),
111                    // If the operand is the program edition, throw an error.
112                    Operand::Edition(_) => bail!("Cannot retrieve the edition from a closure scope."),
113                    // If the operand is the program owner, throw an error.
114                    Operand::ProgramOwner(_) => bail!("Cannot retrieve the program owner from a closure scope."),
115                }
116            })
117            .map(|res| res.map_err(StackEvalError::from))
118            .collect();
119        lap!(timer, "Load the outputs");
120
121        finish!(timer);
122        outputs
123    }
124
125    /// Evaluates a program function on the given inputs.
126    ///
127    /// # Errors
128    /// This method will halt if the given inputs are not the same length as the input statements.
129    pub fn evaluate_function<A: circuit::Aleo<Network = N>, R: CryptoRng + Rng>(
130        &self,
131        mut call_stack: CallStack<N>,
132        caller: Option<ProgramID<N>>,
133        root_tvk: Option<Field<N>>,
134        rng: &mut R,
135    ) -> Result<Response<N>, StackEvalError> {
136        let timer = timer!("Stack::evaluate_function");
137
138        // Retrieve the next request, based on the call stack mode.
139        let (request, call_stack) =
140            match &mut call_stack {
141                CallStack::Authorize(..) => (call_stack.pop()?, call_stack),
142                CallStack::AuthorizeMocked(..) => (call_stack.pop()?, call_stack),
143                CallStack::Evaluate(authorization) => (authorization.next()?, call_stack),
144                // If the evaluation is performed in the `Execute` mode, create a new `Evaluate` mode.
145                // This is done to ensure that evaluation during execution is performed consistently.
146                CallStack::Execute(authorization, _, _) => {
147                    // Note: We need to replicate the authorization, so that 'execute' can call 'authorization.next()?'.
148                    // This way, the authorization remains unmodified in this 'evaluate' scope.
149                    let authorization = authorization.replicate();
150                    let request = authorization.next()?;
151                    let call_stack = CallStack::Evaluate(authorization);
152                    (request, call_stack)
153                }
154                _ => return Err(anyhow!(
155                    "Illegal operation: call stack must be `Authorize`, `Evaluate`, `Execute` or `AuthorizeMocked` in `evaluate_function`."
156                )
157                .into()),
158            };
159        lap!(timer, "Retrieve the next request");
160
161        // Ensure the network ID matches.
162        if **request.network_id() != N::ID {
163            return Err(anyhow!("Network ID mismatch. Expected {}, but found {}", N::ID, request.network_id()).into());
164        }
165
166        // Retrieve the function, inputs, and transition view key.
167        let function = self.get_function(request.function_name())?;
168        let inputs = request.inputs();
169        let signer = *request.signer();
170        let (is_root, caller) = match caller {
171            // If a caller is provided, then this is an evaluation of a child function.
172            Some(caller) => (false, caller.to_address()?),
173            // If no caller is provided, then this is an evaluation of a top-level function.
174            None => (true, signer),
175        };
176        let tvk = *request.tvk();
177        // Retrieve the program checksum, if the program has a constructor.
178        let program_checksum = match self.program().contains_constructor() {
179            true => Some(self.program_checksum_as_field()?),
180            false => None,
181        };
182
183        // Ensure the number of inputs matches.
184        if function.inputs().len() != inputs.len() {
185            return Err(anyhow!(
186                "Function '{}' in the program '{}' expects {} inputs, but {} were provided.",
187                function.name(),
188                self.program.id(),
189                function.inputs().len(),
190                inputs.len()
191            )
192            .into());
193        }
194        lap!(timer, "Perform input checks");
195
196        // Ensure the request is well-formed (unless it has been mocked).
197        if !matches!(call_stack, CallStack::AuthorizeMocked(..))
198            && !request.verify(&function.input_types(), is_root, program_checksum)
199        {
200            return Err(anyhow!("[Evaluate] Request is invalid").into());
201        }
202        lap!(timer, "Verify the request");
203
204        // Initialize the registers.
205        let mut registers = Registers::<N, A>::new(call_stack, self.get_register_types(function.name())?.clone());
206        // Set the transition signer.
207        registers.set_signer(signer);
208        // Set the transition caller.
209        registers.set_caller(caller);
210        // Set the transition view key.
211        registers.set_tvk(tvk);
212        // Set the root tvk.
213        if let Some(root_tvk) = root_tvk {
214            registers.set_root_tvk(root_tvk);
215        } else {
216            registers.set_root_tvk(tvk);
217        }
218        lap!(timer, "Initialize the registers");
219
220        // Store the inputs.
221        function.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| {
222            // Assign the input value to the register.
223            registers.store(self, register, input.clone())
224        })?;
225        lap!(timer, "Store the inputs");
226
227        // Evaluate the instructions.
228        // Note: We handle the `call` instruction separately, as it requires special handling.
229        for (ix, instruction) in function.instructions().iter().enumerate() {
230            // Evaluate the instruction.
231            let result = match instruction {
232                // If the instruction is a `call` instruction, we need to handle it separately.
233                Instruction::Call(call) => CallTrait::evaluate(call, self, &mut registers, rng)
234                    .map_err(|e| InstructionEvalError::Call(Box::new(e))),
235                // If the instruction is a `call.dynamic` instruction, we need to handle it separately.
236                Instruction::CallDynamic(call_dynamic) => CallTrait::evaluate(call_dynamic, self, &mut registers, rng)
237                    .map_err(|e| InstructionEvalError::Call(Box::new(e))),
238                // Otherwise, evaluate the instruction normally.
239                _ => instruction.evaluate(self, &mut registers).map_err(Into::into),
240            };
241            // If the evaluation fails, bail and return the error.
242            if let Err(error) = result {
243                return Err(IndexedInstructionError::new(ix, format!("{instruction}"), error).into());
244            }
245        }
246        lap!(timer, "Evaluate the instructions");
247
248        // Retrieve the output operands.
249        let output_operands = &function.outputs().iter().map(|output| output.operand()).collect::<Vec<_>>();
250        lap!(timer, "Retrieve the output operands");
251
252        // Load the outputs.
253        let outputs = output_operands
254            .iter()
255            .map(|operand| {
256                match operand {
257                    // If the operand is a literal, use the literal directly.
258                    Operand::Literal(literal) => Ok(Value::Plaintext(Plaintext::from(literal))),
259                    // If the operand is a register, retrieve the stack value from the register.
260                    Operand::Register(register) => registers.load(self, &Operand::Register(register.clone())),
261                    // If the operand is the program ID, convert the program ID into an address.
262                    Operand::ProgramID(program_id) => {
263                        Ok(Value::Plaintext(Plaintext::from(Literal::Address(program_id.to_address()?))))
264                    }
265                    // If the operand is the signer, retrieve the signer from the registers.
266                    Operand::Signer => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.signer()?)))),
267                    // If the operand is the caller, retrieve the caller from the registers.
268                    Operand::Caller => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.caller()?)))),
269                    // If the operand is the generator, retrieve the Aleo generator.
270                    Operand::AleoGenerator => N::g_powers()
271                        .first()
272                        .map(|element| Value::Plaintext(Plaintext::from(Literal::Group(*element))))
273                        .ok_or_else(|| anyhow!("Failed to retrieve the Aleo generator.")),
274                    // If the operand is the generator powers, retrieve the generator powers or the indexed group.
275                    Operand::AleoGeneratorPowers(index) => match index {
276                        None => Ok(Value::Plaintext(Plaintext::Array(
277                            N::g_powers().iter().map(|element| Plaintext::from(Literal::Group(*element))).collect(),
278                            OnceLock::new(),
279                        ))),
280                        Some(index) => N::g_powers()
281                            .get(**index as usize)
282                            .map(|element| Value::Plaintext(Plaintext::from(Literal::Group(*element))))
283                            .ok_or_else(|| anyhow!("Index {index} out of bounds for Aleo generator")),
284                    },
285                    // If the operand is the block height, throw an error.
286                    Operand::BlockHeight => bail!("Cannot retrieve the block height from a function scope."),
287                    // If the operand is the block timestamp, throw an error.
288                    Operand::BlockTimestamp => bail!("Cannot retrieve the block timestamp from a function scope."),
289                    // If the operand is the network id, throw an error.
290                    Operand::NetworkID => bail!("Cannot retrieve the network ID from a function scope."),
291                    // If the operand is the program checksum, throw an error.
292                    Operand::Checksum(_) => bail!("Cannot retrieve the program checksum from a function scope."),
293                    // If the operand is the program edition, throw an error.
294                    Operand::Edition(_) => bail!("Cannot retrieve the edition from a function scope."),
295                    // If the operand is the program owner, throw an error.
296                    Operand::ProgramOwner(_) => bail!("Cannot retrieve the program owner from a function scope."),
297                }
298            })
299            .collect::<Result<Vec<_>>>()?;
300        lap!(timer, "Load the outputs");
301
302        // Map the output operands to registers.
303        let output_registers = output_operands
304            .iter()
305            .map(|operand| match operand {
306                Operand::Register(register) => Some(register.clone()),
307                _ => None,
308            })
309            .collect::<Vec<_>>();
310        lap!(timer, "Loaded the output registers");
311
312        // Compute the response.
313        let response = Response::new(
314            request.signer(),
315            request.network_id(),
316            self.program.id(),
317            function.name(),
318            request.inputs().len(),
319            request.tvk(),
320            request.tcm(),
321            outputs,
322            &function.output_types(),
323            &output_registers,
324        )?;
325        finish!(timer);
326
327        // If the circuit is in `Authorize` or `AuthorizeMocked` mode, then save the transition.
328        if let CallStack::Authorize(_, _, authorization) = registers.call_stack_ref() {
329            // Construct the transition.
330            let transition = Transition::from(&request, &response, &function.output_types(), &output_registers)?;
331            // Add the transition to the authorization.
332            authorization.insert_transition(transition)?;
333            lap!(timer, "Save the transition");
334        }
335        if let CallStack::AuthorizeMocked(_, _, authorization) = registers.call_stack_ref() {
336            // Construct the transition without checking correctness of input IDs.
337            let transition =
338                Transition::from_unchecked(&request, &response, &function.output_types(), &output_registers)?;
339            // Add the transition to the authorization.
340            authorization.insert_transition(transition)?;
341            lap!(timer, "Save the mocked transition");
342        }
343
344        Ok(response)
345    }
346}