Skip to content

Commit 9857b01

Browse files
author
AztecBot
committed
Merge branch 'next' into merge-train/spartan
2 parents 6c5dec9 + 33968d8 commit 9857b01

File tree

68 files changed

+893
-349
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+893
-349
lines changed

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

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

1010
## TBD
1111

12+
### Scope enforcement for private state access (TXE and PXE)
13+
14+
Scope enforcement is now active across both TXE (test environment) and PXE (client). Previously, private execution could implicitly access any account's keys and notes. Now, only the caller (`from`) address is in scope by default, and accessing another address's private state requires explicitly granting scope.
15+
16+
#### Noir developers (TXE)
17+
18+
TXE now enforces scope isolation, matching PXE behavior. During private execution, only the caller's keys and notes are accessible. If a Noir test accesses private state of an address other than `from`, it will fail. When `from` is the zero address, scopes are empty (deny-all).
19+
20+
If your TXE tests fail with key or note access errors, ensure the test is calling from the correct address, or restructure the test to match the expected access pattern.
21+
22+
#### Aztec.js developers (PXE/Wallet)
23+
24+
The wallet now passes scopes to PXE, and only the `from` address is in scope by default. Auto-expansion of scopes for nested calls to registered accounts has been removed. A new `additionalScopes` option is available on `send()`, `simulate()`, and `deploy()` for cases where private execution needs access to another address's keys or notes.
25+
26+
**When do you need `additionalScopes`?**
27+
28+
1. **Deploying contracts whose constructor initializes private storage** (e.g., account contracts, or any contract using `SinglePrivateImmutable`/`SinglePrivateMutable` in the constructor). The contract's own address must be in scope so its nullifier key is accessible during initialization.
29+
30+
2. **Operations that access another contract's private state** (e.g., withdrawing from an escrow contract that nullifies the contract's own token notes).
31+
32+
```
33+
34+
**Example: deploying a contract with private storage (e.g., `PrivateToken`)**
35+
36+
```diff
37+
const tokenDeployment = PrivateTokenContract.deployWithPublicKeys(
38+
tokenPublicKeys, wallet, initialBalance, sender,
39+
);
40+
const tokenInstance = await tokenDeployment.getInstance();
41+
await wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey);
42+
const token = await tokenDeployment.send({
43+
from: sender,
44+
+ additionalScopes: [tokenInstance.address],
45+
});
46+
```
47+
48+
**Example: withdrawing from an escrow contract**
49+
50+
```diff
51+
await escrowContract.methods
52+
.withdraw(token.address, amount, recipient)
53+
- .send({ from: owner });
54+
+ .send({ from: owner, additionalScopes: [escrowContract.address] });
55+
```
56+
1257
### `simulateUtility` renamed to `executeUtility`
1358

1459
The `simulateUtility` method and related types have been renamed to `executeUtility` across the entire stack to better reflect that utility functions are executed, not simulated.

noir-projects/aztec-nr/aztec/src/contract_self.nr renamed to noir-projects/aztec-nr/aztec/src/contract_self/contract_self_private.nr

Lines changed: 19 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,23 @@
1-
//! The `self` contract value.
1+
//! The `self` contract value for private execution contexts.
22

33
use crate::{
4-
context::{
5-
calls::{PrivateCall, PrivateStaticCall, PublicCall, PublicStaticCall},
6-
PrivateContext,
7-
PublicContext,
8-
UtilityContext,
9-
},
10-
event::{
11-
event_emission::{emit_event_in_private, emit_event_in_public},
12-
event_interface::EventInterface,
13-
EventMessage,
14-
},
4+
context::{calls::{PrivateCall, PrivateStaticCall, PublicCall, PublicStaticCall}, PrivateContext},
5+
event::{event_emission::emit_event_in_private, event_interface::EventInterface, EventMessage},
156
};
167
use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}};
178

18-
/// Core interface for interacting with aztec-nr contract features.
9+
/// Core interface for interacting with aztec-nr contract features in private execution contexts.
1910
///
2011
/// This struct is automatically injected into every [`external`](crate::macros::functions::external) and
21-
/// [`internal`](crate::macros::functions::internal) contract function by the Aztec macro system and is accessible
22-
/// through the `self` variable.
12+
/// [`internal`](crate::macros::functions::internal) contract function marked with `"private"` by the Aztec macro
13+
/// system and is accessible through the `self` variable.
2314
///
2415
/// ## Usage in Contract Functions
2516
///
2617
/// Once injected, you can use `self` to:
2718
/// - Access storage: `self.storage.balances.at(owner).read()`
2819
/// - Call contracts: `self.call(Token::at(address).transfer(recipient, amount))`
29-
/// - Emit events: `self.emit(event).deliver_to(recipient, delivery_mode)` (private) or `self.emit(event)` (public)
20+
/// - Emit events: `self.emit(event).deliver_to(recipient, delivery_mode)`
3021
/// - Get the contract address: `self.address`
3122
/// - Get the caller: `self.msg_sender()`
3223
/// - Access low-level Aztec.nr APIs through the context: `self.context`
@@ -49,44 +40,37 @@ use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}};
4940
///
5041
/// ## Type Parameters
5142
///
52-
/// - `Context`: The execution context type - either `&mut PrivateContext`, `PublicContext`, or `UtilityContext`
5343
/// - `Storage`: The contract's storage struct (defined with [`storage`](crate::macros::storage::storage), or `()` if
5444
/// the contract has no storage
5545
/// - `CallSelf`: Macro-generated type for calling contract's own non-view functions
5646
/// - `EnqueueSelf`: Macro-generated type for enqueuing calls to the contract's own non-view functions
5747
/// - `CallSelfStatic`: Macro-generated type for calling contract's own view functions
5848
/// - `EnqueueSelfStatic`: Macro-generated type for enqueuing calls to the contract's own view functions
59-
pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
49+
/// - `CallInternal`: Macro-generated type for calling internal functions
50+
pub struct ContractSelfPrivate<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
6051
/// The address of this contract
6152
pub address: AztecAddress,
6253

6354
/// The contract's storage instance, representing the struct to which the
6455
/// [`storage`](crate::macros::storage::storage) macro was applied in your contract. If the contract has no
6556
/// storage, the type of this will be `()`.
6657
///
67-
/// This storage instance is specialized for the current execution context (private, public, or utility) and
68-
/// provides access to the contract's state variables. Each state variable accepts the context as a generic
69-
/// parameter, which determines its available functionality. For example, a PublicImmutable variable can be read
70-
/// from any context (public, private, or utility) but can only be written to from public contexts.
58+
/// This storage instance is specialized for the current execution context (private) and
59+
/// provides access to the contract's state variables.
7160
///
7261
/// ## Developer Note
7362
///
7463
/// If you've arrived here while trying to access your contract's storage while the `Storage` generic type is set
7564
/// to unit type `()`, it means you haven't yet defined a Storage struct using the
7665
/// [`storage`](crate::macros::storage::storage) macro in your contract. For guidance on setting this up, please
7766
/// refer to our docs: https://docs.aztec.network/developers/docs/guides/smart_contracts/storage
78-
7967
pub storage: Storage,
8068

81-
/// The execution context whose type is determined by the [`external`](crate::macros::functions::external)
82-
/// attribute of the contract function based on the external function type (private, public, or utility).
83-
pub context: Context,
69+
/// The private execution context.
70+
pub context: &mut PrivateContext,
8471

8572
/// Provides type-safe methods for calling this contract's own non-view functions.
8673
///
87-
/// In private and public contexts this will be a struct with appropriate methods; in utility context it will be
88-
/// the unit type `()`.
89-
///
9074
/// Example API:
9175
/// ```noir
9276
/// self.call_self.some_private_function(args)
@@ -95,9 +79,6 @@ pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic,
9579

