-
Notifications
You must be signed in to change notification settings - Fork 187
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
Json ABI reflects presence of self parameter. #527
Conversation
What do you guys think about placing all builtin attributes under e.g. |
I wouldn't want to take up so many identifiers on |
Btw, we should make sure to always set the |
I actually like the As noted in our other discussion about const values, I don't think |
I can understand that reasoning but I'm trying to look through the perspective of code auditing. It's helpful to be able to tell in what kind of access category a function falls just by looking at its signature. I'm wondering if something like the following could work nicely:
Examples: Takes
Takes
Takes
I haven't thought too much about this, just thinking out loud here. |
Yeah, I think I'm on board with this. Regarding the I've been thinking about this, and I'm not sure how to make it not feel weird. How is this function foo called? I think we should consider what categories we'd like to divide functions into, and how/if we intend for the user to indicate the category explicitly, and how the compiler will detect/enforce it. Is it useful to distinguish between functions that read from storage and those that read from other state? Solidity doesn't, for example. In either case, the function is impure. Starting with solidity's categories (pure/view/mutating; ignoring payable vs non-payable for now). From the solidity docs:
With Grant's proposal, a function taking self is a clear indicator that the function can read from the state (assuming we also make retrieving address balance part of self/self.ctx). It's impossible to write a "view" function that doesn't take self.
A function taking
If not, how do we differentiate between functions that have the power to do these things and those that don't? I have a vague feeling that adding the concept of "capabilities" might be a reasonable answer here, but I'm not yet sure what that would look like. The general idea is that a function can't perform an action unless its given the capability to do so by the caller (who must have that capability, of course). In practice this might look like the ctx arg idea; a function can take ctx if it needs the ability to read from state, but that ctx needs to be passed in explicitly. Perhaps a function that takes self or mut self have access to these capabilities via the self arg, and can pass them to functions they call, which can then pass them onward, etc. For example:
🤷 |
Been thinking about the ctx arg and capabilities idea some more. RFC:
Following this line of thought, one could "tighten up" the definition of external interfaces, so they're safer to call from fe:
|
Thank you for giving so much thought to this idea. I hadn't thought that far ahead, but I think the suggestion has merit.
Mmh, yeah, if we are trying to make it obvious to derive the category in which a function falls from its signature then that's a problem. A function that we tag as I'm a bit worried where taking all the individual capabilities as arguments will lead though. For instance, I had always thought that things like But I guess in your proposal I'm wondering if instead of using these magical arguments we could use generics + traits instead. Notice that
I think the nice thing here is that we reduce the "magic" a bit and these traits can later work together with the import system such as in the following example:
For my own clarity I try to define categories that are compatible with the existing rules of pure and view but go beyond them to provide more safety:
As we can see this scheme makes the capabilities of the function very clear from the function signature but doesn't require us to add any magic parameters. Also, another cool thing about this scheme is that it allows us to better explore ideas like #519. E.g. in the future we might have a new
As we can see ☝️ we are still using One obvious problem with this scheme is that names could become ambigious e.g.
I think that this could be worked around e.g. by strictly prohibiting locally defined functions/attributes with conflicting names. Or if that's a bit too harsh I'm sure other ways could be explored to resolve that issue. [1] The eWASM project lost a bit of steam but I wouldn't bet on it being entirely dead yet |
A couple thoughts:
In this example, I guess send_money would have to be defined as
|
If ctx is part of self, and ctx has a named type, one could just define a function that takes ctx (which wouldn't be callable externally):
Perhaps all of the (non-contract) chain state readers and mutators could be under a single object, and you can only mutate via a mut reference (which is only obtainable via mut self):
The child objects of Context could also have type names, and a function could be defined that just takes msg. In the future, the Context object could give access to a ProtectedMsg type, or some other kind of single-use msg value, etc. A function taking any of these state r/w types is a pretty clear indicator of what the function does, and what category it falls in. |
I guess we could say that functions that take these state types can be called externally, and the values are magically provided (in the same way that self is magically provided in that case). |
Yep, that's a valid concern 👍
Yes, that's right so another thing that initially thought of would be to use the generics to scope the context object as in:
Yes, I think that's true. Well, I guess I'm on board with the idea if we keep the magic injection to just I also like the idea of demanding
Yes, I think so but I think we should reserve that magic injection to the context object alone. I feel that the difference between |
Closing as defunct. See #783 for the json changes. |
What was wrong?
We currently allow for functions that are pure with regard to contract storage, but the limited scope of these functions is not reflected in the Json ABI.
How was it fixed?
To-Do