Skip to content

Commit add1e8c

Browse files
authored
chore: backport feat: allow setting additional scopes in nr tests (#22968) to v4-next (#23057)
Backport of #22968 ("feat: allow setting additional scopes in nr tests") to `backport-to-v4-next-staging`. Rebased on top of latest staging (which now includes the backport of #22889 — `feat(txe): add tx private logs to tx side effects oracle`). All conflicts handled in a single resolution commit. ## What The PR adds `call_private_opts` / `view_private_opts` (with `CallPrivateOptions` / `ViewPrivateOptions`) to `TestEnvironment`, letting tests grant access to additional account scopes when calling private functions. It also reshapes the TXE oracle ABI for `private_call_new_flow` / `public_call_new_flow` to take an `Option<AztecAddress>` for `from`, removes `call_public_incognito`'s ignored `from` parameter, deprecates `view_public_incognito`, and introduces `AztecAddress.NULL_MSG_SENDER` on the TS side. ## Conflicts handled (single commit) After rebasing, four files still drifted vs `next`: - **`noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr`** — with #22889 now on staging, the imports here match `next` exactly aside from `NULL_MSG_SENDER_CONTRACT_ADDRESS`. Just drop that one symbol (the PR removes its only use). - **`docs/docs-developers/docs/resources/migration_notes.md`** — keep only the two new TBD entries from #22968. The DeployMethod and `aztec-up` bundled-binary entries that the cherry-pick brought along belong to other PRs not yet backported (e.g. #22985); leave them out so those backports add them in their own PRs. - **`yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts`** — drop `NULL_MSG_SENDER_CONTRACT_ADDRESS` (replaced by `AztecAddress.NULL_MSG_SENDER`). Keep `L1_TO_L2_MSG_TREE_HEIGHT`, `NOTE_HASH_TREE_HEIGHT`, `PUBLIC_DATA_TREE_HEIGHT` — they're still in use on this branch since #21577 hasn't been backported. - **`yarn-project/stdlib/src/aztec-address/index.ts`** — keep both the existing `eslint-disable @typescript-eslint/no-unsafe-declaration-merging` comment and the new `NULL_MSG_SENDER_CONTRACT_ADDRESS` import. ## Verification - `nargo check` passes for both Noir contracts the PR touches (`scope_test_contract`, `public_checks_contract`). - No conflict markers remain. Original PR: #22968
2 parents d1eea39 + ea5645f commit add1e8c

12 files changed

Lines changed: 218 additions & 83 deletions

File tree

docs/docs-developers/docs/resources/migration_notes.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@ Aztec is in active development. Each version may introduce breaking changes that
99

1010
## TBD
1111

12+
### [Aztec.nr] TXE `call_public_incognito` no longer takes a `from` parameter
13+
14+
`TestEnvironment::call_public_incognito` previously accepted a `from` address that was silently ignored (the function always uses a null `msg_sender`). The `from` parameter has been removed.
15+
16+
```diff
17+
- env.call_public_incognito(sender, SampleContract::at(addr).some_function());
18+
+ env.call_public_incognito(SampleContract::at(addr).some_function());
19+
```
20+
21+
If you need to call a public function *with* a sender, use `call_public` instead.
22+
23+
### [Aztec.nr] TXE `view_public_incognito` is deprecated
24+
25+
`TestEnvironment::view_public_incognito` is now deprecated in favor of `view_public`, which has the same behavior (null `msg_sender`, static call).
26+
27+
```diff
28+
- env.view_public_incognito(SampleContract::at(addr).some_view());
29+
+ env.view_public(SampleContract::at(addr).some_view());
30+
```
31+
1232
### [PXE] `proveTx` takes an options bag
1333

1434
`PXE.proveTx` used to accept `scopes` as a positional argument; it now takes an options bag consistent with `simulateTx` and `profileTx`, and adds an optional `senderForTags` field. Update direct callers:

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

Lines changed: 112 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,80 @@ struct UtilityContextOptions {
125125
contract_address: Option<AztecAddress>,
126126
}
127127

128+
/// Configuration for [`TestEnvironment::call_private_opts`].
129+
///
130+
/// Constructed by calling [`CallPrivateOptions::new`] and then chaining methods setting each value:
131+
///
132+
/// ```noir
133+
/// env.call_private_opts(from, CallPrivateOptions::new().with_additional_scopes([other]), call);
134+
/// ```
135+
pub struct CallPrivateOptions<let N: u32> {
136+
additional_scopes: [AztecAddress; N],
137+
}
138+
139+
impl CallPrivateOptions<0> {
140+
/// Creates default `CallPrivateOptions`.
141+
///
142+
/// The default values are the same as if using [`TestEnvironment::call_private`] instead of
143+
/// [`TestEnvironment::call_private_opts`].
144+
pub fn new() -> Self {
145+
CallPrivateOptions { additional_scopes: [] }
146+
}
147+
}
148+
149+
impl<let N: u32> CallPrivateOptions<N> {
150+
/// Grants access to secrets of additional accounts.
151+
///
152+
/// By default only the secrets that belong to the `from` account can be accessed: this function lets contracts
153+
/// retrieve private information from other accounts during execution.
154+
///
155+
/// Example usage includes accounts withdrawing from an escrow, which may require accessing the escrow's own notes
156+
/// and secrets.
157+
pub fn with_additional_scopes<let N_2: u32>(
158+
_self: Self,
159+
additional_scopes: [AztecAddress; N_2],
160+
) -> CallPrivateOptions<N_2> {
161+
CallPrivateOptions { additional_scopes }
162+
}
163+
}
164+
165+
/// Configuration for [`TestEnvironment::view_private_opts`].
166+
///
167+
/// Constructed by calling [`ViewPrivateOptions::new`] and then chaining methods setting each value:
168+
///
169+
/// ```noir
170+
/// env.view_private_opts(from, ViewPrivateOptions::new().with_additional_scopes([other]), call);
171+
/// ```
172+
pub struct ViewPrivateOptions<let S: u32> {
173+
additional_scopes: [AztecAddress; S],
174+
}
175+
176+
impl ViewPrivateOptions<0> {
177+
/// Creates default `ViewPrivateOptions`.
178+
///
179+
/// The default values are the same as if using [`TestEnvironment::view_private`] instead of
180+
/// [`TestEnvironment::view_private_opts`].
181+
pub fn new() -> Self {
182+
ViewPrivateOptions { additional_scopes: [] }
183+
}
184+
}
185+
186+
impl<let S: u32> ViewPrivateOptions<S> {
187+
/// Grants access to secrets of additional accounts.
188+
///
189+
/// By default only the secrets that belong to the `from` account can be accessed: this function lets contracts
190+
/// retrieve private information from other accounts during execution.
191+
///
192+
/// Example usage includes accounts querying an escrow's balance, which may require accessing the escrow's own
193+
/// notes.
194+
pub fn with_additional_scopes<let S2: u32>(
195+
_self: Self,
196+
additional_scopes: [AztecAddress; S2],
197+
) -> ViewPrivateOptions<S2> {
198+
ViewPrivateOptions { additional_scopes }
199+
}
200+
}
201+
128202
struct NoteDiscoveryOptions {
129203
contract_address: Option<AztecAddress>,
130204
}
@@ -544,20 +618,34 @@ impl TestEnvironment {
544618
/// let return_value = env.call_private(caller, SampleContract::at(contract_addr).sample_private_function());
545619
/// ```
546620
pub unconstrained fn call_private<let M: u32, let N: u32, T>(
621+
self: Self,
622+
from: AztecAddress,
623+
call: PrivateCall<M, N, T>,
624+
) -> T
625+
where
626+
T: Deserialize,
627+
{
628+
self.call_private_opts(from, CallPrivateOptions::new(), call)
629+
}
630+
631+
/// Variant of `call_private` which allows specifying multiple configuration values via `CallPrivateOptions`.
632+
pub unconstrained fn call_private_opts<let M: u32, let N: u32, let S: u32, T>(
547633
_self: Self,
548634
from: AztecAddress,
635+
opts: CallPrivateOptions<S>,
549636
call: PrivateCall<M, N, T>,
550637
) -> T
551638
where
552639
T: Deserialize,
553640
{
554641
let serialized_return_values = txe_oracles::private_call_new_flow(
555-
from,
642+
Option::some(from),
556643
call.target_contract,
557644
call.selector,
558645
call.args,
559646
hash_args(call.args),
560647
/*is_static=*/ false,
648+
opts.additional_scopes,
561649
);
562650

563651
T::deserialize(serialized_return_values)
@@ -571,20 +659,34 @@ impl TestEnvironment {
571659
/// The `from` parameter specifies the account from whose perspective the view is executed. This affects
572660
/// scope isolation - only notes scoped to this account will be visible during execution.
573661
pub unconstrained fn view_private<let M: u32, let N: u32, T>(
662+
self: Self,
663+
from: AztecAddress,
664+
call: PrivateStaticCall<M, N, T>,
665+
) -> T
666+
where
667+
T: Deserialize,
668+
{
669+
self.view_private_opts(from, ViewPrivateOptions::new(), call)
670+
}
671+
672+
/// Variant of `view_private` which allows specifying multiple configuration values via `ViewPrivateOptions`.
673+
pub unconstrained fn view_private_opts<let M: u32, let N: u32, let S: u32, T>(
574674
_self: Self,
575675
from: AztecAddress,
676+
opts: ViewPrivateOptions<S>,
576677
call: PrivateStaticCall<M, N, T>,
577678
) -> T
578679
where
579680
T: Deserialize,
580681
{
581682
let serialized_return_values = txe_oracles::private_call_new_flow(
582-
from,
683+
Option::some(from),
583684
call.target_contract,
584685
call.selector,
585686
call.args,
586687
hash_args(call.args),
587688
/*is_static=*/ true,
689+
opts.additional_scopes,
588690
);
589691

590692
T::deserialize(serialized_return_values)
@@ -705,16 +807,12 @@ impl TestEnvironment {
705807
/// Performs a public contract function call, including the processing of any nested public calls. Returns the
706808
/// result of the called function. Variant of `call_public`, but the `from` address (`msg_sender`) is set to
707809
/// "null".
708-
pub unconstrained fn call_public_incognito<let M: u32, let N: u32, T>(
709-
_self: Self,
710-
from: AztecAddress,
711-
call: PublicCall<M, N, T>,
712-
) -> T
810+
pub unconstrained fn call_public_incognito<let M: u32, let N: u32, T>(_self: Self, call: PublicCall<M, N, T>) -> T
713811
where
714812
T: Deserialize,
715813
{
716814
let serialized_return_values = txe_oracles::public_call_new_flow(
717-
Option::some(from),
815+
Option::none(),
718816
call.target_contract,
719817
call.selector,
720818
call.args,
@@ -727,13 +825,14 @@ impl TestEnvironment {
727825
/// Variant of `call_public` for public `#[view]` functions.
728826
///
729827
/// Unlike `call_public`, no transaction is created and no block is mined (since `#[view]` functions are only
730-
/// executable in a static context, and these produce no side effects).
828+
/// executable in a static context, and these produce no side effects). The `msg_sender` is set to "null", since
829+
/// view calls have no real caller.
731830
pub unconstrained fn view_public<let M: u32, let N: u32, T>(_self: Self, call: PublicStaticCall<M, N, T>) -> T
732831
where
733832
T: Deserialize,
734833
{
735834
let serialized_return_values = txe_oracles::public_call_new_flow(
736-
Option::some(AztecAddress::zero()),
835+
Option::none(),
737836
call.target_contract,
738837
call.selector,
739838
call.args,
@@ -743,26 +842,15 @@ impl TestEnvironment {
743842
T::deserialize(serialized_return_values)
744843
}
745844

746-
/// Variant of `view_public`, but the `from` address (`msg_sender`) is set to "null"
747-
///
748-
/// Unlike `call_public`, no transaction is created and no block is mined (since `#[view]` functions are only
749-
/// executable in a static context, and these produce no side effects).
845+
#[deprecated("use `TestEnvironment::view_public` instead")]
750846
pub unconstrained fn view_public_incognito<let M: u32, let N: u32, T>(
751-
_self: Self,
847+
self: Self,
752848
call: PublicStaticCall<M, N, T>,
753849
) -> T
754850
where
755851
T: Deserialize,
756852
{
757-
let serialized_return_values = txe_oracles::public_call_new_flow(
758-
Option::none(),
759-
call.target_contract,
760-
call.selector,
761-
call.args,
762-
true,
763-
);
764-
765-
T::deserialize(serialized_return_values)
853+
self.view_public(call)
766854
}
767855

768856
/// Discovers a note from a [`NoteMessage`], which is expected to have been created in the last transaction

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

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::protocol::{
77
abis::function_selector::FunctionSelector,
88
address::AztecAddress,
99
constants::{
10-
CONTRACT_INSTANCE_LENGTH, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX,
11-
MAX_PRIVATE_LOGS_PER_TX, NULL_MSG_SENDER_CONTRACT_ADDRESS, PRIVATE_LOG_SIZE_IN_FIELDS,
10+
CONTRACT_INSTANCE_LENGTH, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_PRIVATE_LOGS_PER_TX,
11+
PRIVATE_LOG_SIZE_IN_FIELDS,
1212
},
1313
contract_instance::ContractInstance,
1414
traits::{Deserialize, ToField},
@@ -36,13 +36,14 @@ pub unconstrained fn deploy<let M: u32, let N: u32, let P: u32>(
3636
ContractInstance::deserialize(instance_fields)
3737
}
3838

39-
pub unconstrained fn private_call_new_flow<let M: u32, let N: u32>(
40-
from: AztecAddress,
39+
pub unconstrained fn private_call_new_flow<let M: u32, let N: u32, let S: u32>(
40+
from: Option<AztecAddress>,
4141
contract_address: AztecAddress,
4242
function_selector: FunctionSelector,
4343
args: [Field; M],
4444
args_hash: Field,
4545
is_static_call: bool,
46+
additional_scopes: [AztecAddress; S],
4647
) -> [Field; N] {
4748
private_call_new_flow_oracle(
4849
from,
@@ -51,6 +52,7 @@ pub unconstrained fn private_call_new_flow<let M: u32, let N: u32>(
5152
args,
5253
args_hash,
5354
is_static_call,
55+
additional_scopes,
5456
)
5557
}
5658

@@ -63,8 +65,6 @@ pub unconstrained fn public_call_new_flow<let M: u32, let N: u32>(
6365
) -> [Field; N] {
6466
let calldata = [function_selector.to_field()].concat(args);
6567

66-
let from = from.unwrap_or(NULL_MSG_SENDER_CONTRACT_ADDRESS);
67-
6868
public_call_new_flow_oracle(from, contract_address, calldata, is_static_call)
6969
}
7070

@@ -109,8 +109,7 @@ pub struct TxEffects {
109109

110110
/// Returns the effects of the last transaction included by the TXE.
111111
pub unconstrained fn get_last_tx_effects() -> TxEffects {
112-
let (tx_hash, note_hashes, nullifiers, raw_log_storage, log_lengths, log_count) =
113-
get_last_tx_effects_oracle();
112+
let (tx_hash, note_hashes, nullifiers, raw_log_storage, log_lengths, log_count) = get_last_tx_effects_oracle();
114113

115114
let mut private_logs = BoundedVec::new();
116115
for i in 0..log_count {
@@ -121,14 +120,7 @@ pub unconstrained fn get_last_tx_effects() -> TxEffects {
121120
}
122121

123122
#[oracle(aztec_txe_getLastTxEffects)]
124-
unconstrained fn get_last_tx_effects_oracle() -> (
125-
Field,
126-
TxNoteHashes,
127-
TxNullifiers,
128-
[[Field; PRIVATE_LOG_SIZE_IN_FIELDS]; MAX_PRIVATE_LOGS_PER_TX],
129-
[u32; MAX_PRIVATE_LOGS_PER_TX],
130-
u32,
131-
) {}
123+
unconstrained fn get_last_tx_effects_oracle() -> (Field, TxNoteHashes, TxNullifiers, [[Field; PRIVATE_LOG_SIZE_IN_FIELDS]; MAX_PRIVATE_LOGS_PER_TX], [u32; MAX_PRIVATE_LOGS_PER_TX], u32) {}
132124

133125
/// Returns the raw offchain effect payloads emitted by the last top-level call into TXE.
134126
/// Each effect is a variable-length field array (e.g. offchain messages have a different size
@@ -217,18 +209,19 @@ pub unconstrained fn add_account(secret: Field) -> TestAccount {}
217209
pub unconstrained fn add_authwit(address: AztecAddress, message_hash: Field) {}
218210

219211
#[oracle(aztec_txe_privateCallNewFlow)]
220-
unconstrained fn private_call_new_flow_oracle<let M: u32, let N: u32>(
221-
_from: AztecAddress,
212+
unconstrained fn private_call_new_flow_oracle<let M: u32, let N: u32, let S: u32>(
213+
_from: Option<AztecAddress>,
222214
_contract_address: AztecAddress,
223215
_function_selector: FunctionSelector,
224216
_args: [Field; M],
225217
_args_hash: Field,
226218
_is_static_call: bool,
219+
_additional_scopes: [AztecAddress; S],
227220
) -> [Field; N] {}
228221

229222
#[oracle(aztec_txe_publicCallNewFlow)]
230223
unconstrained fn public_call_new_flow_oracle<let M: u32, let N: u32>(
231-
from: AztecAddress,
224+
from: Option<AztecAddress>,
232225
contract_address: AztecAddress,
233226
calldata: [Field; M],
234227
is_static_call: bool,

noir-projects/noir-contracts/contracts/protocol/public_checks_contract/src/test.nr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ unconstrained fn check_block_number() {
1313
let public_checks = PublicChecks::at(public_checks_contract_address);
1414

1515
let expected_next_block_number = env.next_block_number();
16-
env.view_public_incognito(public_checks.check_block_number(Comparator.LT, expected_next_block_number + 1));
16+
env.view_public(public_checks.check_block_number(Comparator.LT, expected_next_block_number + 1));
1717
}
1818

1919
#[test(should_fail_with = "Block number mismatch.")]
@@ -24,7 +24,7 @@ unconstrained fn check_block_number_fail() {
2424
let public_checks = PublicChecks::at(public_checks_contract_address);
2525

2626
let expected_next_block_number = env.next_block_number();
27-
env.view_public_incognito(public_checks.check_block_number(Comparator.LT, expected_next_block_number));
27+
env.view_public(public_checks.check_block_number(Comparator.LT, expected_next_block_number));
2828
}
2929

3030
#[test]
@@ -36,7 +36,7 @@ unconstrained fn check_timestamp() {
3636

3737
let expected_next_block_timestamp = env.last_block_timestamp() + 100;
3838
env.set_next_block_timestamp(expected_next_block_timestamp);
39-
env.view_public_incognito(public_checks.check_timestamp(Comparator.LT, expected_next_block_timestamp + 1));
39+
env.view_public(public_checks.check_timestamp(Comparator.LT, expected_next_block_timestamp + 1));
4040
}
4141

4242
#[test(should_fail_with = "Timestamp mismatch.")]
@@ -48,5 +48,5 @@ unconstrained fn check_timestamp_fail() {
4848

4949
let expected_next_block_timestamp = env.last_block_timestamp() + 100;
5050
env.set_next_block_timestamp(expected_next_block_timestamp);
51-
env.view_public_incognito(public_checks.check_timestamp(Comparator.LT, expected_next_block_timestamp));
51+
env.view_public(public_checks.check_timestamp(Comparator.LT, expected_next_block_timestamp));
5252
}

0 commit comments

Comments
 (0)