Skip to content

Commit 0a3f965

Browse files
authored
feat!: commence call interface unification (#15894)
You can just do things. In this case, delete ~700 lines and end up with a net improvement. This deletes all of the `Void` variants of the call interfaces, in turn deleting the `call_<>_void` variants, nicely unifying the testing experience - we now just `call_public` or `call_private` without worrying about return types. I also simplified `ReturnsHash` a bit since it had three ways of achieving the same simple tasks, and added some tests and clarifying comments about how the `()` case is handled. There's lots more to improve on the `CallInterfaces`, and many more removals to be done, but this initial pass is worth doing separately because it directly improves `TestEnvironment`'s API.
1 parent 1ab40f8 commit 0a3f965

File tree

32 files changed

+227
-852
lines changed

32 files changed

+227
-852
lines changed

noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr

Lines changed: 11 additions & 501 deletions
Large diffs are not rendered by default.

noir-projects/aztec-nr/aztec/src/context/mod.nr

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ pub mod call_interfaces;
1010
pub mod gas;
1111

1212
pub use call_interfaces::{
13-
PrivateCallInterface, PrivateStaticCallInterface, PrivateStaticVoidCallInterface,
14-
PrivateVoidCallInterface, PublicCallInterface, PublicStaticCallInterface,
15-
PublicStaticVoidCallInterface, PublicVoidCallInterface, UtilityCallInterface,
13+
PrivateCallInterface, PrivateStaticCallInterface, PublicCallInterface,
14+
PublicStaticCallInterface, UtilityCallInterface,
1615
};
1716
pub use private_context::PrivateContext;
1817
pub use public_context::PublicContext;
Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
use crate::{hash::hash_args_array, oracle::execution_cache};
22
use dep::protocol_types::traits::Deserialize;
33

4+
/// A hash that represents a private contract function call's return value. Call `get_preimage` to get the underlying
5+
/// value.
6+
///
7+
/// The kernels don't process the actual return values but instead their hashes, so it is up to contracts to populate
8+
/// oracles with the preimages of these hashes on return to make them available to their callers.
9+
///
10+
/// Public calls don't utilize this mechanism since the AVM does process the full return values.
411
pub struct ReturnsHash {
512
hash: Field,
613
}
@@ -10,36 +17,83 @@ impl ReturnsHash {
1017
ReturnsHash { hash }
1118
}
1219

13-
pub fn assert_empty(self) {
14-
assert_eq(self.hash, 0);
15-
}
16-
17-
pub fn raw(self) -> Field {
18-
self.hash
19-
}
20-
21-
/// This is only used during private execution, since in public it is the VM itself that keeps track of return
22-
/// values.
20+
/// Fetches the underlying return value from an oracle, constraining that it corresponds to the return data hash.
2321
pub fn get_preimage<T>(self) -> T
2422
where
2523
T: Deserialize,
2624
{
27-
// Safety: We verify that the value returned by `load` is the preimage of `hash`, fully constraining it.
25+
// Safety: We verify that the value returned by `load` is the preimage of `hash`, fully constraining it. If `T`
26+
// is `()`, then `preimage` must be an array of length 0 (since that is `()`'s deserialization length).
27+
// `hash_args_array` handles empty arrays following the protocol rules (i.e. an empty args array is signaled
28+
// with a zero hash), correctly constraining `self.hash`.
2829
let preimage = unsafe { execution_cache::load(self.hash) };
29-
assert_eq(self.hash, hash_args_array(preimage));
30+
assert_eq(self.hash, hash_args_array(preimage), "Preimage mismatch");
3031

3132
Deserialize::deserialize(preimage)
3233
}
34+
}
3335

