-
Notifications
You must be signed in to change notification settings - Fork 327
/
Copy pathmod.nr
185 lines (161 loc) · 5.91 KB
/
mod.nr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use super::utils::compute_fn_selector;
use std::panic;
/// 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 mut parameters_size = 0;
for param in parameters {
parameters_size += size_in_fields(param.1);
}
let initial_read = if parameters.len() == 0 {
quote {}
} else {
// The initial calldata_copy offset is 1 to skip the Field selector
// The expected calldata is the serialization of
// - FunctionSelector: the selector of the function intended to dispatch
// - Parameters: the parameters of the function intended to dispatch
// That is, exactly what is expected for a call to the target function,
// but with a selector added at the beginning.
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 parameter_index = &mut 0;
let reads = parameters.map(|param: (Quoted, Type)| {
let parameter_index_value = *parameter_index;
let param_name = f"arg{parameter_index_value}".quoted_contents();
let param_type = param.1;
let read = quote {
let $param_name: $param_type = reader.read_struct(dep::aztec::protocol_types::traits::Deserialize::deserialize);
};
*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;
// Force early return.
dep::aztec::context::public_context::avm_return([]);
}
} else {
quote {
let return_value = dep::aztec::protocol_types::traits::Serialize::serialize($call);
dep::aztec::context::public_context::avm_return(return_value.as_slice());
}
};
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 {selector}") });
let dispatch = ifs.join(quote { });
let body = quote {
// We mark this as public because our whole system depends on public
// functions having this attribute. However, the public MACRO will
// handle the public_dispatch function specially and do nothing.
#[public]
pub unconstrained fn public_dispatch(selector: Field) {
$dispatch
}
};
body
}
}
comptime fn size_in_fields(typ: Type) -> u32 {
let size = array_size_in_fields(typ);
let size = size.or_else(|| bool_size_in_fields(typ));
let size = size.or_else(|| constant_size_in_fields(typ));
let size = size.or_else(|| field_size_in_fields(typ));
let size = size.or_else(|| int_size_in_fields(typ));
let size = size.or_else(|| str_size_in_fields(typ));
let size = size.or_else(|| struct_size_in_fields(typ));
let size = size.or_else(|| tuple_size_in_fields(typ));
if size.is_some() {
size.unwrap()
} else {
panic(f"Can't determine size in fields of {typ}")
}
}
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 bool_size_in_fields(typ: Type) -> Option<u32> {
if typ.is_bool() {
Option::some(1)
} else {
Option::none()
}
}
comptime fn field_size_in_fields(typ: Type) -> Option<u32> {
if typ.is_field() {
Option::some(1)
} else {
Option::none()
}
}
comptime fn int_size_in_fields(typ: Type) -> Option<u32> {
if typ.as_integer().is_some() {
Option::some(1)
} else {
Option::none()
}
}
comptime fn constant_size_in_fields(typ: Type) -> Option<u32> {
typ.as_constant()
}
comptime fn str_size_in_fields(typ: Type) -> Option<u32> {
typ.as_str().map(|typ| 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 generics = typ.1;
let mut size = 0;
for field in struct_type.fields(generics) {
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)
}