Skip to content

Commit

Permalink
Merge pull request #2128 from AleoHQ/fix/ignore-finalize
Browse files Browse the repository at this point in the history
[Fix] Validating futures.
  • Loading branch information
howardwu authored Oct 25, 2023
2 parents 501c1bf + b686e49 commit 06a4f8e
Show file tree
Hide file tree
Showing 22 changed files with 423 additions and 17 deletions.
32 changes: 30 additions & 2 deletions synthesizer/process/src/stack/finalize_types/initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,46 @@ impl<N: Network> FinalizeTypes<N> {
// Initialize a map of registers to their types.
let mut finalize_types = Self { inputs: IndexMap::new(), destinations: IndexMap::new() };

// Step 1. Check the inputs are well-formed.
// Initialize a list of input futures.
let mut input_futures = Vec::new();

// Step 1. Check the inputs are well-formed. Store the input futures.
for input in finalize.inputs() {
// Check the input register type.
finalize_types.check_input(stack, input.register(), input.finalize_type())?;

// If the input is a future, add it to the list of input futures.
if let FinalizeType::Future(locator) = input.finalize_type() {
input_futures.push((input.register(), *locator));
}
}

// Step 2. Check the commands are well-formed.
// Initialize a list of consumed futures.
let mut consumed_futures = Vec::new();

// Step 2. Check the commands are well-formed. Store the futures consumed by the `await` commands.
for command in finalize.commands() {
// Check the command opcode, operands, and destinations.
finalize_types.check_command(stack, finalize, command)?;

// If the command is an `await`, add the future to the list of consumed futures.
if let Command::Await(await_) = command {
// Note: `check_command` ensures that the register is a future. This is an additional check.
let locator = match finalize_types.get_type(stack, await_.register())? {
FinalizeType::Future(locator) => locator,
FinalizeType::Plaintext(..) => bail!("Expected a future in '{await_}'"),
};
consumed_futures.push((await_.register(), locator));
}
}

// Check that the input futures are consumed in the order they are passed in.
ensure!(
input_futures == consumed_futures,
"Futures in finalize '{}' are not awaited in the order they are passed in.",
finalize.name()
);

Ok(finalize_types)
}
}
Expand Down
43 changes: 28 additions & 15 deletions synthesizer/process/src/stack/register_types/initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,33 +164,32 @@ impl<N: Network> RegisterTypes<N> {
let mut future_registers = register_types
.destinations
.iter()
.filter_map(|(_, register_type)| match register_type {
RegisterType::Future(locator) => Some(*locator),
.filter_map(|(index, register_type)| match register_type {
RegisterType::Future(locator) => Some((Register::Locator(*index), *locator)),
_ => None,
})
.collect::<Vec<_>>();

// Remove the last locator, since this is the future created by the `async` call.
future_registers.pop();

// Check that all the registers were consumed by the `async` call, in order.
match async_ {
// If no `async` instruction exists, then there should not be any future registers.
None => {
if !future_registers.is_empty() {
bail!(
"Function '{}' contains futures, but does not contain an 'async' instruction",
function.name()
)
}
ensure!(
future_registers.is_empty(),
"Function '{}' contains futures, but does not contain an 'async' instruction",
function.name()
)
}
// Otherwise, check that all the registers were consumed by the `async` call, in order.
Some(async_) => {
// Remove the last future, since this is the future created by the `async` call.
future_registers.pop();
// Get the register operands that are `future` types.
let async_future_operands = async_
.operands()
.iter()
.filter_map(|operand| match operand {
Operand::Register(register) => match register_types.get_type(stack, register).ok() {
Some(RegisterType::Future(locator)) => Some(locator),
Some(RegisterType::Future(locator)) => Some((register.clone(), locator)),
_ => None,
},
_ => None,
Expand Down Expand Up @@ -461,8 +460,22 @@ impl<N: Network> RegisterTypes<N> {

// Retrieve the program.
let external = stack.get_external_program(program_id)?;
// Ensure the function or closure exists in the program.
if !external.contains_function(resource) && !external.contains_closure(resource) {
// Check that function exists in the program.
if let Ok(child_function) = external.get_function_ref(resource) {
// If the child function contains a finalize block, then the parent function must also contain a finalize block.
let child_contains_finalize = child_function.finalize_logic().is_some();
let parent_contains_finalize =
stack.get_function_ref(closure_or_function_name)?.finalize_logic().is_some();
if child_contains_finalize && !parent_contains_finalize {
bail!(
"Function '{}/{closure_or_function_name}' must contain a finalize block, since it calls '{}/{resource}'.",
stack.program_id(),
program_id
)
}
}
// Otherwise, ensure the closure exists in the program.
else if !external.contains_closure(resource) {
bail!("'{resource}' is not defined in '{}'.", external.id())
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program child.aleo: ''child.aleo/foo'' does not have a finalize block'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program parent.aleo: The ''call'' can only be invoked before an ''async'' instruction'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program parent.aleo: Function ''foo'' contains futures, but the ''async'' instruction does not consume all of them in the order they were produced'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program parent.aleo: Function ''parent.aleo/foo'' must contain a finalize block, since it calls ''child.aleo/foo''.'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program child.aleo: The last output of function ''foo'' must be a future associated with itself'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program parent.aleo: Function ''foo'' can contain at most one ''async'' instruction'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program parent.aleo: Futures in finalize ''foo'' are not awaited in the order they are passed in.'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program parent.aleo: Function ''parent.aleo/foo'' must contain a finalize block, since it calls ''child.aleo/foo''.'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program parent.aleo: Futures in finalize ''foo'' are not awaited in the order they are passed in.'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
errors:
- 'Failed to run `VM::deploy for program parent.aleo: Function ''foo'' contains futures, but the ''async'' instruction does not consume all of them in the order they were produced'
outputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
randomness: 45791624
cases: []
*/

program child.aleo;

function foo:
input r0 as field.private;
input r1 as field.private;
async foo self.caller into r2;
add r0 r1 into r3;
output r3 as field.private;




Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
randomness: 45791624
cases: []
*/

program child.aleo;

mapping count:
key as address.public;
value as field.public;

function foo:
async foo self.caller into r0;
output r0 as child.aleo/foo.future;

finalize foo:
input r0 as address.public;
get.or_use count[r0] 0field into r1;
add r1 1field into r2;
set r2 into count[r0];

/////////////////////////////////////////////////

import child.aleo;

program parent.aleo;

function foo:
call child.aleo/foo into r0;
async foo r0 into r1;
call child.aleo/foo into r2;
output r1 as parent.aleo/foo.future;

finalize foo:
input r0 as child.aleo/foo.future;
await r0;

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
randomness: 45791624
cases: []
*/

program child.aleo;

mapping count:
key as address.public;
value as field.public;

function foo:
async foo self.caller into r0;
output r0 as child.aleo/foo.future;

finalize foo:
input r0 as address.public;
get.or_use count[r0] 0field into r1;
add r1 1field into r2;
set r2 into count[r0];

/////////////////////////////////////////////////

import child.aleo;

program parent.aleo;

function foo:
call child.aleo/foo into r0;
call child.aleo/foo into r1;
async foo r1 r0 into r2;
output r2 as parent.aleo/foo.future;

finalize foo:
input r0 as child.aleo/foo.future;
input r1 as child.aleo/foo.future;
await r0;
await r1;

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
randomness: 45791624
cases: []
*/

program child.aleo;

mapping count:
key as address.public;
value as field.public;

function foo:
async foo self.caller into r0;
output r0 as child.aleo/foo.future;

finalize foo:
input r0 as address.public;
get.or_use count[r0] 0field into r1;
add r1 1field into r2;
set r2 into count[r0];

/////////////////////////////////////////////////

import child.aleo;

program parent.aleo;

function foo:
input r0 as field.private;
input r1 as field.private;
call child.aleo/foo into r2;
add r0 r1 into r3;
output r3 as field.private;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
randomness: 45791624
cases: []
*/

program child.aleo;

mapping count:
key as address.public;
value as field.public;

function foo:
input r0 as field.private;
input r1 as field.private;
async foo self.caller into r2;
add r0 r1 into r3;
output r2 as child.aleo/foo.future;
output r3 as field.private;

finalize foo:
input r0 as address.public;
get.or_use count[r0] 0field into r1;
add r1 1field into r2;
set r2 into count[r0];

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
randomness: 45791624
cases: []
*/

program child.aleo;

mapping count:
key as address.public;
value as field.public;

function foo:
async foo self.caller into r0;
output r0 as child.aleo/foo.future;

finalize foo:
input r0 as address.public;
get.or_use count[r0] 0field into r1;
add r1 1field into r2;
set r2 into count[r0];

/////////////////////////////////////////////////

import child.aleo;

program parent.aleo;

function foo:
call child.aleo/foo into r0;
call child.aleo/foo into r1;
async foo r0 into r2;
async foo r1 into r3;
output r2 as parent.aleo/foo.future;

finalize foo:
input r0 as child.aleo/foo.future;
await r0;

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
randomness: 45791624
cases: []
*/

program child.aleo;

mapping count:
key as address.public;
value as field.public;

function foo:
async foo self.caller into r0;
output r0 as child.aleo/foo.future;

finalize foo:
input r0 as address.public;
get.or_use count[r0] 0field into r1;
add r1 1field into r2;
set r2 into count[r0];

/////////////////////////////////////////////////

import child.aleo;

program parent.aleo;

function foo:
call child.aleo/foo into r0;
call child.aleo/foo into r1;
async foo r0 r1 into r2;
output r2 as parent.aleo/foo.future;

finalize foo:
input r0 as child.aleo/foo.future;
input r1 as child.aleo/foo.future;
await r1;
await r0;

Loading

0 comments on commit 06a4f8e

Please sign in to comment.