-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Describe implementation assumptions of Substrate regarding (child) storages #580
base: main
Are you sure you want to change the base?
Changes from all commits
3803305
44c39d1
a4a95c5
b0c3337
d715c23
0320fc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,11 +3,47 @@ | |
|
||
Interface for accessing the storage from within the runtime. | ||
|
||
IMPORTANT: As of now, the storage API should silently ignore any keys that start | ||
with the `:child_storage:default:` prefix. This applies to reading and writing. | ||
If the function expects a return value, then _None_ (<<defn-option-type>>) | ||
should be returned. See | ||
https://github.com/paritytech/substrate/issues/12461[substrate issue #12461]. | ||
[#sect-storage-assumptions] | ||
==== Implementation Assumptions | ||
The storage and child storage (<<sect-child-storage-api>>) API in the Substrate | ||
implementation makes some behavioral assumptions on the underlying storage | ||
architecture. While Polkadot Host implementers can decide for themselves on how | ||
those APIs are implemented, those behaviors must be replicated in order for the | ||
Runtime to be executed deterministically. In Substrate, child storages are | ||
namespaced respectively segregated by attaching the `:child_storage:default:` | ||
prefix to every child storage keys, followed by `:<KEY>` for the key within that | ||
child storage. However, the storage API described in this section and the child | ||
storage API share the same database. This means that the storage API can | ||
retrieve child storage entries. | ||
|
||
For example, calling `ext_storage_get_version_1` (<<sect-ext-storage-get>>) with | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this not explained under |
||
the key `:child_storage:default:some_child:some_key` is equivalent to calling | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that this is accurate. As far as I understand, you can only call |
||
`ext_default_child_storage_get_version_1` | ||
(<<sect-ext-default-child-storage-get>>) with child storage key `some_child` and | ||
key `some_key`. | ||
|
||
Importantly, the storage API can only _read_ child storage entries, but must | ||
implement write protection. Any function that modifies data must **silently ignore** | ||
any keys that start with the `:child_storage:` prefix. For | ||
`ext_storage_clear_prefix` (<<sect-ext-storage-clear-prefix>>) specifically, | ||
Comment on lines
+27
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, why not explain this under |
||
this also applies to any key that is a substring of the `:child_storage:` prefix, | ||
such as `:child_sto` (but not `:other`, for example). If the function expects a | ||
return value, then _None_ (<<defn-option-type>>) should be returned. | ||
|
||
Additionally, calling `ext_storage_get_version_1` on a child storage key | ||
directly (without a key within the child storage), such as | ||
`:child_storage:defaut:some_child`, the function returns the root of the child | ||
storage from when `ext_default_child_storage_root` | ||
(<<ext-default-child-storage-root>>) has been **last called** or _None_ | ||
(<<defn-option-type>>) if it has never been called. Respectively, that root | ||
value is cached. Calling `ext_default_child_storage_root` directly always | ||
recomputes the current root value. | ||
|
||
See the following issues for more information: | ||
|
||
* https://github.com/w3f/polkadot-spec/issues/575 | ||
* https://github.com/w3f/polkadot-spec/issues/577 | ||
* https://github.com/paritytech/substrate/issues/12461 | ||
Comment on lines
+44
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't be linking issues in a specification? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This spec isn't supposed to be an explanation of how Substrate works. It is Substrate that is supposed to conform to what this spec says. Linking to an issue saying that we want to remove the feature is IMO not only inappropriate (it places a judgement over how it works) but also doesn't remove the fact that child tries will continue to have to be supported and will forever be part of the Polkadot spec even if we remove its usage from Substrate. |
||
|
||
[#defn-state-version] | ||
.<<defn-state-version, State Version>> | ||
|
@@ -29,6 +65,9 @@ merkle proof (<<defn-hashed-subvalue>>). | |
==== `ext_storage_set` | ||
Sets the value under a given key into storage. | ||
|
||
IMPORTANT: Do note that this API must implement some specific behaviors, | ||
described further in <<sect-storage-assumptions>> | ||
|
||
===== Version 1 - Prototype | ||
---- | ||
(func $ext_storage_set_version_1 | ||
|
@@ -40,9 +79,13 @@ Arguments:: | |
* `value`: a pointer-size (<<defn-runtime-pointer-size>>) containing the | ||
value. | ||
|
||
[#sect-ext-storage-get] | ||
==== `ext_storage_get` | ||
Retrieves the value associated with the given key from storage. | ||
|
||
IMPORTANT: Do note that this API must implement some specific behaviors, | ||
described further in <<sect-storage-assumptions>> | ||
|
||
===== Version 1 - Prototype | ||
---- | ||
(func $ext_storage_get_version_1 | ||
|
@@ -107,11 +150,15 @@ Arguments:: | |
* `return`: an i32 integer value equal to _1_ if the key exists or a value equal | ||
to _0_ if otherwise. | ||
|
||
[#sect-ext-storage-clear-prefix] | ||
==== `ext_storage_clear_prefix` | ||
|
||
Clear the storage of each key/value pair where the key starts with the given | ||
prefix. | ||
|
||
IMPORTANT: Do note that this API must implement some specific behaviors, | ||
described further in <<sect-storage-assumptions>> | ||
|
||
===== Version 1 - Prototype | ||
---- | ||
(func $ext_storage_clear_prefix_version_1 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the purpose of this paragraph. You seem to be literally describing what a specification is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we've been telling the implementers that the "main" storage is fully separated from the child storages and that this functionality can be implemented however they want. But with this new specification we're basically saying that those storages are really not that separate, meaning we kind of insist now that child storage entries are namespaced by a specific prefix and that the Storage API actually can access Child Storages. This paragraph is meant to imply that the Host implementation must simply deal with this "ugliness", but I guess I can clarify this further.
Regarding your other points.
I put everything into one section so it's clear how the underlying data is structured in the Substrate implementation. Those things must also be considered for
read
,append
,next_key
, etc, too. But I should replace the "for example" there with something more formal.As mentioned above, it's to keep things packed together; functions that modify data simply check whether the key starts with
:child_storage:
, but theclear_prefix
function makes an exception by also checking whether the input is a substring of:child_storage:
. If you think it's more appropriate to move that section toext_storage_clear_prefix
I can do so.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't understand. You told something to the implementers, and because you told them something you're writing the spec in a different way than you would have if you hadn't told them?