34-
pub fn get_preimage_and_assert_empty<T>(self) -> T
35-
where
36-
T: Deserialize,
37-
{
38-
// Safety: We verify that the value returned by `load` is the preimage of `hash`, fully constraining it.
39-
let preimage = unsafe { execution_cache::load(self.hash) };
40-
assert_eq(self.hash, hash_args_array(preimage));
41-
assert_eq(self.hash, 0);
36+
mod test {
37+
use crate::{
38+
hash::hash_args_array,
39+
oracle::execution_cache,
40+
test::{helpers::test_environment::TestEnvironment, mocks::mock_struct::MockStruct},
41+
};
42+
use super::ReturnsHash;
43+
use protocol_types::traits::Serialize;
44+
use std::test::OracleMock;
4245

43-
Deserialize::deserialize(preimage)
46+
#[test]
47+
unconstrained fn retrieves_preimage() {
48+
let mut env = TestEnvironment::_new();
49+
env.private_context(|_| {
50+
let value = MockStruct::new(4, 7);
51+
let serialized = value.serialize();
52+
53+
let hash = hash_args_array(serialized);
54+
execution_cache::store(serialized, hash);
55+
56+
assert_eq(ReturnsHash::new(hash).get_preimage(), value);
57+
});
4458
}
59+
60+
#[test]
61+
unconstrained fn retrieves_empty_preimage() {
62+
let mut env = TestEnvironment::_new();
63+
env.private_context(|_| {
64+
let value = ();
65+
let serialized = [];
66+
67+
let hash = hash_args_array(serialized);
68+
execution_cache::store(serialized, hash);
69+
70+
assert_eq(ReturnsHash::new(hash).get_preimage(), value);
71+
});
72+
}
73+
74+
#[test(should_fail_with = "Preimage mismatch")]
75+
unconstrained fn rejects_bad_preimage() {
76+
let value = MockStruct::new(4, 7);
77+
let serialized = value.serialize();
78+
79+
let mut bad_serialized = serialized;
80+
bad_serialized[0] += 1;
81+
82+
let hash = hash_args_array(serialized);
83+
84+
let _ = OracleMock::mock("loadFromExecutionCache").returns(bad_serialized);
85+
assert_eq(ReturnsHash::new(hash).get_preimage(), value);
86+
}
87+
88+
// This test passes due to a Noir bug.
89+
// #[test(should_fail_with="Preimage mismatch")]
90+
// unconstrained fn rejects_bad_empty_preimage() {
91+
// let value = ();
92+
// let serialized = [];
93+
94+
// let hash = hash_args_array(serialized);
95+
96+
// let _ = OracleMock::mock("loadFromExecutionCache").returns([1]);
97+
// assert_eq(ReturnsHash::new(hash).get_preimage(), value);
98+
// }
4599
}

noir-projects/aztec-nr/aztec/src/macros/functions/call_interface_stubs.nr

Lines changed: 6 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::macros::utils::{
22
add_to_field_array, AsStrQuote, compute_fn_selector, is_fn_private, is_fn_public, is_fn_view,
33
};
4-
use std::meta::{type_of, unquote};
4+
use std::meta::unquote;
55

66
comptime global FROM_FIELD: TypedExpr = {
77
let from_field_trait = quote { protocol_types::traits::FromField }.as_trait_constraint();
@@ -17,42 +17,21 @@ comptime global SERIALIZED_ARGS_NAME: Quoted = quote { serialized_args };
1717

1818
pub comptime fn stub_fn(f: FunctionDefinition) -> Quoted {
1919
let is_static_call = is_fn_view(f);
20-
let is_void = f.return_type() == type_of(());
2120

2221
if is_fn_private(f) {
2322
if is_static_call {
24-
if is_void {
25-
create_private_static_void_stub(f)
26-
} else {
27-
create_private_static_stub(f)
28-
}
23+
create_private_static_stub(f)
2924
} else {
30-
if is_void {
31-
create_private_void_stub(f)
32-
} else {
33-
create_private_stub(f)
34-
}
25+
create_private_stub(f)
3526
}
3627
} else if is_fn_public(f) {
3728
if is_static_call {
38-
if is_void {
39-
create_public_static_void_stub(f)
40-
} else {
41-
create_public_static_stub(f)
42-
}
29+
create_public_static_stub(f)
4330
} else {
44-
if is_void {
45-
create_public_void_stub(f)
46-
} else {
47-
create_public_stub(f)
48-
}
31+
create_public_stub(f)
4932
}
5033
} else {
51-
if is_void {
52-
create_utility_void_stub(f)
53-
} else {
54-
create_utility_stub(f)
55-
}
34+
create_utility_stub(f)
5635
}
5736
}
5837

@@ -146,43 +125,6 @@ comptime fn create_private_static_stub(f: FunctionDefinition) -> Quoted {
146125
}
147126
}
148127

149-
comptime fn create_private_void_stub(f: FunctionDefinition) -> Quoted {
150-
let (fn_name, fn_parameters_list, serialized_args_slice_construction, fn_name_str, fn_name_len, fn_selector) =
151-
create_stub_base(f);
152-
153-
quote {
154-
pub fn $fn_name(self, $fn_parameters_list) -> dep::aztec::context::call_interfaces::PrivateVoidCallInterface<$fn_name_len, ()> {
155-
$serialized_args_slice_construction
156-
let selector = $FROM_FIELD($fn_selector);
157-
dep::aztec::context::call_interfaces::PrivateVoidCallInterface::new(
158-
self.target_contract,
159-
selector,
160-
$fn_name_str,
161-
$SERIALIZED_ARGS_NAME,
162-
false
163-
)
164-
}
165-
}
166-
}
167-
168-
comptime fn create_private_static_void_stub(f: FunctionDefinition) -> Quoted {
169-
let (fn_name, fn_parameters_list, serialized_args_slice_construction, fn_name_str, fn_name_len, fn_selector) =
170-
create_stub_base(f);
171-
172-
quote {
173-
pub fn $fn_name(self, $fn_parameters_list) -> dep::aztec::context::call_interfaces::PrivateStaticVoidCallInterface<$fn_name_len, ()> {
174-
$serialized_args_slice_construction
175-
let selector = $FROM_FIELD($fn_selector);
176-
dep::aztec::context::call_interfaces::PrivateStaticVoidCallInterface::new(
177-
self.target_contract,
178-
selector,
179-
$fn_name_str,
180-
serialized_args
181-
)
182-
}
183-
}
184-
}
185-
186128
comptime fn create_public_stub(f: FunctionDefinition) -> Quoted {
187129
let (fn_name, fn_parameters_list, serialized_args_slice_construction, fn_name_str, fn_name_len, fn_selector) =
188130
create_stub_base(f);
@@ -222,43 +164,6 @@ comptime fn create_public_static_stub(f: FunctionDefinition) -> Quoted {
222164
}
223165
}
224166

225-
comptime fn create_public_void_stub(f: FunctionDefinition) -> Quoted {
226-
let (fn_name, fn_parameters_list, serialized_args_slice_construction, fn_name_str, fn_name_len, fn_selector) =
227-
create_stub_base(f);
228-
229-
quote {
230-
pub fn $fn_name(self, $fn_parameters_list) -> dep::aztec::context::call_interfaces::PublicVoidCallInterface<$fn_name_len, ()> {
231-
$serialized_args_slice_construction
232-
let selector = $FROM_FIELD($fn_selector);
233-
dep::aztec::context::call_interfaces::PublicVoidCallInterface::new(
234-
self.target_contract,
235-
selector,
236-
$fn_name_str,
237-
$SERIALIZED_ARGS_NAME,
238-
false
239-
)
240-
}
241-
}
242-
}
243-
244-
comptime fn create_public_static_void_stub(f: FunctionDefinition) -> Quoted {
245-
let (fn_name, fn_parameters_list, serialized_args_slice_construction, fn_name_str, fn_name_len, fn_selector) =
246-
create_stub_base(f);
247-
248-
quote {
249-
pub fn $fn_name(self, $fn_parameters_list) -> dep::aztec::context::call_interfaces::PublicStaticVoidCallInterface<$fn_name_len, ()> {
250-
$serialized_args_slice_construction
251-
let selector = $FROM_FIELD($fn_selector);
252-
dep::aztec::context::call_interfaces::PublicStaticVoidCallInterface::new(
253-
self.target_contract,
254-
selector,
255-
$fn_name_str,
256-
serialized_args
257-
)
258-
}
259-
}
260-
}
261-
262167
comptime fn create_utility_stub(f: FunctionDefinition) -> Quoted {
263168
let (fn_name, fn_parameters_list, serialized_args_slice_construction, fn_name_str, fn_name_len, fn_selector) =
264169
create_stub_base(f);
@@ -280,24 +185,3 @@ comptime fn create_utility_stub(f: FunctionDefinition) -> Quoted {
280185
}
281186
}
282187
}
283-
284-
comptime fn create_utility_void_stub(f: FunctionDefinition) -> Quoted {
285-
let (fn_name, fn_parameters_list, serialized_args_slice_construction, fn_name_str, fn_name_len, fn_selector) =
286-
create_stub_base(f);
287-
288-
// This is here because utility function call interfaces can only be used within TXe tests.
289-
let modified_fn_name = f"_experimental_{fn_name}".quoted_contents();
290-
291-
quote {
292-
pub fn $modified_fn_name(self, $fn_parameters_list) -> dep::aztec::context::call_interfaces::UtilityVoidCallInterface<$fn_name_len, ()> {
293-
$serialized_args_slice_construction
294-
let selector = $FROM_FIELD($fn_selector);
295-
dep::aztec::context::call_interfaces::UtilityVoidCallInterface::new(
296-
self.target_contract,
297-
selector,
298-
$fn_name_str,
299-
$SERIALIZED_ARGS_NAME,
300-
)
301-
}
302-
}
303-
}

noir-projects/aztec-nr/aztec/src/test/helpers/authwit.nr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
authwit::auth::{compute_authwit_message_hash, compute_inner_authwit_hash},
3-
context::call_interfaces::{CallInterface, PublicVoidCallInterface},
3+
context::call_interfaces::{CallInterface, PublicCallInterface},
44
hash::hash_args,
55
oracle::execution::{get_chain_id, get_version},
66
test::helpers::{test_environment::TestEnvironment, txe_oracles},
@@ -48,9 +48,9 @@ where
4848
compute_inner_authwit_hash([caller.to_field(), selector.to_field(), args_hash]);
4949
let message_hash = compute_authwit_message_hash(target, chain_id, version, inner_hash);
5050

51-
let _ = env.call_public_void(
51+
let _ = env.call_public(
5252
on_behalf_of,
53-
PublicVoidCallInterface::<_, ()>::new(
53+
PublicCallInterface::<_, ()>::new(
5454
CANONICAL_AUTH_REGISTRY_ADDRESS,
5555
comptime { FunctionSelector::from_signature("set_authorized(Field,bool)") },
5656
"set_authorized",

0 commit comments

Comments
 (0)