@@ -59,7 +59,7 @@ Some of the most important methods are summarized here:
59
59
- [ ` mutate(fn) ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.StorageValue.html#tymethod.mutate ) -
60
60
Mutate the value with the provided function.
61
61
- [ ` take() ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.StorageValue.html#tymethod.take ) -
62
- Remove the value from storage.
62
+ Load the value and remove it from storage.
63
63
64
64
### Storage Maps
65
65
@@ -85,7 +85,7 @@ that is similar to that of Storage Values.
85
85
- ` mutate ` - Use the provided function to mutate the value associated with the given key. Docs:
86
86
[ ` StorageMap#mutate(key, fn) ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.StorageMap.html#tymethod.mutate ) ,
87
87
[ ` StorageDoubleMap#mutate(key1, key2, fn) ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.StorageDoubleMap.html#tymethod.mutate )
88
- - ` take ` - Remove the value associated with the given key from storage. Docs:
88
+ - ` take ` - Load the value associated with the given key and remove it from storage. Docs:
89
89
[ ` StorageMap#take(key) ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.StorageMap.html#tymethod.take ) ,
90
90
[ ` StorageDoubleMap#take(key1, key2) ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.StorageDoubleMap.html#tymethod.take )
91
91
@@ -95,13 +95,14 @@ Depending on [the hashing algorithm](#Transparent-Hashing-Algorithms) that you s
95
95
map's keys, you may be able to iterate across its keys and values. Because maps are often used to
96
96
track unbounded sets of data (account balances, for example) it is especially likely to exceed block
97
97
production time by iterating over maps in their entirety within the runtime. Furthermore, because
98
- maps are comprised of more layers of indirection than native lists, they are significantly more
99
- costly than lists to iterate over with respect to time. This is not to say that it is "wrong" to
100
- iterate over maps in your runtime; in general Substrate focuses on "first principles" as opposed to
101
- hard and fast rules of right and wrong. Being efficient within the runtime of a blockchain is an
102
- important first principle of Substrate and this information is designed to help you understand _ all_
103
- of Substrate's storage capabilities and use them in a way that respects the important first
104
- principles around which they were designed.
98
+ accessing the elements of a map requires more pointer dereferencing than accessing the elements of a
99
+ native list, maps are significantly _ more_ costly than lists to iterate over with respect to time.
100
+ This is not to say that it is "wrong" to iterate over maps in your runtime; in general Substrate
101
+ focuses on "[ first principles] ( #Best-Practices ) " as opposed to hard and fast rules of right and
102
+ wrong. Being efficient within the runtime of a blockchain is an important first principle of
103
+ Substrate and this information is designed to help you understand _ all_ of Substrate's storage
104
+ capabilities and use them in a way that respects the important first principles around which they
105
+ were designed.
105
106
106
107
##### Iterable Storage Map Methods
107
108
@@ -110,16 +111,16 @@ Storage Double Maps, the `iter` and `drain` methods require a parameter, i.e. th
110
111
111
112
- ` iter ` - Enumerate all elements in the map in no particular order. If you alter the map while
112
113
doing this, you'll get undefined results. Docs:
113
- [ IterableStorageMap#iter()] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageMap.html#tymethod.iter ) ,
114
- [ IterableStorageDoubleMap#iter(key1)] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageDoubleMap.html#tymethod.iter )
114
+ [ ` IterableStorageMap#iter() ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageMap.html#tymethod.iter ) ,
115
+ [ ` IterableStorageDoubleMap#iter(key1) ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageDoubleMap.html#tymethod.iter )
115
116
- ` drain ` - Remove all elements from the map and iterate through them in no particular order. If you
116
117
add elements to the map while doing this, you'll get undefined results. Docs:
117
- [ IterableStorageMap#drain()] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageMap.html#tymethod.drain ) ,
118
- [ IterableStorageDoubleMap#drain(key1)] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageDoubleMap.html#tymethod.drain )
118
+ [ ` IterableStorageMap#drain() ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageMap.html#tymethod.drain ) ,
119
+ [ ` IterableStorageDoubleMap#drain(key1) ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageDoubleMap.html#tymethod.drain )
119
120
- ` translate ` - Use the provided function to translate all elements of the map, in no particular
120
121
order. To remove an element from the map, return ` None ` from the translation function. Docs:
121
- [ IterableStorageMap#translate(fn)] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageMap.html#tymethod.translate ) ,
122
- [ IterableStorageDoubleMap#translate(fn)] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageDoubleMap.html#tymethod.translate )
122
+ [ ` IterableStorageMap#translate(fn) ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageMap.html#tymethod.translate ) ,
123
+ [ ` IterableStorageDoubleMap#translate(fn) ` ] ( https://substrate.dev/rustdocs/master/frame_support/storage/trait.IterableStorageDoubleMap.html#tymethod.translate )
123
124
124
125
#### Hashing Algorithms
125
126
@@ -169,7 +170,7 @@ those that are transparent:
169
170
| [ Identity] ( https://substrate.dev/rustdocs/master/frame_support/struct.Identity.html ) | | |
170
171
171
172
The Identity hasher encapsulates a hashing algorithm that has an output equal to its input (the
172
- identity function).This type of hasher should only be used when the starting key is already a
173
+ identity function). This type of hasher should only be used when the starting key is already a
173
174
cryptographic hash.
174
175
175
176
## Declaring Storage Items
@@ -182,18 +183,27 @@ type of storage item:
182
183
``` rust
183
184
decl_storage! {
184
185
trait Store for Module <T : Trait > as Example {
186
+ SomePrivateValue : u32 ;
185
187
pub SomePrimitiveValue get (fn some_primitive_value ): u32 ;
186
188
// complex types are prefaced by T::
187
189
pub SomeComplexValue : T :: AccountId ;
188
- pub SomeMap get (fn some_map ): map hasher (blake2_128_concat ) str => u32 ;
189
- pub SomeDoubleMap : double_map hasher (blake2_128_concat ) str , hasher (blake2_128_concat ) str => u32 ;
190
+ pub SomeMap get (fn some_map ): map hasher (blake2_128_concat ) T :: AccountId => u32 ;
191
+ pub SomeDoubleMap : double_map hasher (blake2_128_concat ) u32 , hasher (blake2_128_concat ) T :: AccountId => u32 ;
190
192
}
191
193
}
192
194
```
193
195
194
196
Notice that the map storage items specify [ the hashing algorithm] ( #Hashing-Algorithms ) that will be
195
197
used.
196
198
199
+ ### Visibility
200
+
201
+ In the example above, all the storage items except ` SomePrivateValue ` are made public by way of the
202
+ ` pub ` keyword. Blockchain storage is always publicly
203
+ [ visible from _ outside_ of the runtime] ( #Accessing-Storage-Items ) ; the visibility of Substrate
204
+ storage items only impacts whether or not other runtime pallets will be able to access the storage
205
+ item.
206
+
197
207
### Getter Methods
198
208
199
209
The ` decl_storage ` macro provides an optional ` get ` extension that can be used to implement a getter
@@ -254,19 +264,87 @@ interacting with Substrate-based blockchains, including querying storage. Refer
254
264
key-value database to implement the different kinds of Storage Items and how to query this database
255
265
directly by way of the RPC server.
256
266
257
- ## Child Storage Tries
267
+ ## Best Practices
258
268
259
- TODO
269
+ Substrate's goal is to provide a flexible framework that allows people to build the blockchain that
270
+ suits their needs - the creators of Substrate tend not to think in terms of "right" or "wrong". That
271
+ being said, the Substrate codebase adheres to a number of best practices in order to promote the
272
+ creation of blockchain networks that are secure, performant and maintainable in the long-term. The
273
+ following sections outline best practices for using Substrate storage and also describe the
274
+ important first principles that motivated them.
275
+
276
+ ### What to Store
277
+
278
+ Remember, the fundamental principle of blockchain runtime storage is to minimize its use. Only
279
+ _ consensus-critical_ data should be stored in your runtime. When possible, use techniques like
280
+ hashing to reduce the amount of data you must store. For instance, many of Substrate's governance
281
+ capabilities (e.g.
282
+ [ the Democracy pallet's ` propose ` dispatchable] ( https://crates.parity.io/pallet_democracy/enum.Call.html#variant.propose ) )
283
+ allow network participants to vote on the _ hash_ of a dispatchable call, which is always bounded in
284
+ size, as opposed to the call itself, which may be unbounded in length. This is especially true in
285
+ the case of runtime upgrades where the dispatchable call takes an entire runtime WASM blob as its
286
+ parameter. Because these governance mechanisms are implemented _ on-chain_ , all the information that
287
+ is needed to come to consensus on the state of a given proposal must also be stored on-chain - this
288
+ includes _ what_ is being voted on. However, by binding an on-chain proposal to its hash, Substrate's
289
+ governance mechanisms allow this to be done in a way that defers bringing all the data associated
290
+ with a proposal on-chain until _ after_ it has been approved. This means that storage is not wasted
291
+ on proposals that fail. Once a proposal has passed, someone can initiate the actual dispatchable
292
+ call (including all its parameters), which will be hashed and compared to the hash in the proposal.
293
+ Another common pattern for using hashes to minimize data that is stored on-chain is to store the
294
+ metadata associated with an object in [ IPFS] ( https://ipfs.io/ ) ; this means that only the IPFS
295
+ location (a hash that is bounded in size) needs to be stored on-chain.
296
+
297
+ Hashes are only one mechanism that can be used to control the size of runtime storage. An example of
298
+ another mechanism is [ bounds] ( #Create-Bounds ) .
260
299
261
- ## Storage Cache
300
+ ### Verify First, Write Last
262
301
263
- TODO
302
+ The state of a blockchain network's storage is immutable; data can be changed, but there will always
303
+ be a record of these changes, and making them typically incurs costs. Because of this, it is
304
+ important that data is only persisted to runtime storage when it is certain that all preconditions
305
+ have been met. In general, code blocks that may result in adding data to storage should be
306
+ structured as follows:
264
307
265
- ## Best Practices
308
+ ``` rust
309
+ {
310
+ // all checks and throwing code go here
311
+
312
+ // all storage writes go here; no throwing code below this line
313
+
314
+ // all event emissions go here
315
+ }
316
+ ```
317
+
318
+ Do not use runtime storage to store intermediate or transient data within the context of an
319
+ operation that is logically atomic or data that will not be needed if the operation is to fail. This
320
+ does not mean that runtime storage should not be used to track the state of ongoing actions that
321
+ require multiple atomic operations, as in the case of
322
+ [ the multi-signature capabilities from the Utility pallet] ( https://crates.parity.io/pallet_utility/enum.Call.html#variant.as_multi ) .
323
+ In this case, runtime storage is used to track the signatories on a dispatchable call even though a
324
+ given call may never receive enough signatures to actually be invoked. In this case, each signature
325
+ is considered an atomic event in the ongoing multi-signature operation; the data needed to record a
326
+ single signature is not stored until after all the preconditions associated with that signature have
327
+ been met.
328
+
329
+ ### Create Bounds
330
+
331
+ Creating bounds on the size of storage items is an extremely effective way to control the use of
332
+ runtime storage and one that is used repeatedly throughout the Substrate codebase. In general, any
333
+ storage item whose size is determined by user action should have a bound on it.
334
+ [ The multi-signature capabilities from the Utility pallet] ( https://crates.parity.io/pallet_utility/trait.Trait.html#associatedtype.MaxSignatories )
335
+ that were described above are one such example. In this case, the list of signatories associated
336
+ with a multi-signature operation is provided by the multi-signature participants. Because this
337
+ signatory list is [ necessary to come to consensus] ( #What-to-Store ) on the state of the
338
+ multi-signature operation, it must be stored in the runtime. However, in order to give runtime
339
+ developers control over how much space in storage these lists may occupy, the Utility pallet
340
+ requires users to configure a bound on this number that will be included as a
341
+ [ precondition] ( #Verify-First-Write-Last ) before anything is written to storage.
342
+
343
+ ## Child Storage Tries
266
344
267
345
TODO
268
346
269
- ### Verify First, Write Last
347
+ ## Storage Cache
270
348
271
349
TODO
272
350
0 commit comments