diff --git a/synthesizer/process/src/stack/call/mod.rs b/synthesizer/process/src/stack/call/mod.rs index 5d6578ab73..d34e31d91f 100644 --- a/synthesizer/process/src/stack/call/mod.rs +++ b/synthesizer/process/src/stack/call/mod.rs @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{CallStack, Registers, RegistersCall, StackEvaluate, StackExecute}; +use crate::{stack::Address, CallStack, Registers, RegistersCall, StackEvaluate, StackExecute}; use aleo_std::prelude::{finish, lap, timer}; use console::{network::prelude::*, program::Request}; use synthesizer_program::{ Call, CallOperator, + Operand, RegistersLoad, RegistersLoadCircuit, RegistersSigner, @@ -241,7 +242,7 @@ impl CallTrait for Call { // Return the request and response. (request, response) } - CallStack::CheckDeployment(_, private_key, ..) | CallStack::PackageRun(_, private_key, ..) => { + CallStack::PackageRun(_, private_key, ..) => { // Compute the request. let request = Request::sign( &private_key, @@ -257,8 +258,54 @@ impl CallTrait for Call { // Push the request onto the call stack. call_stack.push(request.clone())?; - // Execute the request. - let response = substack.execute_function::(call_stack, console_caller, rng)?; + // Evaluate the request. + let response = substack.evaluate_function::(call_stack, console_caller)?; + + // Return the request and response. + (request, response) + } + CallStack::CheckDeployment(_, private_key, ..) => { + // Compute the request. + let request = Request::sign( + &private_key, + *substack.program_id(), + *function.name(), + inputs.iter(), + &function.input_types(), + rng, + )?; + + // Compute the address. + let address = Address::try_from(&private_key)?; + // Sample dummy outputs + let outputs = function + .outputs() + .iter() + .map(|output| substack.sample_value(&address, output.value_type(), rng)) + .collect::>>()?; + // Map the output operands to registers. + let output_registers = function + .outputs() + .iter() + .map(|output| match output.operand() { + Operand::Register(register) => Some(register.clone()), + _ => None, + }) + .collect::>(); + + // Compute the response. + let response = crate::Response::new( + request.network_id(), + substack.program().id(), + function.name(), + request.inputs().len(), + request.tvk(), + request.tcm(), + outputs, + &function.output_types(), + &output_registers, + )?; + // Return the request and response. (request, response) } diff --git a/synthesizer/process/src/stack/evaluate.rs b/synthesizer/process/src/stack/evaluate.rs index 9dda9b6db2..6d6b6f5761 100644 --- a/synthesizer/process/src/stack/evaluate.rs +++ b/synthesizer/process/src/stack/evaluate.rs @@ -106,6 +106,10 @@ impl StackEvaluate for Stack { // Retrieve the next request, based on the call stack mode. let (request, call_stack) = match &call_stack { CallStack::Evaluate(authorization) => (authorization.next()?, call_stack), + CallStack::PackageRun(requests, _, _) => { + let last_request = requests.last().ok_or(anyhow!("CallStack does not contain request"))?.clone(); + (last_request, call_stack) + } // If the evaluation is performed in the `Execute` mode, create a new `Evaluate` mode. // This is done to ensure that evaluation during execution is performed consistently. CallStack::Execute(authorization, _) => { @@ -116,7 +120,9 @@ impl StackEvaluate for Stack { let call_stack = CallStack::Evaluate(authorization); (request, call_stack) } - _ => bail!("Illegal operation: call stack must be `Evaluate` or `Execute` in `evaluate_function`."), + _ => bail!( + "Illegal operation: call stack must be `PackageRun`, `Evaluate` or `Execute` in `evaluate_function`." + ), }; lap!(timer, "Retrieve the next request"); diff --git a/synthesizer/process/src/stack/helpers/matches.rs b/synthesizer/process/src/stack/helpers/matches.rs index 975045943f..4ed9a81de7 100644 --- a/synthesizer/process/src/stack/helpers/matches.rs +++ b/synthesizer/process/src/stack/helpers/matches.rs @@ -13,7 +13,6 @@ // limitations under the License. use super::*; -use console::program::{Argument, FinalizeType}; impl StackMatches for Stack { /// Checks that the given value matches the layout of the value type. diff --git a/synthesizer/process/src/stack/helpers/sample.rs b/synthesizer/process/src/stack/helpers/sample.rs index 0ad9f94171..c6a2dfe556 100644 --- a/synthesizer/process/src/stack/helpers/sample.rs +++ b/synthesizer/process/src/stack/helpers/sample.rs @@ -15,27 +15,6 @@ use super::*; impl Stack { - /// Returns a value for the given value type. - pub fn sample_value( - &self, - burner_address: &Address, - value_type: &ValueType, - rng: &mut R, - ) -> Result> { - match value_type { - ValueType::Constant(plaintext_type) - | ValueType::Public(plaintext_type) - | ValueType::Private(plaintext_type) => Ok(Value::Plaintext(self.sample_plaintext(plaintext_type, rng)?)), - ValueType::Record(record_name) => { - Ok(Value::Record(self.sample_record(burner_address, record_name, rng)?)) - } - ValueType::ExternalRecord(locator) => { - bail!("Illegal operation: Cannot sample external records (for '{locator}.record').") - } - ValueType::Future(locator) => bail!("Illegal operation: Cannot sample futures (for '{locator}.future')."), - } - } - /// Returns a record for the given record name, with the given burner address. pub fn sample_record( &self, @@ -64,6 +43,16 @@ impl Stack { // Return the plaintext value. Ok(plaintext) } + + /// Samples a future value according to the given future type. + pub fn sample_future(&self, locator: &Locator, rng: &mut R) -> Result> { + // Sample a future value. + let future = self.sample_future_internal(locator, 0, rng)?; + // Ensure the future value matches the future type. + self.matches_future(&future, locator)?; + // Return the future value. + Ok(future) + } } impl Stack { @@ -182,4 +171,46 @@ impl Stack { // Return the plaintext. Ok(plaintext) } + + /// Samples a future value according to the given locator. + fn sample_future_internal( + &self, + locator: &Locator, + depth: usize, + rng: &mut R, + ) -> Result> { + // Retrieve the associated function. + let function = match locator.program_id() == self.program_id() { + true => self.get_function_ref(locator.resource())?, + false => self.get_external_program(locator.program_id())?.get_function_ref(locator.resource())?, + }; + + // Retrieve the finalize inputs. + let inputs = match function.finalize_logic() { + Some(finalize_logic) => finalize_logic.inputs(), + None => bail!("Function '{locator}' does not have a finalize block"), + }; + + let arguments = inputs + .into_iter() + .map(|input| { + match input.finalize_type() { + FinalizeType::Plaintext(plaintext_type) => { + // Sample the plaintext value. + let plaintext = self.sample_plaintext_internal(plaintext_type, depth + 1, rng)?; + // Return the argument. + Ok(Argument::Plaintext(plaintext)) + } + FinalizeType::Future(locator) => { + // Sample the future value. + let future = self.sample_future_internal(locator, depth + 1, rng)?; + // Return the argument. + Ok(Argument::Future(future)) + } + } + }) + .collect::>>()?; + + Ok(Future::new(*locator.program_id(), *locator.resource(), arguments)) + } } diff --git a/synthesizer/process/src/stack/mod.rs b/synthesizer/process/src/stack/mod.rs index 2b95b46fb7..c432938a82 100644 --- a/synthesizer/process/src/stack/mod.rs +++ b/synthesizer/process/src/stack/mod.rs @@ -41,8 +41,10 @@ use console::{ account::{Address, PrivateKey}, network::prelude::*, program::{ + Argument, Entry, EntryType, + FinalizeType, Future, Identifier, Literal, @@ -292,6 +294,30 @@ impl StackProgram for Stack { } Ok(num_calls) } + + /// Returns a value for the given value type. + fn sample_value( + &self, + burner_address: &Address, + value_type: &ValueType, + rng: &mut R, + ) -> Result> { + match value_type { + ValueType::Constant(plaintext_type) + | ValueType::Public(plaintext_type) + | ValueType::Private(plaintext_type) => Ok(Value::Plaintext(self.sample_plaintext(plaintext_type, rng)?)), + ValueType::Record(record_name) => { + Ok(Value::Record(self.sample_record(burner_address, record_name, rng)?)) + } + ValueType::ExternalRecord(locator) => { + // Retrieve the external stack. + let stack = self.get_external_stack(locator.program_id())?; + // Sample the output. + Ok(Value::Record(stack.sample_record(burner_address, locator.resource(), rng)?)) + } + ValueType::Future(locator) => Ok(Value::Future(self.sample_future(locator, rng)?)), + } + } } impl StackProgramTypes for Stack { diff --git a/synthesizer/program/src/traits/stack_and_registers.rs b/synthesizer/program/src/traits/stack_and_registers.rs index 50418e894f..d3a5961141 100644 --- a/synthesizer/program/src/traits/stack_and_registers.rs +++ b/synthesizer/program/src/traits/stack_and_registers.rs @@ -35,6 +35,7 @@ use console::{ }, types::{Address, Field}, }; +use rand::{CryptoRng, Rng}; pub trait StackMatches { /// Checks that the given value matches the layout of the value type. @@ -83,6 +84,14 @@ pub trait StackProgram { /// Returns the expected number of calls for the given function name. fn get_number_of_calls(&self, function_name: &Identifier) -> Result; + + /// Samples a value for the given value_type. + fn sample_value( + &self, + burner_address: &Address, + value_type: &ValueType, + rng: &mut R, + ) -> Result>; } pub trait FinalizeRegistersState {