Skip to content

Commit

Permalink
dispatch function
Browse files Browse the repository at this point in the history
  • Loading branch information
fcarreiro committed Sep 26, 2024
1 parent 951ce6d commit 1d84218
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 11 deletions.
4 changes: 3 additions & 1 deletion avm-transpiler/src/transpile_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ impl From<CompiledAcirContractArtifact> for TranspiledContractArtifact {

for function in contract.functions {
// TODO(4269): once functions are tagged for transpilation to AVM, check tag
if function.custom_attributes.contains(&"public".to_string()) {
if function.custom_attributes.contains(&"public".to_string())
|| function.name == "public_dispatch"
{
info!("Transpiling AVM function {} on contract {}", function.name, contract.name);
// Extract Brillig Opcodes from acir
let acir_program = function.bytecode;
Expand Down
150 changes: 150 additions & 0 deletions noir-projects/aztec-nr/aztec/src/macros/dispatch/mod.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use super::utils::compute_fn_selector;

/// Returns an `fn public_dispatch(...)` function for the given module that's assumed to be an Aztec contract.
pub comptime fn generate_public_dispatch(m: Module) -> Quoted {
let functions = m.functions();
let functions = functions.filter(|function: FunctionDefinition| function.has_named_attribute("public"));

let unit = get_type::<()>();

let ifs = functions.map(
|function: FunctionDefinition| {
let name = function.name();
let parameters = function.parameters();
let return_type = function.return_type();

let selector: Field = compute_fn_selector(function);

let param_sizes = parameters.map(|param: (Quoted, Type)| {
size_in_fields(param.1)
});

let mut parameters_size = 0;
for param_size in param_sizes {
parameters_size += param_size;
}

let initial_read = if parameters.len() == 0 {
quote {}
} else {
// The initial calldata_copy offset is 1 to skip the Field selector
quote {
let input_calldata: [Field; $parameters_size] = dep::aztec::context::public_context::calldata_copy(1, $parameters_size);
let mut reader = dep::aztec::protocol_types::utils::reader::Reader::new(input_calldata);
}
};

let mut parameter_index = 0;
let mut offset = 0;
let reads = parameters.map(|param: (Quoted, Type)| {
// let param_size = param_sizes[parameter_index];
let param_name = f"arg{parameter_index}".quoted_contents();
let param_type = param.1;
let read = quote {
// let $param_name: $param_type = reader.read_struct((($param_type)::deserialize));
let $param_name: $param_type = reader.read_struct(dep::aztec::protocol_types::traits::Deserialize::deserialize);
// let ($param_name, _) = dep::aztec::protocol_types::traits::FromCalldata::from_calldata(input_calldata, $offset);
};
// offset += param_size;
parameter_index += 1;
quote { $read }
});
let read = reads.join(quote { });

let mut args = &[];
for parameter_index in 0..parameters.len() {
let param_name = f"arg{parameter_index}".quoted_contents();
args = args.push_back(quote { $param_name });
}

let args = args.join(quote { , });
let call = quote { $name($args) };

let return_code = if return_type == unit {
quote { $call }
} else {
quote {
let return_value = dep::aztec::protocol_types::traits::Serialize::serialize($call);
dep::aztec::context::public_context::avm_return(return_value);
}
};

let if_ = quote {
if selector == $selector {
$initial_read
$read
$return_code
}
};
if_
}
);

if ifs.len() == 0 {
// No dispatch function if there are no public functions
quote {}
} else {
let ifs = ifs.push_back(quote { { panic(f"Unknown selector") } });
let dispatch = ifs.join(quote { else });

let body = quote {
unconstrained pub fn public_dispatch(selector: Field) {
$dispatch
}
};

body
}
}

comptime fn size_in_fields(typ: Type) -> u32 {
if typ.as_slice().is_some() {
panic(f"Can't determine size in fields of Slice type")
} else {
let size = array_size_in_fields(typ);
let size = size.or_else(|| struct_size_in_fields(typ));
let size = size.or_else(|| tuple_size_in_fields(typ));
size.unwrap_or(1)
}
}

comptime fn array_size_in_fields(typ: Type) -> Option<u32> {
typ.as_array().and_then(
|typ: (Type, Type)| {
let (typ, element_size) = typ;
element_size.as_constant().map(|x: u32| {
x * size_in_fields(typ)
})
}
)
}

comptime fn struct_size_in_fields(typ: Type) -> Option<u32> {
typ.as_struct().map(
|typ: (StructDefinition, [Type])| {
let struct_type = typ.0;
let mut size = 0;
for field in struct_type.fields() {
size += size_in_fields(field.1);
}
size
}
)
}

comptime fn tuple_size_in_fields(typ: Type) -> Option<u32> {
typ.as_tuple().map(
|types: [Type]| {
let mut size = 0;
for typ in types {
size += size_in_fields(typ);
}
size
}
)
}

comptime fn get_type<T>() -> Type {
let t: T = std::mem::zeroed();
std::meta::type_of(t)
}
12 changes: 8 additions & 4 deletions noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,14 @@ pub comptime fn public(f: FunctionDefinition) -> Quoted {
let args_len = original_params.map(|(name, typ): (Quoted, Type)| flatten_to_fields(name, typ, &[]).0.len()).fold(0, |acc: u32, val: u32| acc + val);

// Unlike in the private case, in public the `context` does not need to receive the hash of the original params.
let context_creation = quote { let mut context = dep::aztec::context::public_context::PublicContext::new(|| {
let serialized_args : [Field; $args_len] = dep::aztec::context::public_context::calldata_copy(0, $args_len);
dep::aztec::hash::hash_args_array(serialized_args)
}); };
let context_creation = quote {
let mut context = dep::aztec::context::public_context::PublicContext::new(|| {
// The +1 is due to the function selector in the dispatch function.
// This function, as all public functions, is only expected to be called through "public_dispatch".
let serialized_args : [Field; $args_len] = dep::aztec::context::public_context::calldata_copy(0, $args_len + 1);
dep::aztec::hash::hash_args_array(serialized_args)
});
};

// Modifications introduced by the different marker attributes.
let internal_check = if is_fn_internal(f) {
Expand Down
7 changes: 4 additions & 3 deletions noir-projects/aztec-nr/aztec/src/macros/mod.nr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod dispatch;
mod functions;
mod utils;
mod notes;
Expand All @@ -10,26 +11,26 @@ use notes::{NOTES, generate_note_export};

use functions::transform_unconstrained;
use utils::module_has_storage;
use dispatch::generate_public_dispatch;

/// Marks a contract as an Aztec contract, generating the interfaces for its functions and notes, as well as injecting
/// the `compute_note_hash_and_optionally_a_nullifier` function PXE requires in order to validate notes.
pub comptime fn aztec(m: Module) -> Quoted {
let interface = generate_contract_interface(m);

let unconstrained_functions = m.functions().filter(
| f: FunctionDefinition | f.is_unconstrained() & !f.has_named_attribute("test") & !f.has_named_attribute("public")
);
for f in unconstrained_functions {
transform_unconstrained(f);
}

let compute_note_hash_and_optionally_a_nullifier = generate_compute_note_hash_and_optionally_a_nullifier();
let note_exports = generate_note_exports();

let public_dispatch = generate_public_dispatch(m);
quote {
$note_exports
$interface
$compute_note_hash_and_optionally_a_nullifier
$public_dispatch
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl Deserialize<BOOL_SERIALIZED_LEN> for bool {
}

impl Serialize<U8_SERIALIZED_LEN> for u8 {
fn serialize(self) -> [Field; U32_SERIALIZED_LEN] {
fn serialize(self) -> [Field; U8_SERIALIZED_LEN] {
[self as Field]
}
}
Expand Down Expand Up @@ -56,7 +56,7 @@ impl Deserialize<U64_SERIALIZED_LEN> for u64 {
}

impl Serialize<U128_SERIALIZED_LEN> for U128 {
fn serialize(self) -> [Field; 1] {
fn serialize(self) -> [Field; U128_SERIALIZED_LEN] {
[self.to_integer()]
}
}
Expand All @@ -68,7 +68,7 @@ impl Deserialize<U128_SERIALIZED_LEN> for U128 {
}

impl Serialize<FIELD_SERIALIZED_LEN> for Field {
fn serialize(self) -> [Field; U32_SERIALIZED_LEN] {
fn serialize(self) -> [Field; FIELD_SERIALIZED_LEN] {
[self]
}
}
Expand All @@ -78,3 +78,120 @@ impl Deserialize<FIELD_SERIALIZED_LEN> for Field {
fields[0]
}
}

impl <T, let N: u32, let M: u32> Serialize<N * M> for [T; N] where T: Serialize<M> {
fn serialize(self) -> [Field; N * M] {
// TODO: Implement
[0; N*M]//...
}
}

impl <T, let N: u32, let M: u32> Deserialize<N * M> for [T; N] where T: Deserialize<M>{
fn deserialize(fields: [Field; N * M]) -> Self {
let mut reader = crate::utils::reader::Reader::new(fields);
let mut result: [T; N] = std::mem::zeroed();
reader.read_struct_array::<T, M, N>(Deserialize::deserialize, result)
}
}

// impl <T, let N: u32> Deserialize<N> for [u8; N] {
// fn deserialize(fields: [Field; N]) -> Self {
// fields.map(|value| value as u8)
// }
// }

// impl <let N: u32> Serialize<N> for [u8; N] {
// fn serialize(self) -> [Field; N] {
// self.map(|value| value as Field)
// }
// }

// impl <let N: u32> Deserialize<N> for [u8; N] {
// fn deserialize(fields: [Field; N]) -> Self {
// fields.map(|value| value as u8)
// }
// }

// impl <let N: u32> Serialize<N> for [u16; N] {
// fn serialize(self) -> [Field; N] {
// self.map(|value| value as Field)
// }
// }

// impl <let N: u32> Deserialize<N> for [u16; N] {
// fn deserialize(fields: [Field; N]) -> Self {
// fields.map(|value| value as u16)
// }
// }

// impl <let N: u32> Serialize<N> for [u32; N] {
// fn serialize(self) -> [Field; N] {
// self.map(|value| value as Field)
// }
// }

// impl <let N: u32> Deserialize<N> for [u32; N] {
// fn deserialize(fields: [Field; N]) -> Self {
// fields.map(|value| value as u32)
// }
// }

// impl <let N: u32> Serialize<N> for [u64; N] {
// fn serialize(self) -> [Field; N] {
// self.map(|value| value as Field)
// }
// }

// impl <let N: u32> Deserialize<N> for [u64; N] {
// fn deserialize(fields: [Field; N]) -> Self {
// fields.map(|value| value as u64)
// }
// }

// impl <let N: u32> Serialize<N> for [i8; N] {
// fn serialize(self) -> [Field; N] {
// self.map(|value| value as Field)
// }
// }

// impl <let N: u32> Deserialize<N> for [i8; N] {
// fn deserialize(fields: [Field; N]) -> Self {
// fields.map(|value| value as i8)
// }
// }

// impl <let N: u32> Serialize<N> for [i16; N] {
// fn serialize(self) -> [Field; N] {
// self.map(|value| value as Field)
// }
// }

// impl <let N: u32> Deserialize<N> for [i16; N] {
// fn deserialize(fields: [Field; N]) -> Self {
// fields.map(|value| value as i16)
// }
// }

// impl <let N: u32> Serialize<N> for [i32; N] {
// fn serialize(self) -> [Field; N] {
// self.map(|value| value as Field)
// }
// }

// impl <let N: u32> Deserialize<N> for [i32; N] {
// fn deserialize(fields: [Field; N]) -> Self {
// fields.map(|value| value as i32)
// }
// }

// impl <let N: u32> Serialize<N> for [i64; N] {
// fn serialize(self) -> [Field; N] {
// self.map(|value| value as Field)
// }
// }

// impl <let N: u32> Deserialize<N> for [i64; N] {
// fn deserialize(fields: [Field; N]) -> Self {
// fields.map(|value| value as i64)
// }
// }

0 comments on commit 1d84218

Please sign in to comment.