-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
Status code proposal #1066
Status code proposal #1066
Conversation
83830cf
to
454e56c
Compare
EIPS/eip-1066.md
Outdated
--- | ||
eip: 1066 | ||
title: Status Codes | ||
author: Brooklyn Zelenka (@expede) <[email protected]>, Tom Carchrae (@carchrae) <[email protected]>, Gleb Naumenko (@naumenkogs) <[email protected]> |
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'm afraid the formatting doesn't presently support linking both emails and usernames - please pick 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.
Ah, okay. Will update!
EIPS/eip-1066.md
Outdated
|
||
### Code Table | ||
|
||
| X. Low Nibble | 0. Generic | 10. Permission | 20. Find/Match/&c | 30. Negotiation / Offers | 40. Availability | 50. | 60. | 70. | 80. | 90. | A0. | B0. | C0. | D0. | E0. Cryptography & Tokens | F0. Off Chain | |
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'd suggest reformatting this with one status code per line, with a divider (or separate table) between ranges.
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.
@Arachnid 🤔 I'm not entirely certain that I follow? The intent is to show that they break cleanly into two dimensions, and meanings should be understandable when you know the categories and reasons.
Is it because of the width of the table on GitHub? If so, I can just move the table to a website or similar.
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.
Once merged it'll show up on eips.ethereum.org either way. I'm just suggesting that it'd look clearer grouped by type, rather than trying to show it in a 2d matrix.
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 just noticed that you're using the low nybble for categories and the high nybble for codes in that category. That seems really weird - why not the other way around?
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.
At the end of the day it makes no difference to the machine, and is purely mnemonic. Totally happy to hear where the community lands on this one. This is just where our team ended up, after a few weeks of working with them.
You're probably feel some "weirdness" because it's a different scheme from HTTP. We had it the other way initially, but flipped it for a few reasons. Mostly it makes a handful of things somewhat easier. Below are the main ones. There's nothing earth shattering, but we've found it to be slightly easier, and very natural once you've used it for an hour or two.
Short Forms
Generic is 0x0_
, general codes are consistent with their integer representations
hex"1" == hex"01" == 1 // with casting
Contract Categories
Many applications will always be part of the same category. For instance, validation will generally be in the 0x10
range.
contract Whitelist {
mapping(address => bool) private whitelist;
uint256 private deadline;
byte constant private prefix = hex"10";
check(address _, address _user) returns (byte _status) {
if (now >= deadline) { return prefix | 5; }
if (whitelist[_user]) { return prefix | 1; }
return prefix;
}
}
Helpers
This above also means that working with app-specific enums is slightly easier:
enum Sleep {
Awake,
Asleep,
REM,
FallingAsleep
}
// From the helper library
function appCode(Sleep _state) returns (byte code) {
return byte(160 + _state); // 160 = 0xA0
}
// Versus
function appCode(Sleep _state) returns (byte code) {
return byte((16 * _state) + 10); // 10 = 0xA
}
Would love to hear thoughts from the community, though 👍🏻
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.
It would be worth adding that to the rationale section.
|
||
### Encoding | ||
|
||
ESCs are encoded as a `byte`. Hex values break nicely into high and low nibbles: |
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.
Given existing ABI encoding expands all values to word size (256 bits / 32 bytes), it might make sense to use a larger type for this, providing more flexibility.
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.
Hm, yes perhaps for transmission format. Will change 👍
However, we feel that the actual codes themselves should be limited to one byte
. 256 codes is plenty, and showing restraint gives us a number of advantages:
- 256 codes is already plenty to express many ideas, and to handle as return values
- Easy to decode 16x16 without a reference guide once you're familiar with them
- 32-bytes would be 2^256 ~ 1.158^77 codes, which largely defeats the purpose of a finite set
- You can pack multiple single
byte
s into abytes32
for cases where multiple codes are appropriate (though what to do about padding is another question)
Thoughts?
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.
Using a wider type would permit application-specific return codes without collisions, which seems useful to me.
I don't think people should be returning multiple status codes; that seems liable to complicate matters for little or no gain.
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.
Sorry for the late reply; it's been a bit of a crazy week. I really do appreciate you hashing out these details 🙏
I don't think people should be returning multiple status codes; that seems liable to complicate matters for little or no gain.
👍 I actually agree about the multiple status code. This is something that keeps coming up as an ask in local community conversations, but I'm not a fan. It's good to hear that I'm not the only one!
Using a wider type would permit application-specific return codes without collisions, which seems useful to me.
I'm still not sure if such a large space for application-specific codes would be useful or harmful. Perhaps if you could provide some use cases for such a large space. Below is some more detailed reasoning on why I believe that 2^8 codes is the right call.
I'm not totally convinced about the added space, especially since (if?) people will be choosing codes, not a random hash. A random hash would be inconsistent with a semantic code system, which in turn adds friction. People will tend towards things that are easy to remember, or have some other value. For example: 0xCAFE
and 0xBAD_
may get a lot of use.
Even with a random hash for say, the first two digits of four, I question why they need to live in the same namespace. A contract that forwards other codes really has that as part of it's contract (in the DbC sense), and needs to maintain them itself. Law of Demeter aside, an explosion of forwarded codes makes it very difficult to know what to expect, and thus limits the usefulness of such a system.
I totally get that bytes32
is one machine word, and padding a byte
with a bunch of 0
s to fill out the word can seem like a waste of space. The purpose of this design is to be constrained for developers, with the goal of making it easy to choose a code, for other contracts to be able to react autonomously, and to translate messages. These all require a manageable number of responses to be effective. With bytes32
, we're allocating a very small fraction of a percent to known codes, and the other [75 nines] would be unspecified. With 256 status known codes, if someone needs to use another scheme for their application, they're still free to. Adding 2^256 (2^32*8) codes to the same space seems to defeat the purpose.
If we later wanted to expand the code-space, bytes1
casts to bytes32
, so we have simple interoperability between the ESCv0 and the hypothetical ESCv1.
Phew, that ended up being a bit longer than intended 😅 Not trying to brow beat with walls of text; just trying to clarify the intent and design!
I really like this idea. My only concern is that, with the current state of Ethereum tooling, it may motivate people to use return codes instead of reverts, when the latter are much better suited to many types of failure. |
I like the idea as well, but always thought that the pattern of success booleans was a handicap due to the lack of a good trycatch mechanism in Solidity (and also that it's very hard to tell if your external call actually ran any code). The fact that this works at the application level is awesome, but imo a real solution is adding status codes like this to revert-with-reason. Then you get both machine and human readable errors just like the web. That said I'm generally down to use this pattern, since it does solve the primary issue of boolean returns. |
I always disliked the success booleans. It would be interesting if making an external call where no code was executed reverted and returned a 404-like mechanism. |
@shrugs - we were also really excited to see revert with reason get merged. but we feel that is complimentary to status codes. revert with reason is an improvement on the mechanics of returning the error, but doesn't attempt to standardise errors. there are cases where you don't want to reverts, but rather handle any error/condition gracefully. if you use revert, it is game over for that (which is fine in some cases) |
but both @expede and i agree that showing how to integrate (or extend) with revert with reason is something worthwhile. we had built out some of our examples to include it (stubbing, since revert with reason is only recently merged) |
Right, the status codes would be different from an exception code, but my opinion is that they should be baked into the language and VM, not an app-level feature. Languages have this error/exception pattern for two features; exceptions (ruby's My opinion is: I'm sad that this doesn't exist in the language/vm level, it's sort of a crazy oversight imo, but also this status code standard is the best solution in the short term and I totally support the ethos and implementation of it. |
@shrugs Yeah, 100% would like to see this type of functionality more baked in. It's something that we're exploring for a future EIP (though probably not in the try/catch style). VM and transaction format changes are much deeper, and we'd like to explore the consequences of such a change before making a proposal. Status codes at the application level are a very safe first step, and as @carchrae mentioned, totally orthogonal to VM changes. Status codes are for more than failure, and can represent states beyond just "that didn't work". There are many types of success, timing/sequence logic, and composing multiple contracts into a decision tree that use specific returns to switch control flow, much like how Erlang and Elixir work. |
@shrugs There's no VM support needed that isn't already there - both |
@Arachnid to expand on what @carchrae said about it being complementary to revert-with-reason:
💯 Fully agree. Codes and We're working to make these easily compatible. For instance, in the ESC helper library, we have |
@expede @carchrae I understand; I'm just concerned that people will be encouraged by this to use the anti-pattern of trying to manually undo things and return rather than reverting in cases where they need to abort an operation. Theres several categories of status codes here that would make more sense as a revert reason than as a return value. |
@Arachnid totally agree with everything. If i remember correctly this is why previous attempts to do this kind of thing failed. Well that and not supporting multiple return values. But like you point out - anything but reverting on errors is wrong, since it results in code that is prone to side effects, and should not be encouraged. Now that there is a revert instruction that takes arguments, having standard error codes seems like it could finally become a thing. |
this focus of this EIP isn't really about the use of revert vs return. it is more the focus on how we return meaningful information in a standardised way. we've been thinking about this for a few months, and really, your concern of forcing return instead of revert was something implied by returning a status ... but then revert with reason got merged! 🎉 🎈 🎆 so, now it is possible to revert and use an ESC too. |
we may need to change some of the language of the EIP to stress it doesn't force a return or revert choice, or add some explict revert examples with codes. hard to keep up sometimes with what you can/can't do. all great comments so far - we appreciate your feedback/input. |
5b8a012
to
8de015b
Compare
I'm happy to merge this as a draft if you add a |
1ce269c
to
188f83a
Compare
Hey @Arachnid, it's now updated with a |
This proposal outlines a common set of Ethereum status codes (ESC) in the same vein as HTTP statuses or BEAM tagged tuples. This is a shared set of signals to allow smart contracts to react to situations autonomously, expose localized error messages to users, and so on.
ERC-1066 is purely about convention, requires no changes to the EVM, and is usable today. Further investigation is being done into potentially adding these to the status field of transactions, but is out of scope of this EIP.
We have intentionally left portions of the status code chart blank. Rather than impose a complete set up front, we hope to consult to broader community on what may be the most useful and widely applicable codes.