9680
/// Provides type-safe methods for enqueuing calls to this contract's own non-view functions.
9781
///
98-
/// In private context this will be a struct with appropriate methods; in public and utility contexts it will be
99-
/// the unit type `()`.
100-
///
10182
/// Example API:
10283
/// ```noir
10384
/// self.enqueue_self.some_public_function(args)
@@ -106,19 +87,13 @@ pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic,
10687

10788
/// Provides type-safe methods for calling this contract's own view functions.
10889
///
109-
/// In private and public contexts this will be a struct with appropriate methods; in utility context it will be
110-
/// the unit type `()`.
111-
///
11290
/// Example API:
11391
/// ```noir
11492
/// self.call_self_static.some_view_function(args)
11593
/// ```
11694
pub call_self_static: CallSelfStatic,
11795

118-
/// Provides type-safe methods for enqueuing calls to this contract's own view functions.
119-
///
120-
/// In private context this will be a struct with appropriate methods; in public and utility contexts it will be
121-
/// the unit type `()`.
96+
/// Provides type-safe methods for enqueuing calls to the contract's own view functions.
12297
///
12398
/// Example API:
12499
/// ```noir
@@ -128,24 +103,18 @@ pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic,
128103

129104
/// Provides type-safe methods for calling internal functions.
130105
///
131-
/// In private and public contexts this will be a struct with appropriate methods; in utility context it will be
132-
/// the unit type `()`.
133-
///
134106
/// Example API:
135107
/// ```noir
136108
/// self.internal.some_internal_function(args)
137109
/// ```
138110
pub internal: CallInternal,
139111
}
140112

