Struct SinglePrivateMutable
pub struct SinglePrivateMutable<Note, Context>
{ /* private fields */ }
Implementations
impl<Context, Note> SinglePrivateMutable<Note, Context>
impl<Note> SinglePrivateMutable<Note, UtilityContext>
pub unconstrained fn is_initialized(self) -> bool
Returns whether this SinglePrivateImmutable has been initialized.
pub unconstrained fn view_note(self) -> Note
Returns the current note in this SinglePrivateMutable.
impl<Note> SinglePrivateMutable<Note, &mut PrivateContext>
pub fn initialize(self, note: Note, owner: AztecAddress) -> NoteMessage<Note>
Initializes a SinglePrivateMutable state variable instance with its first note and returns a NoteMessage
that allows you to decide what method of note message delivery to use for the new note.
This function can only be called once per SinglePrivateMutable. Subsequent calls will fail because the initialization nullifier will already exist.
pub fn replace<Env>(
self,
f: fn[Env](Note) -> Note,
new_owner: AztecAddress,
) -> NoteMessage<Note>
Reads the current note of a SinglePrivateMutable state variable, nullifies it, and inserts a new note for
new_owner produced by the provided function f.
This function returns a NoteMessage that allows you to decide what method of note message delivery to use for the new note.
This function implements a "read-and-replace" pattern for updating private state in Aztec. It first retrieves
the current note, then nullifies it (marking it as spent), and finally inserts a new_note produced by the
user-provided function f. The function f takes the current note and returns a new note that will replace the
current note and become the "current value".
This function can only be called after the SinglePrivateMutable has been initialized. If called on an
uninitialized SinglePrivateMutable, it will fail because there is no current note to replace. If you don't know
if the state variable has been initialized already, you can use initialize_or_replace to handle both cases.
The nullification of the previous note ensures that it cannot be used again, maintaining the invariant that a SinglePrivateMutable has exactly one current note.
pub fn initialize_or_replace<Env>(
self,
f: fn[Env](Option<Note>) -> Note,
new_owner: AztecAddress,
) -> NoteMessage<Note>
Initializes the SinglePrivateMutable if it's uninitialized, or replaces the current note using a transform
function f while the new note is owned by new_owner. The function f takes an Option with the current
Note and returns the Note to insert. The Option is none if the state variable was not initialized.
This function returns a NoteMessage that allows you to decide what method of note message delivery to use for the new note.
pub fn get_note(self) -> NoteMessage<Note>
Reads the current note of a SinglePrivateMutable state variable instance. The read is performed by nullifying the current note and inserting a new note with the same value. By nullifying the current note, we ensure that we're reading the latest note.
This function returns a NoteMessage that allows you to decide what method of note message delivery to use for the new note.
A state variable that holds a single private value that can be changed (unlike crate::state_vars::private_mutable::PrivateMutable, which holds one private value per account - hence the name 'single').
Because this private value has no semantic owner, it is up to the application to determine which accounts will learn of its existence via crate::note::note_message::NoteMessage::deliver_to.
Usage
Unlike crate::state_vars::private_immutable::PrivateImmutable which is "owned" (requiring wrapping in an crate::state_vars::owned::Owned state variable), SinglePrivateMutable is used directly in storage:
Reading from a SinglePrivateMutable nullifies the current note, which restricts the use of this state variable to situations where race conditions are not a concern. This is commonly the case when dealing with admin-only functions. Given that this state variable works with private state it makes sense to use it only when we need the value to be known by a single individual or a closed set of parties.
Examples
Account contract with signing key rotation
An account contract's signing key can be modeled as a SinglePrivateMutable. The "current value" of
this state variable holds the active signing key. When the account owner wishes to update the signing key, they
invoke the
replacefunction, providing a new public key note as input.Private Token
A private token's admin and total supply can be stored in two single private mutable state variables:
When the mint or burn functions are invoked, we check that the message sender corresponds to the current value of the admin state variable. Then we update the total supply and balances. The resulting note message can then be delivered to an account whose encryption keys are known to a closed set of parties. With this approach, only the admin can modify the total supply of the token, but anyone with the relevant encryption keys can audit it.
Requirements
The contract that holds this state variable must have keys associated with it. This is because the initialization nullifier includes the contract's nullifying secret key (nsk) in its preimage. This is expected to not ever be a problem because the contracts that use SinglePrivateMutable generally have keys associated with them (account contracts or escrow contracts).
Warning
The methods of this state variable that replace the underlying note return a NoteMessage that allows you to decide what method of note message delivery to use. Keep in mind that unless the caller of the relevant function is incentivized to deliver the note you should use constrained delivery or you are at a risk of a malicious actor bricking the contract.