@@ -2,42 +2,80 @@ use crate::context::{PublicContext, UtilityContext};
22use crate::state_vars::state_variable::StateVariable ;
33use dep::protocol_types::traits::Packable ;
44
5- /// # PublicMutable
5+ /// Mutable public values.
66///
7- /// PublicMutable is a public state variable type for values that can be read
8- /// and written within public [external](crate::macros::functions::external) functions of your smart contract .
7+ /// This is the most basic public state variable. It is equivalent to a non-`immutable` non-`constant` Solidity state
8+ /// variable .
99///
10- /// You can declare a state variable of type PublicMutable within your contract's
11- /// [storage](crate::macros::storage::storage) struct:
10+ /// ## Access Patterns
1211///
13- /// E.g.:
14- /// `your_variable: PublicMutable<T, Context>`
15- /// or:
16- /// `your_mapping: Map<Field, PublicMutable<T, Context>>`
12+ /// A value stored in a `PublicMutable` can be read and written from public contract functions. It is not possible to
13+ /// read or write another contract's `PublicMutable` values directly: the only way to achieve this is to call a public
14+ /// function on that contract.
1715///
18- /// The methods of PublicMutable are:
19- /// - `read`
20- /// - `write`
21- /// (see the methods' own doc comments for more info).
16+ /// It is not possible to read or write a `PublicMutable` from private contract functions. It is possible however for a
17+ /// private function to [enqueue a public call to itself](crate::contract_self::ContractSelf::enqueue_self) in which it
18+ /// performs the required operation.
2219///
23- /// ## Example.
20+ /// For an immutable variant which can be read from private functions, see
21+ /// [PublicImmutable](crate::state_vars::PublicImmutable).
2422///
25- /// A voting contract's proposal count can be represented as a PublicMutable<u64>.
26- /// The count can be read by anyone to see how many proposals exist, and incremented
27- /// when new proposals are submitted.
23+ /// For a mutable (with restrictions) variant which can be read from private functions see
24+ /// [DelayedPublicMutable](crate::state_vars::DelayedPublicMutable).
2825///
29- /// # Generic Parameters:
26+ /// ## Privacy
3027///
31- /// * `T` - The type of value stored (must implement Packable).
32- /// * `Context` - The execution context (PublicContext or UtilityContext) .
28+ /// `PublicMutable` provides zero privacy. All write and read operations are public: the entire network can see these
29+ /// accesses and the data involved .
3330///
34- /// # Advanced
31+ /// ## Use Cases
3532///
36- /// Unlike private state variables which use notes, PublicMutable stores values
37- /// directly in Aztec's public data tree. This enables direct read and write
38- /// access to the current state during public function execution.
33+ /// This is suitable for any kind of global state that needs to be accessible by everyone. For example, a token may have
34+ /// a public total supply, or a voting contract may have public vote tallies.
3935///
40- /// docs:start:public_mutable_struct
36+ /// Note that contracts having public values does not necessarily mean the the actions that update these values must
37+ /// themselves be wholly public. For example, the token could allow for private minting and burning, and casting a vote
38+ /// could be kept private: these private functions would enqueue a public function that writes to the `PublicMutable`.
39+ ///
40+ /// Similarly, private functions can enqueue a public call in which the `PublicMutable` is checked to meet some
41+ /// condition. For example, a private action might be executable only if the vote count has exceeded some threshold, in
42+ /// which case the private function would enqueue a public function that reads from the `PublicMutable`.
43+ ///
44+ /// Such patterns preserve the privacy of the account that executed the action, as well as details related to the
45+ /// private execution itself, but they _do_ reveal that the transaction interacted with the `PublicMutable` value (and
46+ /// hence that the contract was called), as all accesses to it are public. The
47+ /// [only_self](crate::macros::functions::only_self) attribute is very useful when implementing this.
48+ ///
49+ /// ## Examples
50+ ///
51+ /// Declaring a `PublicMutable` in the the contract's [storage](crate::macros::storage::storage) struct requires
52+ /// specifying the type `T` that is stored in the variable:
53+ ///
54+ /// ```noir
55+ /// #[storage]
56+ /// struct Storage<Context> {
57+ /// total_supply: PublicMutable<u128, Context>,
58+ /// public_balances: Map<AztecAddress, PublicMutable<u128, Context>, Context>,
59+ ///
60+ /// vote_tallies: Map<ElectionId, PublicMutable<u128, Context>, Context>,
61+ /// }
62+ /// ```
63+ ///
64+ /// ## Requirements
65+ ///
66+ /// The type `T` stored in the `PublicMutable` must implement the `Packable` trait.
67+ ///
68+ /// ## Implementation Details
69+ ///
70+ /// Values are packed and stored directly in the public storage tree, with no overhead. A `PublicMutable` therefore
71+ /// takes up as many storage slots as the packing length of the stored type `T`.
72+ ///
73+ /// Private reads are not possible because private functions do not have access to the current network state, only the
74+ /// _past_ state at the anchor block. They _can_ perform historical reads of `PublicMutable` values at past times, but
75+ /// they have no way to guarantee that the value has not changed since then.
76+ /// [PublicImmutable](crate::state_vars::PublicImmutable) and
77+ /// [DelayedPublicMutable](crate::state_vars::DelayedPublicMutable) are examples of public state variables that _can_ be
78+ /// read privately by restricting mutation.
4179pub struct PublicMutable <T , Context > {
4280 context : Context ,
4381 storage_slot : Field ,
@@ -58,33 +96,90 @@ where
5896}
5997
6098impl <T > PublicMutable <T , PublicContext > {
61- /// Reads the current value stored in this PublicMutable state variable .
99+ /// Returns the current value.
62100 ///
63- /// # Returns
101+ /// If [write](PublicMutable::write) has never been called, then this returns the default empty public storage
102+ /// value, which is all zeroes - equivalent to `let t = T::unpack(std::mem::zeroed());`.
64103 ///
65- /// * `T` - The current value stored in this PublicMutable.
104+ /// It is not possible to detect if a `PublicMutable` has ever been initialized or not other than by testing for the
105+ /// zero sentinel value. For a more robust solution, store an `Option<T>` in the `PublicMutable`.
66106 ///
67- /// docs:start:public_mutable_struct_read
107+ /// ## Examples
108+ ///
109+ /// A public getter that returns the current value:
110+ /// ```noir
111+ /// #[external("public")]
112+ /// fn get_total_supply() -> u128 {
113+ /// self.storage.total_supply.read()
114+ /// }
115+ /// ```
116+ ///
117+ /// An [only_self](crate::macros::functions::only_self) helper that asserts a condition a private function requires:
118+ /// ```noir
119+ /// #[external("private")]
120+ /// fn execute_proposal(election_id: ElectionId) {
121+ /// self.enqueue_self._assert_vote_passed(election_id);
122+ ///
123+ /// // execute the proposal - this remains private
124+ /// }
125+ ///
126+ /// #[external("public")]
127+ /// #[only_self]
128+ /// fn _assert_vote_passed(election_id: ElectionId) {
129+ /// assert(self.storage.vote_tallies.at(election_id).read() >= VOTE_PASSED_THRESHOLD);
130+ /// }
131+ /// ```
132+ ///
133+ /// ## Cost
134+ ///
135+ /// The `SLOAD` AVM opcode is invoked a number of times equal to `T`'s packed length.
68136 pub fn read (self ) -> T
69137 where
70138 T : Packable ,
71139 {
72140 self .context .storage_read (self .storage_slot )
73141 }
74142
75- /// Writes a new value to this PublicMutable state variable.
143+ /// Stores a new value.
144+ ///
145+ /// The old value is overridden and cannot be recovered. The new value can be immediately retrieved by
146+ /// [read](PublicMutable::read).
147+ ///
148+ /// ## Examples
149+ ///
150+ /// A public setter that updates the current value:
151+ /// ```noir
152+ /// #[external("public")]
153+ /// fn mint_tokens(recipient: AztecAddress, amount: u128) {
154+ /// let current_recipient_balance = self.storage.public_balances.at(recipient).read();
155+ /// self.storage.public_balances.at(recipient).write(current_recipient_balance + amount);
156+ ///
157+ /// let current_supply = self.storage.total_supply.read();
158+ /// self.storage.total_supply.write(current_supply + amount);
159+ /// }
160+ /// ```
76161 ///
77- /// # Arguments
162+ /// An [only_self](crate::macros::functions::only_self) helper that updates public state trigered by a private
163+ /// function:
164+ /// ```noir
165+ /// #[external("private")]
166+ /// fn vote_for_proposal(election_id: ElectionId, votes: u128) {
167+ /// // validate the sender can cast this many votes - this remains private
78168 ///
79- /// * `value` - The new value to store in this PublicMutable.
169+ /// self.enqueue_self._tally_vote(election_id, votes);
170+ /// }
80171 ///
81- /// # Advanced
172+ /// #[external("public")]
173+ /// #[only_self]
174+ /// fn _tally_vote(election_id: ElectionId, votes: u128) {
175+ /// let current = self.storage.vote_tallies.read();
176+ /// self.storage.vote_tallies.write(current + votes);
177+ /// }
178+ /// ```
82179 ///
83- /// This function updates the value stored in Aztec's public data tree.
84- /// The new value becomes immediately available to subsequent reads within
85- /// the same transaction.
180+ /// ## Cost
86181 ///
87- /// docs:start:public_mutable_struct_write
182+ /// The `SSTORE` AVM opcode is invoked a number of times equal to `T`'s packed length.
88183 pub fn write (self , value : T )
89184 where
90185 T : Packable ,
@@ -94,15 +189,22 @@ impl<T> PublicMutable<T, PublicContext> {
94189}
95190
96191impl <T > PublicMutable <T , UtilityContext > {
97- /// Reads the current value stored in this PublicMutable state variable .
192+ /// Returns the value at the anchor block .
98193 ///
99- /// Notice that this function is executable only within a UtilityContext, which
100- /// is an unconstrained environment on the user's local device .
194+ /// If [write](PublicMutable::write) has never been called, then this returns the default empty public storage
195+ /// value, which is all zeroes - equivalent to `let t = T::unpack(std::mem::zeroed());` .
101196 ///
102- /// # Returns
197+ /// It is not possible to detect if a `PublicMutable` has ever been initialized or not other than by testing for the
198+ /// zero sentinel value. For a more robust solution, store an `Option<T>` in the `PublicMutable`.
103199 ///
104- /// * `T` - The current value stored in this PublicMutable.
200+ /// ## Examples
105201 ///
202+ /// ```noir
203+ /// #[external("utility")]
204+ /// fn get_total_supply() -> u128 {
205+ /// self.storage.total_supply.read()
206+ /// }
207+ /// ```
106208 pub unconstrained fn read (self ) -> T
107209 where
108210 T : Packable ,
0 commit comments