Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1324,7 +1324,7 @@ workflows:
or pipeline.git.branch == "canary"
or pipeline.git.branch == "testnet"
or pipeline.git.branch == "mainnet"
or pipeline.git.branch == "revert_anchor_times"
or pipeline.git.branch == "fix/scalar-outputs"
jobs:
- check-unused-dependencies # This can be cleaned up before releases
- check-cargo-semver-checks # This can be cleaned up before releases
Expand Down
6 changes: 6 additions & 0 deletions circuit/program/src/data/future/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,10 @@ impl<A: Aleo> Future<A> {
pub fn inputs(&self) -> &[Argument<A>] {
&self.arguments
}

/// Returns the arguments.
#[inline]
pub fn arguments(&self) -> &[Argument<A>] {
&self.arguments
}
}
4 changes: 3 additions & 1 deletion synthesizer/process/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ impl<N: Network> Process<N> {
// Retrieve the stack.
let stack = self.get_stack(request.program_id())?;
// Execute the circuit.
let response = stack.execute_function::<A, R>(call_stack, caller, root_tvk, rng)?;
let Some(response) = stack.execute_function::<A, R>(call_stack, caller, root_tvk, rng)? else {
return Err(anyhow!("Response should be present in `Execute` mode.").into());
};
lap!(timer, "Execute the function");

// Extract the trace.
Expand Down
23 changes: 16 additions & 7 deletions synthesizer/process/src/stack/call/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,11 @@ impl<N: Network> CallTrait<N> for CallDynamic<N> {
authorization.push(callee_request.clone())?;

// Execute the callee's request.
let callee_response =
target.substack().execute_function::<A, R>(call_stack, console_caller, root_tvk, rng)?;
let Some(callee_response) =
target.substack().execute_function::<A, R>(call_stack, console_caller, root_tvk, rng)?
else {
return Err(anyhow!("Response should be present in `Authorize` mode.").into());
};

// Convert the callee's outputs to the caller's context.
let caller_response_outputs = callee_response.to_dynamic_outputs()?;
Expand Down Expand Up @@ -514,8 +517,11 @@ impl<N: Network> CallTrait<N> for CallDynamic<N> {
call_stack.push(callee_request.clone())?;

// Evaluate the callee's request.
let callee_response =
target.substack().execute_function::<A, _>(call_stack, console_caller, root_tvk, rng)?;
let Some(callee_response) =
target.substack().execute_function::<A, _>(call_stack, console_caller, root_tvk, rng)?
else {
return Err(anyhow!("Response should be present in `PackageRun` mode.").into());
};

// Convert the callee's outputs to the caller's context.
let caller_response_outputs = callee_response.to_dynamic_outputs()?;
Expand All @@ -525,7 +531,7 @@ impl<N: Network> CallTrait<N> for CallDynamic<N> {
}
// In `Evaluate` mode, throw an error.
CallStack::Evaluate(..) => {
return Err(anyhow!("Cannot 'execute' a function in 'evaluate' mode.").into());
return Err(anyhow!("Cannot 'execute' a function in 'Evaluate' mode.").into());
}
// In `Execute` mode, evaluate and execute the instructions.
CallStack::Execute(authorization, _, translations) => {
Expand Down Expand Up @@ -558,12 +564,15 @@ impl<N: Network> CallTrait<N> for CallDynamic<N> {
rng,
)?;
// Execute the request.
let callee_response = target.substack().execute_function::<A, R>(
let Some(callee_response) = target.substack().execute_function::<A, R>(
registers.call_stack(),
console_caller,
root_tvk,
rng,
)?;
)?
else {
return Err(anyhow!("Response should be present in `Execute` mode.").into());
};

// Ensure the values are equal.
if console_callee_response.outputs() != callee_response.outputs() {
Expand Down
136 changes: 72 additions & 64 deletions synthesizer/process/src/stack/call/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ impl<N: Network> CallTrait<N> for Call<N> {
}
// If the circuit is in AuthorizeMocked mode, throw an error.
CallStack::AuthorizeMocked(..) => {
return Err(anyhow!("Cannot 'execute' a function in 'authorize mocked' mode.").into());
return Err(anyhow!("Cannot 'execute' a function in 'AuthorizeMocked' mode.").into());
}
// If the circuit is in AuthorizeRequests mode, throw an error.
CallStack::AuthorizeRequests(..) => {
Expand Down Expand Up @@ -369,11 +369,18 @@ impl<N: Network> CallTrait<N> for Call<N> {
// Push the request onto the call stack.
call_stack.push(request.clone())?;

// Execute the request.
// Synthesize the circuit. Note the response will be empty.
let response = substack.execute_function::<A, R>(call_stack, console_caller, root_tvk, rng)?;

if response.is_some() {
return Err(anyhow!(
"execute_function should return an empty Response in 'Synthesize' mode."
)
.into());
}

// Return the request and response.
(request, response)
(request, None)
}
// In Synthesize mode (with an existing proving key) or CheckDeployment mode, we generate dummy outputs to avoid building a full sub-circuit.
CallStack::Synthesize(_, private_key, ..) | CallStack::CheckDeployment(_, private_key, ..) => {
Expand All @@ -391,63 +398,8 @@ impl<N: Network> CallTrait<N> for Call<N> {
rng,
)?;

// Compute the address.
let address = Address::try_from(private_key)?;

// For each output, if it's a record, compute the randomizer and nonce.
let outputs = function
.outputs()
.iter()
.map(|output| match output.value_type() {
ValueType::Record(record_name) => {
let index = match output.operand() {
Operand::Register(Register::Locator(index)) => Field::from_u64(*index),
_ => {
return Err(anyhow!(
"Expected a `Register::Locator` operand for a record output."
));
}
};
// Sample the record.
Ok(Value::Record(substack.sample_record_using_tvk(
&address,
record_name,
*request.tvk(),
index,
rng,
)?))
}
// For non-record outputs, call sample_value.
_ => substack.sample_value(&address, &output.value_type().into(), rng),
})
.collect::<Result<Vec<_>>>()?;

// Construct the dummy response from these outputs.
let output_registers = function
.outputs()
.iter()
.map(|output| match output.operand() {
Operand::Register(register) => Some(register.clone()),
_ => None,
})
.collect::<Vec<_>>();

// Execute the request.
let response = crate::Response::new(
request.signer(),
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)
(request, None)
}
// In PackageRun mode, we sign and execute the request once.
CallStack::PackageRun(_, private_key, ..) => {
Expand Down Expand Up @@ -478,7 +430,7 @@ impl<N: Network> CallTrait<N> for Call<N> {
}
// If the circuit is in evaluate mode, then throw an error.
CallStack::Evaluate(..) => {
return Err(anyhow!("Cannot 'execute' a function in 'evaluate' mode.").into());
return Err(anyhow!("Cannot 'execute' a function in 'Evaluate' mode.").into());
}
// If the circuit is in execute mode, then evaluate and execute the instructions.
CallStack::Execute(authorization, ..) => {
Expand Down Expand Up @@ -506,8 +458,12 @@ impl<N: Network> CallTrait<N> for Call<N> {
rng,
)?;
// Execute the request.
let response =
substack.execute_function::<A, R>(registers.call_stack(), console_caller, root_tvk, rng)?;
let Some(response) =
substack.execute_function::<A, R>(registers.call_stack(), console_caller, root_tvk, rng)?
else {
return Err(anyhow!("Response should be present in 'Execute' mode.").into());
};

// Ensure the values are equal.
if console_response.outputs() != response.outputs() {
dev_eprintln!("\n{:#?} != {:#?}\n", console_response.outputs(), response.outputs());
Expand All @@ -518,7 +474,7 @@ impl<N: Network> CallTrait<N> for Call<N> {
.into());
}
// Return the request and response.
(request, response)
(request, Some(response))
}
}
};
Expand Down Expand Up @@ -588,6 +544,58 @@ impl<N: Network> CallTrait<N> for Call<N> {
})
.collect::<Vec<_>>();

let outputs = match registers.call_stack_ref() {
// In Synthesize and CheckDeployment modes, `response` is None at this point. Only its outputs
// are used in the remainder of this function, and only their types (and not specific values)
// are relevant, so we sample them instead.
CallStack::Synthesize(_, private_key, ..) | CallStack::CheckDeployment(_, private_key, ..) => {
if response.is_some() {
return Err(
anyhow!("Response should be empty in 'Synthesize' and 'CheckDeployment' modes.").into()
);
}

let address = Address::try_from(private_key)?;

function
.outputs()
.iter()
.map(|output| match output.value_type() {
ValueType::Record(record_name) => {
let index = match output.operand() {
Operand::Register(Register::Locator(index)) => Field::from_u64(*index),
_ => {
return Err(anyhow!(
"Expected a `Register::Locator` operand for a record output."
));
}
};
// Sample the record.
Ok(Value::Record(substack.sample_record_using_tvk(
&address,
record_name,
*request.tvk(),
index,
rng,
)?))
}
// For non-record outputs, call sample_value.
_ => substack.sample_value(&address, &output.value_type().into(), rng),
})
.collect::<Result<Vec<_>>>()?
}
_ => {
if let Some(response) = response {
response.outputs().to_vec()
} else {
return Err(anyhow!(
"Response should be populated in all modes except 'Synthesize' and 'CheckDeployment'."
)
.into());
}
}
};

// Inject the outputs as `Mode::Private` (with the 'tcm' and output IDs as `Mode::Public`).
let outputs = circuit::Response::process_outputs_from_callback(
&network_id,
Expand All @@ -596,7 +604,7 @@ impl<N: Network> CallTrait<N> for Call<N> {
num_inputs,
&tvk,
&tcm,
response.outputs().to_vec(),
outputs,
&function.output_types(),
&output_registers,
None,
Expand Down
48 changes: 34 additions & 14 deletions synthesizer/process/src/stack/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ impl<N: Network> Stack<N> {
outputs
}

/// Executes a program function on the given inputs.
/// Executes a program function on the given inputs. The output includes a `Response` object if
/// not in `CheckDeployment` or `Synthesize` mode.
///
/// Note: To execute a transition, do **not** call this method. Instead, call `Process::execute`.
///
Expand All @@ -200,7 +201,7 @@ impl<N: Network> Stack<N> {
console_caller: Option<ProgramID<N>>,
console_root_tvk: Option<Field<N>>,
rng: &mut R,
) -> Result<Response<N>, StackExecError> {
) -> Result<Option<Response<N>>, StackExecError> {
let timer = timer!("Stack::execute_function");

// Ensure the global constants for the Aleo environment are initialized.
Expand Down Expand Up @@ -550,17 +551,26 @@ impl<N: Network> Stack<N> {
Self::log_circuit::<A>("Complete", &call_stack_type);

// Eject the response.
let response = response.eject_value();
let console_response =
if matches!(registers.call_stack_ref(), CallStack::Synthesize(..) | CallStack::CheckDeployment(..)) {
// When synthesizing proving/verifying keys or checking the latter, the values in
// the Response object are not relevant, and neither is its console counterpart.
None
} else {
let console_response = response.eject_value();

if console_response.outputs().len() != output_types.len() {
return Err(anyhow!("Number of outputs does not match number of output types").into());
}

if response.outputs().len() != output_types.len() {
return Err(anyhow!("Number of outputs does not match number of output types").into());
}
// Ensure the outputs matches the expected value types.
console_response.outputs().iter().zip_eq(&output_types).try_for_each(|(output, output_type)| {
// Ensure the output matches its expected type.
self.matches_value_type(output, output_type)
})?;

// Ensure the outputs matches the expected value types.
response.outputs().iter().zip_eq(&output_types).try_for_each(|(output, output_type)| {
// Ensure the output matches its expected type.
self.matches_value_type(output, output_type)
})?;
Some(console_response)
};

// If the circuit is in `Execute` or `PackageRun` mode, then ensure the circuit is satisfied.
if matches!(registers.call_stack_ref(), CallStack::Execute(..) | CallStack::PackageRun(..)) {
Expand Down Expand Up @@ -591,7 +601,12 @@ impl<N: Network> Stack<N> {
// If the circuit is in `Authorize` mode, then save the transition.
if let CallStack::Authorize(_, _, authorization) = registers.call_stack_ref() {
// Construct the transition.
let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
let transition = Transition::from(
&console_request,
console_response.as_ref().unwrap(),

@mohammadfawaz mohammadfawaz Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe justify why this unwrap is safe (an expect maybe)

&output_types,
&output_registers,
)?;

// Add the transition to the authorization.
authorization.insert_transition(transition)?;
Expand All @@ -617,7 +632,12 @@ impl<N: Network> Stack<N> {
registers.ensure_console_and_circuit_registers_match()?;

// Construct the transition.
let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
let transition = Transition::from(
&console_request,
console_response.as_ref().unwrap(),

@mohammadfawaz mohammadfawaz Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. expect instead of unwrap?

&output_types,
&output_registers,
)?;

// Retrieve the proving key.
let proving_key = self.get_proving_key(function.name())?;
Expand Down Expand Up @@ -664,7 +684,7 @@ impl<N: Network> Stack<N> {
finish!(timer);

// Return the response.
Ok(response)
Ok(console_response)
}
}

Expand Down
1 change: 1 addition & 0 deletions synthesizer/process/src/stack/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ mod initialize;
mod sample;
mod stack_trait;
mod synthesize;
mod type_matching;
Loading