141-
// Implementation for `ContractSelf` in private execution contexts.
142-
//
143-
// This implementation is used when an external or internal contract function is marked with "private".
144-
impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> ContractSelf<&mut PrivateContext, Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
145-
/// Creates a new `ContractSelf` instance for a private function.
113+
impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> ContractSelfPrivate<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
114+
/// Creates a new `ContractSelfPrivate` instance for a private function.
146115
///
147116
/// This constructor is called automatically by the macro system and should not be called directly.
148-
pub fn new_private(
117+
pub fn new(
149118
context: &mut PrivateContext,
150119
storage: Storage,
151120
call_self: CallSelf,
@@ -335,8 +304,8 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInte
335304

336305
/// Enqueues a privacy-preserving public contract call function.
337306
///
338-
/// This is the same as [`ContractSelf::enqueue`], except it hides this calling contract's address from the target
339-
/// public function (i.e. [`ContractSelf::msg_sender`] will panic).
307+
/// This is the same as [`ContractSelfPrivate::enqueue`], except it hides this calling contract's address from the
308+
/// target public function (i.e. [`ContractSelfPrivate::msg_sender`] will panic).
340309
///
341310
/// This means the origin of the call (msg_sender) will not be publicly visible to any blockchain observers, nor to
342311
/// the target public function. If the target public function reads `self.msg_sender()` the call will revert.
@@ -426,141 +395,3 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInte
426395
call.set_as_teardown_incognito(self.context)
427396
}
428397
}
429-
430-
// Implementation for `ContractSelf` in public execution contexts.
431-
//
432-
// This implementation is used when an external or internal contract function is marked with "public".
433-
impl<Storage, CallSelf, CallSelfStatic, CallInternal> ContractSelf<PublicContext, Storage, CallSelf, (), CallSelfStatic, (), CallInternal> {
434-
/// Creates a new `ContractSelf` instance for a public function.
435-
///
436-
/// This constructor is called automatically by the macro system and should not be called directly.
437-
pub fn new_public(
438-
context: PublicContext,
439-
storage: Storage,
440-
call_self: CallSelf,
441-
call_self_static: CallSelfStatic,
442-
internal: CallInternal,
443-
) -> Self {
444-
Self {
445-
context,
446-
storage,
447-
address: context.this_address(),
448-
call_self,
449-
enqueue_self: (),
450-
call_self_static,
451-
enqueue_self_static: (),
452-
internal,
453-
}
454-
}
455-
456-
/// The address of the contract address that made this function call.
457-
///
458-
/// This is similar to Solidity's `msg.sender` value.
459-
///
460-
/// ## Incognito Calls
461-
///
462-
/// Contracts can call public functions from private ones hiding their identity (see
463-
/// [`enqueue_incognito`](ContractSelf::enqueue_incognito)). This function reverts when executed in such a context.
464-
///
465-
/// If you need to handle these cases, use [`PublicContext::maybe_msg_sender`].
466-
pub fn msg_sender(self: Self) -> AztecAddress {
467-
self.context.maybe_msg_sender().unwrap()
468-
}
469-
470-
/// Emits an event publicly.
471-
///
472-
/// Public events are emitted as plaintext and are therefore visible to everyone. This is is the same as Solidity
473-
/// events on EVM chains.
474-
///
475-
/// Unlike private events, they don't require delivery of an event message.
476-
///
477-
/// # Example
478-
/// ```noir
479-
/// #[event]
480-
/// struct Update { value: Field }
481-
///
482-
/// #[external("public")]
483-
/// fn publish_update(value: Field) {
484-
/// self.emit(Update { value });
485-
/// }
486-
/// ```
487-
///
488-
/// # Cost
489-
///
490-
/// Public event emission is achieved by emitting public transaction logs. A total of `N+1` fields are emitted,
491-
/// where `N` is the serialization length of the event.
492-
pub fn emit<Event>(&mut self, event: Event)
493-
where
494-
Event: EventInterface + Serialize,
495-
{
496-
emit_event_in_public(self.context, event);
497-
}
498-
499-
/// Makes a public contract call.
500-
///
501-
/// Will revert if the called function reverts or runs out of gas.
502-
///
503-
/// # Arguments
504-
/// * `call` - The object representing the public function to invoke.
505-
///
506-
/// # Returns
507-
/// * `T` - Whatever data the called function has returned.
508-
///
509-
/// # Example
510-
/// ```noir
511-
/// self.call(Token::at(address).transfer_in_public(recipient, amount));
512-
/// ```
513-
///
514-
pub unconstrained fn call<let M: u32, let N: u32, T>(self, call: PublicCall<M, N, T>) -> T
515-
where
516-
T: Deserialize,
517-
{
518-
call.call(self.context)
519-
}
520-
521-
/// Makes a public read-only contract call.
522-
///
523-
/// This is similar to Solidity's `staticcall`. The called function cannot modify state or emit events. Any nested
524-
/// calls are constrained to also be static calls.
525-
///
526-
/// Will revert if the called function reverts or runs out of gas.
527-
///
528-
/// # Arguments
529-
/// * `call` - The object representing the read-only public function to invoke.
530-
///
531-
/// # Returns
532-
/// * `T` - Whatever data the called function has returned.
533-
///
534-
/// # Example
535-
/// ```noir
536-
/// self.view(Token::at(address).balance_of_public(recipient));
537-
/// ```
538-
///
539-
pub unconstrained fn view<let M: u32, let N: u32, T>(self, call: PublicStaticCall<M, N, T>) -> T
540-
where
541-
T: Deserialize,
542-
{
543-
call.view(self.context)
544-
}
545-
}
546-
547-
// Implementation for `ContractSelf` in utility execution contexts.
548-
//
549-
// This implementation is used when an external or internal contract function is marked with "utility".
550-
impl<Storage> ContractSelf<UtilityContext, Storage, (), (), (), (), ()> {
551-
/// Creates a new `ContractSelf` instance for a utility function.
552-
///
553-
/// This constructor is called automatically by the macro system and should not be called directly.
554-
pub fn new_utility(context: UtilityContext, storage: Storage) -> Self {
555-
Self {
556-
context,
557-
storage,
558-
address: context.this_address(),
559-
call_self: (),
560-
enqueue_self: (),
561-
call_self_static: (),
562-
enqueue_self_static: (),
563-
internal: (),
564-
}
565-
}
566-
}

0 commit comments

Comments
 (0)