Skip to content

Commit 9a72f1c

Browse files
nventurobenesjan
andauthored
feat!: improve testenv acct creation api (#16007)
I renamed `create_account` to `create_light_account` and `create_account_contract` to `create_contract_account`. I think these names better convey the difference between the two functions. I also added documentation for what these functions do and how they differ. Additionally, I removed the `secret` param from these functions, since the caller never cared at all what the secret was, but was forced to pass different ones in each call to avoid getting repeat accounts. Instead I just keep track of it inside `TestEnvironment`. We additionally now have separate tracking for light and contract accounts, which means that all contract accounts now get generated with the same secret sequence, resulting in better usage of our cache. (e.g. before if a test did `light(0) contract(1)` and another did `contract(0) light(1)` that'd be two cache misses, but now that results in `light(0) contract(0)` for both tests, resulting in a cache hit. The only thing that matters now is how many contract accounts you create in total) --------- Co-authored-by: benesjan <janbenes1234@gmail.com>
1 parent 9553a46 commit 9a72f1c

File tree

19 files changed

+299
-128
lines changed

19 files changed

+299
-128
lines changed

noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ use crate::{
77
};
88
use dep::protocol_types::{address::AztecAddress, public_keys::PublicKeys};
99

10-
mod test;
11-
1210
pub unconstrained fn get_nsk_app(npk_m_hash: Field) -> Field {
1311
get_key_validation_request(npk_m_hash, NULLIFIER_INDEX).sk_app
1412
}
@@ -34,3 +32,61 @@ pub fn get_public_keys(account: AztecAddress) -> PublicKeys {
3432

3533
public_keys
3634
}
35+
36+
mod test {
37+
use super::get_public_keys;
38+
39+
use crate::test::helpers::test_environment::TestEnvironment;
40+
use protocol_types::traits::Serialize;
41+
use std::test::OracleMock;
42+
43+
global KEY_ORACLE_RESPONSE_LENGTH: u32 = 13; // 12 fields for the keys, one field for the partial address
44+
45+
#[test(should_fail_with = "Invalid public keys hint for address")]
46+
unconstrained fn get_public_keys_fails_with_bad_hint() {
47+
let mut env = TestEnvironment::new();
48+
let account = env.create_light_account();
49+
50+
// Instead of querying for some unknown account, which would result in the oracle erroring out, we mock a bad oracle
51+
// response to check that the circuit properly checks the address derivation.
52+
let mut random_keys_and_partial_address = [0; KEY_ORACLE_RESPONSE_LENGTH];
53+
// We use randomly generated points on the curve, and a random partial address to ensure that
54+
// this combination does not derive the address and we should see the assertion fail.
55+
// npk_m
56+
random_keys_and_partial_address[0] =
57+
0x292364b852c6c6f01472951e76a39cbcf074591fd0e063a81965e7b51ad868a5;
58+
random_keys_and_partial_address[1] =
59+
0x0a687b46cdc9238f1c311f126aaaa4acbd7a737bff2efd7aeabdb8d805843a27;
60+
random_keys_and_partial_address[2] =
61+
0x0000000000000000000000000000000000000000000000000000000000000000;
62+
// ivpk_m
63+
random_keys_and_partial_address[3] =
64+
0x173c5229a00c5425255680dd6edc27e278c48883991f348fe6985de43b4ec25f;
65+
random_keys_and_partial_address[4] =
66+
0x1698608e23b5f6c2f43c49a559108bb64e2247b8fc2da842296a416817f40b7f;
67+
random_keys_and_partial_address[5] =
68+
0x0000000000000000000000000000000000000000000000000000000000000000;
69+
// ovpk_m
70+
random_keys_and_partial_address[6] =
71+
0x1bad2f7d1ad960a1bd0fe4d2c8d17f5ab4a86ef8b103e0a9e7f67ec0d3b4795e;
72+
random_keys_and_partial_address[7] =
73+
0x206db87110abbecc9fbaef2c865189d94ef2c106202f734ee4eba9257fd28bf1;
74+
random_keys_and_partial_address[8] =
75+
0x0000000000000000000000000000000000000000000000000000000000000000;
76+
// tpk_m
77+
random_keys_and_partial_address[9] =
78+
0x05e3bd9cfe6b47daa139613619cf7d7fd8bb0112b6f2908caa6d9b536ed948ed;
79+
random_keys_and_partial_address[10] =
80+
0x051066f877c9df47552d02e7dc32127ff4edefc8498e813bca1cbd3f5d1be429;
81+
random_keys_and_partial_address[11] =
82+
0x0000000000000000000000000000000000000000000000000000000000000000;
83+
// partial address
84+
random_keys_and_partial_address[12] =
85+
0x236703e2cb00a182e024e98e9f759231b556d25ff19f98896cebb69e9e678cc9;
86+
87+
let _ = OracleMock::mock("getPublicKeysAndPartialAddress").returns(
88+
random_keys_and_partial_address.serialize(),
89+
);
90+
let _ = get_public_keys(account);
91+
}
92+
}

noir-projects/aztec-nr/aztec/src/keys/getters/test.nr

Lines changed: 0 additions & 68 deletions
This file was deleted.

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

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,31 @@ use super::txe_oracles::public_call_new_flow;
1717

1818
mod test;
1919

20-
pub struct TestEnvironment {}
20+
struct Counter {
21+
next_value: Field,
22+
}
23+
24+
impl Counter {
25+
fn new() -> Self {
26+
Self { next_value: 0 }
27+
}
28+
29+
fn next(&mut self) -> Field {
30+
let ret = self.next_value;
31+
self.next_value += 1;
32+
ret
33+
}
34+
}
35+
36+
pub struct TestEnvironment {
37+
// The secrets to be used for light and contract account creation. By keeping track of the last used secret we can
38+
// issue new ones automatically without requiring the user to provide different ones. Additionally, having the
39+
// secrets be deterministic for each set of accounts and all concurrent tests results in TXE being able to maximize
40+
// cache usage and not have to recompute account addresses and contract artifacts, which are relatively expensive
41+
// operations.
42+
light_account_secret: Counter,
43+
contract_account_secret: Counter,
44+
}
2145

2246
pub struct CallResult<T> {
2347
pub return_value: T,
@@ -47,12 +71,12 @@ impl PrivateContextOptions {
4771

4872
impl TestEnvironment {
4973
pub unconstrained fn new() -> Self {
50-
Self {}
74+
Self { light_account_secret: Counter::new(), contract_account_secret: Counter::new() }
5175
}
5276

5377
pub unconstrained fn _new() -> Self {
5478
txe_oracles::enable_context_checks();
55-
Self {}
79+
Self { light_account_secret: Counter::new(), contract_account_secret: Counter::new() }
5680
}
5781

5882
/// EXPERIMENTAL FEATURE - NOT MEANT FOR EXTERNAL USE
@@ -262,13 +286,20 @@ impl TestEnvironment {
262286
txe_oracles::advance_timestamp_by(duration);
263287
}
264288

265-
pub unconstrained fn create_account(_self: Self, secret: Field) -> AztecAddress {
266-
let test_account = txe_oracles::create_account(secret);
267-
test_account.address
268-
}
269-
270-
pub unconstrained fn create_account_contract(self, secret: Field) -> AztecAddress {
271-
let test_account = txe_oracles::add_account(secret);
289+
/// Creates a new account that can be used as the `from` parameter in contract calls, e.g. in `private_call` or
290+
/// `public_call`, or be made the owner or recipient of notes in `private_context`.
291+
///
292+
/// The returned account has a full set of privacy keys, so it can nullify notes, receive messages, etc. It also has
293+
/// an associated `SchnorrAccount` contract that can process authwit requests - the authwits can be added via the
294+
/// `add_private_authwit_from_call_interface` and `add_public_authwit_from_call_interface` helper functions. If
295+
/// authwits are not required, consider using `create_light_account` instead, which is a faster variant of this
296+
/// function.
297+
///
298+
/// Each call to `create_contract_account` will return a different address, and so it can be called repeatedly to
299+
/// generate multiple addresses. These addresses are also different from the ones that `create_light_account`
300+
/// returns, and so these two functions can be mixed and match to create a set of unique accounts.
301+
pub unconstrained fn create_contract_account(&mut self) -> AztecAddress {
302+
let test_account = txe_oracles::add_account(self.contract_account_secret.next());
272303
let address = test_account.address;
273304

274305
let _ = self.call_private(
@@ -285,6 +316,21 @@ impl TestEnvironment {
285316
address
286317
}
287318

319+
/// Creates a new account that can be used as the `from` parameter in contract calls, e.g. in `private_call` or
320+
/// `public_call`, or be made the owner or recipient of notes in `private_context`. This is a faster variant of
321+
/// `create_contract_account`, but comes with reduced capabilities.
322+
///
323+
/// The returned account has a full set of privacy keys, so it can nullify notes, receive messages, etc. It doesn't
324+
/// however have an associated account contract, so it cannot process private authwit requests.
325+
///
326+
/// Each call to `create_light_account` will return a different address, and so it can be called repeatedly to
327+
/// generate multiple addresses. These addresses are also different from the ones that `create_contract_account`
328+
/// returns, and so these two functions can be mixed and match to create a set of unique accounts.
329+
pub unconstrained fn create_light_account(&mut self) -> AztecAddress {
330+
let test_account = txe_oracles::create_account(self.light_account_secret.next());
331+
test_account.address
332+
}
333+
288334
pub unconstrained fn deploy<let N: u32, let M: u32>(
289335
self: Self,
290336
path: str<N>,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::test::helpers::test_environment::TestEnvironment;
22

3+
mod accounts;
34
mod private_context;
45
mod public_context;
56

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use crate::{
2+
authwit::auth::{compute_authwit_message_hash, IS_VALID_SELECTOR},
3+
context::call_interfaces::PrivateCallInterface,
4+
keys::getters::get_public_keys,
5+
test::helpers::{test_environment::TestEnvironment, txe_oracles},
6+
};
7+
use protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress};
8+
9+
#[test]
10+
unconstrained fn create_light_account_does_not_repeat_accounts() {
11+
let mut env = TestEnvironment::_new();
12+
13+
let first_account = env.create_light_account();
14+
let second_account = env.create_light_account();
15+
16+
assert(first_account != second_account);
17+
}
18+
19+
#[test]
20+
unconstrained fn create_contract_account_does_not_repeat_accounts() {
21+
let mut env = TestEnvironment::new();
22+
23+
let first_account = env.create_contract_account();
24+
let second_account = env.create_contract_account();
25+
26+
assert(first_account != second_account);
27+
}
28+
29+
#[test]
30+
unconstrained fn light_and_contract_accounts_do_not_share_addresses() {
31+
let mut env = TestEnvironment::new();
32+
33+
let light_account = env.create_light_account();
34+
let contract_account = env.create_contract_account();
35+
36+
assert(light_account != contract_account);
37+
}
38+
39+
#[test]
40+
unconstrained fn light_accounts_have_valid_keys() {
41+
let mut env = TestEnvironment::new();
42+
let light_account = env.create_light_account();
43+
44+
let _ = get_public_keys(light_account);
45+
}
46+
47+
#[test]
48+
unconstrained fn contract_accounts_have_valid_keys() {
49+
let mut env = TestEnvironment::new();
50+
let contract_account = env.create_contract_account();
51+
52+
let _ = get_public_keys(contract_account);
53+
}
54+
55+
#[test(should_fail_with = "use `create_contract_account` instead of `create_light_account` for authwit support")]
56+
unconstrained fn light_accounts_fail_on_private_authwit_checks() {
57+
let mut env = TestEnvironment::new();
58+
let light_account = env.create_light_account();
59+
60+
let inner_hash = 13;
61+
let caller = AztecAddress::zero();
62+
63+
let _: Field = env
64+
.call_private(
65+
caller,
66+
PrivateCallInterface::new(
67+
light_account,
68+
comptime { FunctionSelector::from_signature("verify_private_authwit(Field)") },
69+
"verify_private_authwit",
70+
[inner_hash],
71+
true,
72+
),
73+
)
74+
.return_value;
75+
}
76+
77+
#[test(should_fail_with = "Unknown auth witness")]
78+
unconstrained fn contract_accounts_reject_invalid_private_authwits() {
79+
let mut env = TestEnvironment::new();
80+
let contract_account = env.create_contract_account();
81+
82+
let inner_hash = 13;
83+
let caller = AztecAddress::zero();
84+
85+
let _: Field = env
86+
.call_private(
87+
caller,
88+
PrivateCallInterface::new(
89+
contract_account,
90+
comptime { FunctionSelector::from_signature("verify_private_authwit(Field)") },
91+
"verify_private_authwit",
92+
[inner_hash],
93+
true,
94+
),
95+
)
96+
.return_value;
97+
}
98+
99+
#[test]
100+
unconstrained fn contract_accounts_accept_valid_private_authwits() {
101+
let mut env = TestEnvironment::new();
102+
let contract_account = env.create_contract_account();
103+
104+
let inner_hash = 13;
105+
let caller = AztecAddress::zero();
106+
107+
env.private_context(|context| {
108+
let message_hash = compute_authwit_message_hash(
109+
caller,
110+
context.chain_id(),
111+
context.version(),
112+
inner_hash,
113+
);
114+
txe_oracles::add_authwit(contract_account, message_hash);
115+
});
116+
117+
let result = env
118+
.call_private(
119+
caller,
120+
PrivateCallInterface::new(
121+
contract_account,
122+
comptime { FunctionSelector::from_signature("verify_private_authwit(Field)") },
123+
"verify_private_authwit",
124+
[inner_hash],
125+
true,
126+
),
127+
)
128+
.return_value;
129+
assert_eq(result, IS_VALID_SELECTOR);
130+
}

noir-projects/noir-contracts/contracts/app/auth_contract/src/test.nr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ use aztec::{
55
};
66

77
unconstrained fn setup() -> (TestEnvironment, AztecAddress, AztecAddress, AztecAddress, AztecAddress) {
8-
let env = TestEnvironment::new();
8+
let mut env = TestEnvironment::new();
99

10-
let admin = env.create_account(1);
11-
let to_authorize = env.create_account(2);
12-
let other = env.create_account(3);
10+
let admin = env.create_light_account();
11+
let to_authorize = env.create_light_account();
12+
let other = env.create_light_account();
1313

1414
let initializer_call_interface = Auth::interface().constructor(admin);
1515

0 commit comments

Comments
 (0)