-
Notifications
You must be signed in to change notification settings - Fork 2.2k
[WIP] Feat - Namespaced storage inspection: forge inspect storage-layout --eip7201
#11672
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
base: master
Are you sure you want to change the base?
[WIP] Feat - Namespaced storage inspection: forge inspect storage-layout --eip7201
#11672
Conversation
I introduce here a new flag for `forge inspect storage-layout --ast` , `--eip7201`; While it's not completely automated, it is now possible to inspect eip7201 storage, even if it was through the use of libraries and other proxy patterns. It requires a good knowledge of the storage schemas to begin with, as it requires to flag in the source code pertinent bits. Available Natspec flag are as follows: - `@custom:storage-bucket <GETTER_NAME>` : to be put just before a contract's constructor that uses eip7201 storage, for each slot used - `@custom:storage-bucket-transient <GETER_NAME>` : same, but for transient storage slots - `@custom:storage-bucket-slot (opt.) <?SLOT_ID> ` : to be put just before a storage slot's declaration. - `@custom:storage-bucket-transient-slot (opt.) <SLOT_ID?>`: same, but for transient storage - `@custom:storage-bucket-struct <NAMESPACE>:<STRUCT_NAME>` : to put above all declared structs - `@custom:storage-bucket-enum <NAMESPACE>:<ENUM_NAME>` : for enum declarations - `@custom:storage-bucket-usertype <NAMESPACE>:<USERTYPE_NAME>` : for user type declarations - `@custom:storage-bucket-type ${@Custom:storage-bucket} <"keyvalue" / "singleton">` : tags the eip7201 storage's getter with the right name and type (schemaless getter, key/value pair; arrays are not supported yet) - `@custom:storage-bucket-value <GETTER_RETURN_VALUE>` The script is able to fill in the blancs most of the time; it'll get better with time tho
removed a few Natspec tags (storage-bucket-value, storage-bucket-key); made optional the `storage-bucket-slot` (it is now inferred from the contract itself); To make eip7201 storage detectable by Foundry, just add : - `@custom:storage-bucket-schema <STORAGE-ID>` over a getter function that reads from the eip7201 slot - `@custom:storage-bucket <STORAGE-ID>` over the constructor of a contract that uses that storage slot <STORAGE-ID> must match
this solves #7662 |
|
I didn't know about Solar, that's on me As for the custom tags, yeah, they're not part of the original EIP (though they do define one |
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 appreciate the effort put into this, but as @DaniPopes mentioned we have most of the tools in place to do this already, and this re-implements a lot of it. For example, we already have Natspec parsers, and we also have a more robust parsing approach for user defined types like structs/enums/mappings.
This PR is also not up to spec with the EIP, which I think is important: I don't think it is super useful that we roll our own approach, even if we think it would be better, as people are already using EIP7201 as-is. As such, I would only be willing to merge something that is up to spec. The spec is also relatively simple:
- Parse the source file, looking for any user defined types that have
@custom:storage-location eip7201:<offset>
- Add these to the storage layout, computing their storage layout as normal but adding the offset.
Let me know if you need pointers on how to implement this :)
I'll gladly remove my own parsing logic in favour of something more robust like Solar's; I've started reading the documentation, but if there are specific sections you'd recommend starting with, please let me know @onbjerg. Regarding the Natspec flags, I think we could rearrange things and re-use the one from the EIP verbatim; however, I suspect we may still need at least one more. Let me explain: In the EIP, the author defined But when it comes to storage layout, we’re missing context on how that slot is actually used: are we storing a single instance of a structure, or is that slot acting as the base of a mapping or an array? Maybe I’m overthinking it and we don’t need this level of detail in a storage-layout report... but I thought it would be valuable if we could. I can’t really say the authors of the EIP were shortsighted, given how popular this storage abstraction has become in just a couple of years, but re-reading it, we have:
I get that the authors probably didn’t want to open that can of worms, but the lack of detail on “is this namespace meant to be a single object, a mapping, or an array” makes a big difference:
We can’t use the same namespace for more than one of these three cases without overwriting critical data. So to compute a valid storage layout, that context either needs to be given explicitly or inferred from compiler artifacts (which is what I was attempting to do here, a bit haphazardly). Hopefully Solar’s parsing capabilities are strong enough that this becomes trivial. But if it's not, what do you think we should do ? On a side-note, Certora is using a somewhat-similar method, and have created a few storage layout annotations of their own to bridge that gap. |
forge inspect storage-layout --eip7201
forge inspect storage-layout --eip7201
What this EIP does is allow you to specify what The way this trick plays out in practice is usually that you define a struct. That is to say, I don't recall anyone using a mapping directly, they'd define a wrapper struct that holds a mapping. There is (almost?) no cost to doing so. |
That makes a lot of sense; not only that, that means I have been using that EIP the wrong way for a while. Thank you very much I'll keep you guys posted when the Solar version for this PR is ready |
Motivation
It would be pretty nice to inspect the storage layout of contracts with namespaced storage; especially since this development pattern has become more and more prevalent over the years
Solution
I introduce a few Natspec custom flags that, with the help of AST artifacts, is enough to reliably detect the exact layout of an EIP7201 storage slot and its underlying, or even nested Structs.
@custom:storage-bucket <BUCKET_ID>
: to declare over the constructor of the contract implementing the namespaced storage@custom:storage-bucket-transient <BUCKET_ID>
: same, but for transient storage slots@custom:storage-bucket-schema <BUCKET_ID>
: to declare over a getter function that returns the Namespaced storage object@custom:storage-bucket-slot (opt.) <?SLOT_ID>
: to be put just before a storage slot's declaration.@custom:storage-bucket-transient-slot (opt.) <SLOT_ID?>
: same, but for transient storageMy hope is that, with enough build artifacts trickery, it would be possible to reduce the amount of natspecs comments to one per
SLOT_ID
; more in line with the EIP.PR Checklist