execute.rs
1 // Copyright (c) 2025 ADnet Contributors 2 // This file is part of the AlphaVM 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 16 use super::*; 17 18 impl<N: Network> Stack<N> { 19 /// Executes 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 pub fn execute_closure<A: circuit::Aleo<Network = N>>( 24 &self, 25 closure: &Closure<N>, 26 inputs: &[circuit::Value<A>], 27 call_stack: CallStack<N>, 28 signer: circuit::Address<A>, 29 caller: circuit::Address<A>, 30 tvk: circuit::Field<A>, 31 ) -> Result<Vec<circuit::Value<A>>> { 32 let timer = timer!("Stack::execute_closure"); 33 34 // Ensure the call stack is not `Evaluate`. 35 ensure!(!matches!(call_stack, CallStack::Evaluate(..)), "Illegal operation: cannot evaluate in execute mode"); 36 37 // Ensure the number of inputs matches the number of input statements. 38 if closure.inputs().len() != inputs.len() { 39 bail!("Expected {} inputs, found {}", closure.inputs().len(), inputs.len()) 40 } 41 lap!(timer, "Check the number of inputs"); 42 43 // Retrieve the number of public variables in the circuit. 44 let num_public = A::num_public(); 45 46 // Initialize the registers. 47 let mut registers = Registers::new(call_stack, self.get_register_types(closure.name())?.clone()); 48 // Set the transition signer, as a circuit. 49 registers.set_signer_circuit(signer); 50 // Set the transition caller, as a circuit. 51 registers.set_caller_circuit(caller); 52 // Set the transition view key, as a circuit. 53 registers.set_tvk_circuit(tvk); 54 lap!(timer, "Initialize the registers"); 55 56 // Store the inputs. 57 closure.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| { 58 // If the circuit is in execute mode, then store the console input. 59 if let CallStack::Execute(..) = registers.call_stack_ref() { 60 use circuit::Eject; 61 // Assign the console input to the register. 62 registers.store(self, register, input.eject_value())?; 63 } 64 // Assign the circuit input to the register. 65 registers.store_circuit(self, register, input.clone()) 66 })?; 67 lap!(timer, "Store the inputs"); 68 69 // Execute the instructions. 70 for instruction in closure.instructions() { 71 // If the circuit is in execute mode, then evaluate the instructions. 72 if let CallStack::Execute(..) = registers.call_stack_ref() { 73 // If the evaluation fails, bail and return the error. 74 if let Err(error) = instruction.evaluate(self, &mut registers) { 75 bail!("Failed to evaluate instruction ({instruction}): {error}"); 76 } 77 } 78 // Execute the instruction. 79 instruction.execute(self, &mut registers)?; 80 } 81 lap!(timer, "Execute the instructions"); 82 83 // Ensure the number of public variables remains the same. 84 ensure!(A::num_public() == num_public, "Illegal closure operation: instructions injected public variables"); 85 86 use circuit::Inject; 87 88 // Load the outputs. 89 let outputs = closure 90 .outputs() 91 .iter() 92 .map(|output| { 93 match output.operand() { 94 // If the operand is a literal, use the literal directly. 95 Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from( 96 circuit::Literal::new(circuit::Mode::Constant, literal.clone()), 97 ))), 98 // If the operand is a register, retrieve the stack value from the register. 99 Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())), 100 // If the operand is the program ID, convert the program ID into an address. 101 Operand::ProgramID(program_id) => { 102 Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address( 103 circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?), 104 )))) 105 } 106 // If the operand is the signer, retrieve the signer from the registers. 107 Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from( 108 circuit::Literal::Address(registers.signer_circuit()?), 109 ))), 110 // If the operand is the caller, retrieve the caller from the registers. 111 Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from( 112 circuit::Literal::Address(registers.caller_circuit()?), 113 ))), 114 // If the operand is the block height, throw an error. 115 Operand::BlockHeight => { 116 bail!("Illegal operation: cannot retrieve the block height in a closure scope") 117 } 118 // If the operand is the block timestamp, throw an error. 119 Operand::BlockTimestamp => { 120 bail!("Illegal operation: cannot retrieve the block timestamp in a closure scope") 121 } 122 // If the operand is the network id, throw an error. 123 Operand::NetworkID => { 124 bail!("Illegal operation: cannot retrieve the network id in a closure scope") 125 } 126 // If the operand is the checksum, throw an error. 127 Operand::Checksum(_) => bail!("Illegal operation: cannot retrieve the checksum in a closure scope"), 128 // If the operand is the edition, throw an error. 129 Operand::Edition(_) => bail!("Illegal operation: cannot retrieve the edition in a closure scope"), 130 // If the operand is the program owner, throw an error. 131 Operand::ProgramOwner(_) => { 132 bail!("Illegal operation: cannot retrieve the program owner in a closure scope") 133 } 134 } 135 }) 136 .collect(); 137 lap!(timer, "Load the outputs"); 138 139 finish!(timer); 140 outputs 141 } 142 143 /// Executes a program function on the given inputs. 144 /// 145 /// Note: To execute a transition, do **not** call this method. Instead, call `Process::execute`. 146 /// 147 /// # Errors 148 /// This method will halt if the given inputs are not the same length as the input statements. 149 pub fn execute_function<A: circuit::Aleo<Network = N>, R: CryptoRng + Rng>( 150 &self, 151 mut call_stack: CallStack<N>, 152 console_caller: Option<ProgramID<N>>, 153 console_root_tvk: Option<Field<N>>, 154 rng: &mut R, 155 ) -> Result<Response<N>> { 156 let timer = timer!("Stack::execute_function"); 157 158 // Ensure the global constants for the Aleo environment are initialized. 159 A::initialize_global_constants(); 160 // Ensure the circuit environment is clean. 161 A::reset(); 162 163 // If in 'CheckDeployment' mode, set the constraint limit and variable limit. 164 // We do not have to reset it after function calls because `CheckDeployment` mode does not execute those. 165 if let CallStack::CheckDeployment(_, _, _, constraint_limit, variable_limit) = &call_stack { 166 A::set_constraint_limit(*constraint_limit); 167 A::set_variable_limit(*variable_limit); 168 } 169 170 // Retrieve the next request. 171 let console_request = call_stack.pop()?; 172 173 // Ensure the network ID matches. 174 ensure!( 175 **console_request.network_id() == N::ID, 176 "Network ID mismatch. Expected {}, but found {}", 177 N::ID, 178 console_request.network_id() 179 ); 180 181 // We can only have a root_tvk if this request was called by another request 182 ensure!(console_caller.is_some() == console_root_tvk.is_some()); 183 // Determine if this is the top-level caller. 184 let console_is_root = console_caller.is_none(); 185 186 // Determine the parent. 187 // - If this execution is the top-level caller, then the parent is the program ID. 188 // - If this execution is a child caller, then the parent is the caller. 189 let console_parent = match console_caller { 190 // If this execution is the top-level caller, then the parent is the program ID. 191 None => console_request.program_id().to_address()?, 192 // If this execution is a child caller, then the parent is the caller. 193 Some(console_caller) => console_caller.to_address()?, 194 }; 195 196 // Retrieve the function from the program. 197 let function = self.get_function(console_request.function_name())?; 198 // Retrieve the number of inputs. 199 let num_inputs = function.inputs().len(); 200 // Ensure the number of inputs matches the number of input statements. 201 if num_inputs != console_request.inputs().len() { 202 bail!("Expected {num_inputs} inputs, found {}", console_request.inputs().len()) 203 } 204 // Retrieve the input types. 205 let input_types = function.input_types(); 206 // Retrieve the output types. 207 let output_types = function.output_types(); 208 lap!(timer, "Retrieve the input and output types"); 209 210 // Ensure the inputs match their expected types. 211 console_request.inputs().iter().zip_eq(&input_types).try_for_each(|(input, input_type)| { 212 // Ensure the input matches the input type in the function. 213 self.matches_value_type(input, input_type) 214 })?; 215 lap!(timer, "Verify the input types"); 216 217 // Retrieve the program checksum, if the program has a constructor. 218 let program_checksum = match self.program().contains_constructor() { 219 true => Some(self.program_checksum_as_field()?), 220 false => None, 221 }; 222 223 // Ensure the request is well-formed. 224 ensure!( 225 console_request.verify(&input_types, console_is_root, program_checksum), 226 "[Execute] Request is invalid" 227 ); 228 lap!(timer, "Verify the console request"); 229 230 // Initialize the registers. 231 let mut registers = Registers::new(call_stack, self.get_register_types(function.name())?.clone()); 232 233 // Set the root tvk, from a parent request or the current request. 234 let console_root_tvk = console_root_tvk.unwrap_or(*console_request.tvk()); 235 // Inject the `root_tvk` as `Mode::Private`. 236 let root_tvk = circuit::Field::<A>::new(circuit::Mode::Private, console_root_tvk); 237 // Set the root tvk. 238 registers.set_root_tvk(console_root_tvk); 239 // Set the root tvk, as a circuit. 240 registers.set_root_tvk_circuit(root_tvk.clone()); 241 242 // If a program checksum was passed in, Inject it as `Mode::Public`. 243 let program_checksum = program_checksum.map(|c| circuit::Field::<A>::new(circuit::Mode::Public, c)); 244 245 use circuit::{Eject, Inject}; 246 247 // Inject the transition public key `tpk` as `Mode::Public`. 248 let tpk = circuit::Group::<A>::new(circuit::Mode::Public, console_request.to_tpk()); 249 // Inject the request as `Mode::Private`. 250 let request = circuit::Request::new(circuit::Mode::Private, console_request.clone()); 251 252 // Inject `is_root` as `Mode::Public`. 253 let is_root = circuit::Boolean::new(circuit::Mode::Public, console_is_root); 254 // Inject the parent as `Mode::Public`. 255 let parent = circuit::Address::new(circuit::Mode::Public, console_parent); 256 // Determine the caller. 257 let caller = Ternary::ternary(&is_root, request.signer(), &parent); 258 259 // Ensure the request has a valid signature, inputs, and transition view key. 260 A::assert(request.verify(&input_types, &tpk, Some(root_tvk), is_root, program_checksum)); 261 lap!(timer, "Verify the circuit request"); 262 263 // Set the transition signer. 264 registers.set_signer(*console_request.signer()); 265 // Set the transition signer, as a circuit. 266 registers.set_signer_circuit(request.signer().clone()); 267 268 // Set the transition caller. 269 registers.set_caller(caller.eject_value()); 270 // Set the transition caller, as a circuit. 271 registers.set_caller_circuit(caller); 272 273 // Set the transition view key. 274 registers.set_tvk(*console_request.tvk()); 275 // Set the transition view key, as a circuit. 276 registers.set_tvk_circuit(request.tvk().clone()); 277 278 lap!(timer, "Initialize the registers"); 279 280 Self::log_circuit::<A>("Request"); 281 282 // Retrieve the number of constraints for verifying the request in the circuit. 283 let num_request_constraints = A::num_constraints(); 284 285 // Retrieve the number of public variables in the circuit. 286 let num_public = A::num_public(); 287 288 // Store the inputs. 289 function.inputs().iter().map(|i| i.register()).zip_eq(request.inputs()).try_for_each(|(register, input)| { 290 // If the circuit is in execute mode, then store the console input. 291 if let CallStack::Execute(..) = registers.call_stack_ref() { 292 // Assign the console input to the register. 293 registers.store(self, register, input.eject_value())?; 294 } 295 // Assign the circuit input to the register. 296 registers.store_circuit(self, register, input.clone()) 297 })?; 298 lap!(timer, "Store the inputs"); 299 300 // Initialize a tracker to determine if there are any function calls. 301 let mut contains_function_call = false; 302 303 // Execute the instructions. 304 for instruction in function.instructions() { 305 // If the circuit is in execute mode, then evaluate the instructions. 306 if let CallStack::Execute(..) = registers.call_stack_ref() { 307 // Evaluate the instruction. 308 let result = match instruction { 309 // If the instruction is a `call` instruction, we need to handle it separately. 310 Instruction::Call(call) => CallTrait::evaluate(call, self, &mut registers, rng), 311 // Otherwise, evaluate the instruction normally. 312 _ => instruction.evaluate(self, &mut registers), 313 }; 314 // If the evaluation fails, bail and return the error. 315 if let Err(error) = result { 316 bail!("Failed to evaluate instruction ({instruction}): {error}"); 317 } 318 } 319 320 // Execute the instruction. 321 let result = match instruction { 322 // If the instruction is a `call` instruction, we need to handle it separately. 323 Instruction::Call(call) => CallTrait::execute(call, self, &mut registers, rng), 324 // Otherwise, execute the instruction normally. 325 _ => instruction.execute(self, &mut registers), 326 }; 327 // If the execution fails, bail and return the error. 328 if let Err(error) = result { 329 bail!("Failed to execute instruction ({instruction}): {error}"); 330 } 331 332 // If the instruction was a function call, then set the tracker to `true`. 333 if let Instruction::Call(call) = instruction { 334 // Check if the call is a function call. 335 if call.is_function_call(self)? { 336 contains_function_call = true; 337 } 338 } 339 } 340 lap!(timer, "Execute the instructions"); 341 342 // Load the outputs. 343 let output_operands = &function.outputs().iter().map(|output| output.operand()).collect::<Vec<_>>(); 344 let outputs = output_operands 345 .iter() 346 .map(|operand| { 347 match operand { 348 // If the operand is a literal, use the literal directly. 349 Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from( 350 circuit::Literal::new(circuit::Mode::Constant, literal.clone()), 351 ))), 352 // If the operand is a register, retrieve the stack value from the register. 353 Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())), 354 // If the operand is the program ID, convert the program ID into an address. 355 Operand::ProgramID(program_id) => { 356 Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address( 357 circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?), 358 )))) 359 } 360 // If the operand is the signer, retrieve the signer from the registers. 361 Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from( 362 circuit::Literal::Address(registers.signer_circuit()?), 363 ))), 364 // If the operand is the caller, retrieve the caller from the registers. 365 Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from( 366 circuit::Literal::Address(registers.caller_circuit()?), 367 ))), 368 // If the operand is the block height, throw an error. 369 Operand::BlockHeight => { 370 bail!("Illegal operation: cannot retrieve the block height in a function scope") 371 } 372 // If the operand is the block timestamp, throw an error. 373 Operand::BlockTimestamp => { 374 bail!("Illegal operation: cannot retrieve the block timestamp in a function scope") 375 } 376 // If the operand is the network id, throw an error. 377 Operand::NetworkID => { 378 bail!("Illegal operation: cannot retrieve the network id in a function scope") 379 } 380 // If the operand is the checksum, throw an error. 381 Operand::Checksum(_) => { 382 bail!("Illegal operation: cannot retrieve the checksum in a function scope") 383 } 384 // If the operand is the edition, throw an error. 385 Operand::Edition(_) => { 386 bail!("Illegal operation: cannot retrieve the edition in a function scope") 387 } 388 // If the operand is the program owner, throw an error. 389 Operand::ProgramOwner(_) => { 390 bail!("Illegal operation: cannot retrieve the program owner in a function scope") 391 } 392 } 393 }) 394 .collect::<Result<Vec<_>>>()?; 395 lap!(timer, "Load the outputs"); 396 397 // Map the output operands into registers. 398 let output_registers = output_operands 399 .iter() 400 .map(|operand| match operand { 401 Operand::Register(register) => Some(register.clone()), 402 _ => None, 403 }) 404 .collect::<Vec<_>>(); 405 406 Self::log_circuit::<A>(format!("Function '{}()'", function.name())); 407 408 // Retrieve the number of constraints for executing the function in the circuit. 409 let num_function_constraints = A::num_constraints().saturating_sub(num_request_constraints); 410 411 // If the function does not contain function calls, ensure no new public variables were injected. 412 if !contains_function_call { 413 // Ensure the number of public variables remains the same. 414 ensure!(A::num_public() == num_public, "Instructions in function injected public variables"); 415 } 416 417 // Construct the response. 418 let response = circuit::Response::from_outputs( 419 request.signer(), 420 request.network_id(), 421 request.program_id(), 422 request.function_name(), 423 num_inputs, 424 request.tvk(), 425 request.tcm(), 426 outputs, 427 &output_types, 428 &output_registers, 429 ); 430 lap!(timer, "Construct the response"); 431 432 Self::log_circuit::<A>("Response"); 433 434 // Retrieve the number of constraints for verifying the response in the circuit. 435 let num_response_constraints = 436 A::num_constraints().saturating_sub(num_request_constraints).saturating_sub(num_function_constraints); 437 438 Self::log_circuit::<A>("Complete"); 439 440 // Eject the response. 441 let response = response.eject_value(); 442 443 // Ensure the outputs matches the expected value types. 444 response.outputs().iter().zip_eq(&output_types).try_for_each(|(output, output_type)| { 445 // Ensure the output matches its expected type. 446 self.matches_value_type(output, output_type) 447 })?; 448 449 // If the circuit is in `Execute` or `PackageRun` mode, then ensure the circuit is satisfied. 450 if matches!(registers.call_stack_ref(), CallStack::Execute(..) | CallStack::PackageRun(..)) { 451 // If the circuit is empty or not satisfied, then throw an error. 452 ensure!( 453 A::num_constraints() > 0 && A::is_satisfied(), 454 "'{}/{}' is not satisfied on the given inputs ({} constraints).", 455 self.program.id(), 456 function.name(), 457 A::num_constraints() 458 ); 459 } 460 461 // Eject the circuit assignment and reset the circuit. 462 let assignment = A::eject_assignment_and_reset(); 463 464 // If the circuit is in `Synthesize` or `Execute` mode, synthesize the circuit key, if it does not exist. 465 if matches!(registers.call_stack_ref(), CallStack::Synthesize(..) | CallStack::Execute(..)) { 466 // If the proving key does not exist, then synthesize it. 467 if !self.contains_proving_key(function.name()) { 468 // Add the circuit key to the mapping. 469 self.synthesize_from_assignment(function.name(), &assignment)?; 470 lap!(timer, "Synthesize the {} circuit key", function.name()); 471 } 472 } 473 // If the circuit is in `Authorize` mode, then save the transition. 474 if let CallStack::Authorize(_, _, authorization) = registers.call_stack_ref() { 475 // Construct the transition. 476 let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?; 477 // Add the transition to the authorization. 478 authorization.insert_transition(transition)?; 479 lap!(timer, "Save the transition"); 480 } 481 // If the circuit is in `CheckDeployment` mode, then save the assignment. 482 else if let CallStack::CheckDeployment(_, _, assignments, _, _) = registers.call_stack_ref() { 483 // Construct the call metrics. 484 let metrics = CallMetrics { 485 program_id: *self.program_id(), 486 function_name: *function.name(), 487 num_instructions: function.instructions().len(), 488 num_request_constraints, 489 num_function_constraints, 490 num_response_constraints, 491 }; 492 // Add the assignment to the assignments. 493 assignments.write().push((assignment, metrics)); 494 lap!(timer, "Save the circuit assignment"); 495 } 496 // If the circuit is in `Execute` mode, then execute the circuit into a transition. 497 else if let CallStack::Execute(_, trace) = registers.call_stack_ref() { 498 registers.ensure_console_and_circuit_registers_match()?; 499 500 // Construct the transition. 501 let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?; 502 503 // Retrieve the proving key. 504 let proving_key = self.get_proving_key(function.name())?; 505 // Construct the call metrics. 506 let metrics = CallMetrics { 507 program_id: *self.program_id(), 508 function_name: *function.name(), 509 num_instructions: function.instructions().len(), 510 num_request_constraints, 511 num_function_constraints, 512 num_response_constraints, 513 }; 514 515 // Add the transition to the trace. 516 trace.write().insert_transition( 517 console_request.input_ids(), 518 &transition, 519 (proving_key, assignment), 520 metrics, 521 )?; 522 } 523 // If the circuit is in `PackageRun` mode, then save the assignment. 524 else if let CallStack::PackageRun(_, _, assignments) = registers.call_stack_ref() { 525 // Construct the call metrics. 526 let metrics = CallMetrics { 527 program_id: *self.program_id(), 528 function_name: *function.name(), 529 num_instructions: function.instructions().len(), 530 num_request_constraints, 531 num_function_constraints, 532 num_response_constraints, 533 }; 534 // Add the assignment to the assignments. 535 assignments.write().push((assignment, metrics)); 536 lap!(timer, "Save the circuit assignment"); 537 } 538 539 finish!(timer); 540 541 // Return the response. 542 Ok(response) 543 } 544 } 545 546 impl<N: Network> Stack<N> { 547 /// Prints the current state of the circuit. 548 #[allow(unused_variables)] 549 pub(crate) fn log_circuit<A: circuit::Aleo<Network = N>>(scope: impl std::fmt::Display) { 550 #[cfg(debug_assertions)] 551 { 552 use alphavm_utilities::dev_println; 553 554 use colored::Colorize as _; 555 556 // Determine if the circuit is satisfied. 557 let is_satisfied = if A::is_satisfied() { "✅" } else { "❌" }; 558 // Determine the count. 559 let (num_constant, num_public, num_private, num_constraints, num_nonzeros) = A::count(); 560 561 let scope = scope.to_string().bold(); 562 563 // Print the log. 564 dev_println!( 565 "{is_satisfied} {scope:width$} (Constant: {num_constant}, Public: {num_public}, Private: {num_private}, Constraints: {num_constraints}, NonZeros: {num_nonzeros:?})", 566 width = 20 567 ); 568 } 569 } 570 }