diff --git a/EIPS/eip-1066.md b/EIPS/eip-1066.md new file mode 100644 index 0000000000000..a4e68e858088f --- /dev/null +++ b/EIPS/eip-1066.md @@ -0,0 +1,585 @@ +--- +eip: 1066 +title: Status Codes +author: Brooklyn Zelenka (@expede), Tom Carchrae (@carchrae), Gleb Naumenko (@naumenkogs) +discussions-to: https://ethereum-magicians.org/t/erc-1066-ethereum-status-codes-esc/ +status: Draft +type: Standards Track +category: ERC +created: 2018-05-05 +--- + +## Simple Summary + +Broadly applicable status codes for Ethereum smart contracts. + +## Abstract + +This standard outlines a common set of Ethereum status codes (ESC) in the same +vein as HTTP statuses. This provides a shared set of signals to allow smart contracts +to react to situations autonomously, expose localized error messages to users, and so on. + +The current state of the art is to either `revert` and require human intervention, +or return a Boolean pass/fail status. Status codes are similar-but-orthogonal +to `revert`ing with a reason, but aimed at automation and translation. + +As is the case with HTTP, having a standard set of known codes has many benefits for developers. +They remove friction from needing to develop your own schemes for every contract, +makes inter-contract automation easier, and makes it easier to broadly understand +which of the finite states your request produced. Importantly, it makes it much easier +to distinguish between expected errors states, and truly exceptional conditions +that require halting execution. + +## Motivation + +### Autonomy + +Smart contracts are largely intended to be autonomous. While each contract may +define a specific interface, having a common set of semantic codes can help +developers write code that can react appropriately to various situations. + +### Semantic Density + +HTTP status codes are widely used for this purpose. BEAM languages use atoms +and tagged tuples to signify much the same information. Both provide a lot of +information both to the programmer (debugging for instance), and to the program +that needs to decide what to do next. + +ESCs convey a much richer set of information than Booleans, +and are able to be reacted to autonomously unlike arbitrary strings. + +### User Feedback + +Since status codes are finite and known in advance, we can provide global, +human-readable sets of status messages. These may also be translated into any language, +differing levels of technical detail, added as `revert` messages, natspecs, and so on. + +We also see a desire for this [in transactions](http://eips.ethereum.org/EIPS/eip-658), +and there's no reason that ESCs couldn't be used by the EVM itself. + +### More than Pass/Fail + +While clearly related, status codes are complementary to "revert with reason". +ESCs are not limited to rolling back the transaction, and may represent known error states +without halting execution. They may also represent off-chain conditions, +supply a string to revert, signal time delays, and more. + +## Specification + +### Format + +Codes are returned as the first value of potentially multiple return values. + +```solidity +// Status only + +function isInt(uint num) public pure returns (byte status) { + return hex"01"; +} + +// Status and value + +uint8 private counter; + +function safeIncrement(uint8 interval) public returns (byte status, uint8 newCounter) { + uint8 updated = counter + interval; + + if (updated >= counter) { + counter = updated; + return (hex"01", updated); + } else { + return (hex"00", counter); + } +} +``` + +In the rare case that there a multiple codes required to express an idea, +they should be organized in asending order. + +### Code Table + +Codes break nicely into a 16x16 matrix, represented as a 2-digit hex number. +The high nibble represents the code's kind or "category", and the low nibble contains +the state or "reason". We present them below as separate tables per range for +explanitory and layout reasons. + +Unspecified codes are _not_ free for arbitrary use, but rather open for further specification. + +#### Generic + +General codes. These double as bare "reasons", since `0x01 == 1`. + +| Code | Description | +|-----------------|:------------------------| +| `0x00` | Failure | +| `0x01` | Success | +| `0x02` | Accepted / Started | +| `0x03` | Awaiting / Before | +| `0x04` | Action Required | +| `0x05` | Expired | +| `0x06` | | +| `0x07` | | +| `0x08` | | +| `0x09` | | +| `0x0A` | | +| `0x0B` | | +| `0x0C` | | +| `0x0D` | | +| `0x0E` | | +| `0x0F` | Meta or Info Only | + +#### Permission + +Related to permisson, authorization, approval, and so on. + +| Code | Description | +|-----------------|:-------------------------| +| `0x10` | Disallowed | +| `0x11` | Allowed | +| `0x12` | Requested Permission | +| `0x13` | Awaiting Permission | +| `0x14` | Awaiting Your Permission | +| `0x15` | No Longer Allowed | +| `0x16` | | +| `0x17` | | +| `0x18` | | +| `0x19` | | +| `0x1A` | | +| `0x1B` | | +| `0x1C` | | +| `0x1D` | | +| `0x1E` | | +| `0x1F` | Permission Meta or Info | + +#### Find, Match, &c + +This range is broadly intended for finding and matching. +Data lookups and order matching are two common use cases. + +| Code | Description | +|-----------------|:-------------------------| +| `0x20` | Not Found | +| `0x21` | Found | +| `0x22` | Match Request Sent | +| `0x23` | Awaiting Match | +| `0x24` | Match Request Received | +| `0x25` | Out of Range | +| `0x26` | | +| `0x27` | | +| `0x28` | | +| `0x29` | | +| `0x2A` | | +| `0x2B` | | +| `0x2C` | | +| `0x2D` | | +| `0x2E` | | +| `0x2F` | Matching Meta or Info | + +#### Negotiation, Terms, and Offers + +Negotiation, and very broadly the flow of such transactions. +Note that "other party" may be more than one actor (not nessesarily the sender). + +| Code | Description | +|-----------------|:----------------------------| +| `0x30` | Other Party Disagreed | +| `0x31` | Other Party Agreed | +| `0x32` | Sent Offer | +| `0x33` | Awaiting Their Ratification | +| `0x34` | Awaiting Your Ratification | +| `0x35` | Offer Expired | +| `0x36` | | +| `0x37` | | +| `0x38` | | +| `0x39` | | +| `0x3A` | | +| `0x3B` | | +| `0x3C` | | +| `0x3D` | | +| `0x3E` | | +| `0x3F` | Negotiation Meta or Info | + +#### Availability + +Service or action availability. + +| Code | Description | +|-----------------|:----------------------------| +| `0x40` | Unavailable | +| `0x41` | Available | +| `0x42` | You May Begin | +| `0x43` | Not Yet Available | +| `0x44` | Awaiting Your Availability | +| `0x45` | No Longer Available | +| `0x46` | | +| `0x47` | | +| `0x48` | | +| `0x49` | | +| `0x4A` | | +| `0x4B` | | +| `0x4C` | | +| `0x4D` | | +| `0x4E` | | +| `0x4F` | Availability Meta or Info | + +#### `0x5_` TBD + +Currently unspecified + +#### `0x6_` TBD + +Currently unspecified + +#### `0x7_` TBD + +Currently unspecified + +#### `0x8_` TBD + +Currently unspecified + +#### `0x9_` TBD + +Currently unspecified + +#### Application-Specific Codes + +Contracts may have special states that they need to signal. +This proposal only outlines the broadest meanings, but implementers may have very +specific meanings for each, as long as they are coherent with the broader definition. + +| Code | Description | +|-----------------|:--------------------------------| +| `0xA0` | App-Specific Failure | +| `0xA1` | App-Specific Success | +| `0xA2` | App-Specific Acceptance / Start | +| `0xA3` | App-Specific Awaiting / Before | +| `0xA4` | App-Specific Action Required | +| `0xA5` | App-Specific Expiry | +| `0xA6` | | +| `0xA7` | | +| `0xA8` | | +| `0xA9` | | +| `0xAA` | | +| `0xAB` | | +| `0xAC` | | +| `0xAD` | | +| `0xAE` | | +| `0xAF` | App-Specific Meta or Info | + +#### `0xB_` TBD + +Currently unspecified + +#### `0xC_` TBD + +Currently unspecified + +#### `0xD_` TBD + +Currently unspecified + +#### Cryptography and Authentication + +Actions around signatures, cryptography, signing, and application-level authentication. + +The meta code `0xEF` is often used to signal a payload descibing the algorithm +or process used. + +| Code | Description | +|-----------------|:----------------------------| +| `0xE0` | Decrypt Failure | +| `0xE1` | Decrypt Success | +| `0xE2` | Signed | +| `0xE3` | Their Signature Required | +| `0xE4` | Your Signature Required | +| `0xE5` | Auth Expired | +| `0xE6` | | +| `0xE7` | | +| `0xE8` | | +| `0xE9` | | +| `0xEA` | | +| `0xEB` | | +| `0xEC` | | +| `0xED` | | +| `0xEE` | | +| `0xEF` | Crypto Info or Meta | + +#### `0xF0` Off-Chain + +For off-chain actions. Much like th `0x0_: Generic` range, `0xF_` is very general, +and does little to modify the reason. + +Among other things, the meta code `0xFF` may be used to describe what the off-chain process is. + +| Code | Description | +|-----------------|:------------------------------| +| `0xF0` | Off-Chain Failure | +| `0xF1` | Off-Chain Success | +| `0xF2` | Off-Chain Process Stared | +| `0xF3` | Awaiting Off-Chain Completion | +| `0xF4` | Off-Chain Action Required | +| `0xF5` | Off-Chain Service Unavailable | +| `0xF6` | | +| `0xF7` | | +| `0xF8` | | +| `0xF9` | | +| `0xFA` | | +| `0xFB` | | +| `0xFC` | | +| `0xFD` | | +| `0xFE` | | +| `0xFF` | Off-Chain Info or Meta | + +### Example Function Change + +```solidity +uint256 private startTime; +mapping(address => uint) private counters; + +// Before +function increase() public returns (bool _available) { + if (now < startTime && counters[msg.sender] == 0) { + return false; + }; + + counters[msg.sender] += 1; + return true; +} + +// After +function increase() public returns (byte _status) { + if (now < start) { return hex"43"; } // Not yet available + if (counters[msg.sender] == 0) { return hex"10"; } // Not authorized + + counters[msg.sender] += 1; + return hex"01"; // Success +} +``` + +### Example Sequence Diagrams + +``` +0x03 = Waiting +0x31 = Other Party (ie: not you) Agreed +0x41 = Available +0x43 = Not Yet Available + + + Exchange + + +AwesomeCoin DEX TraderBot + + + + + | | buy(AwesomeCoin) | + | | <------------------------+ + | buy() | | + | <---------------------+ | + | | | + | Status [0x43] | | + +---------------------> | Status [0x43] | + | +------------------------> | + | | | + | | isDoneYet() | + | | <------------------------+ + | | | + | | Status [0x43] | + | +------------------------> | + | | | + | | | + | Status [0x41] | | + +---------------------> | | + | | | + | buy() | | + | <---------------------+ | + | | | + | | | + | Status [0x31] | | + +---------------------> | Status [0x31] | + | +------------------------> | + | | | + | | | + | | | + | | | + + + + +``` + + + +``` +0x01 = Generic Success +0x10 = Disallowed +0x11 = Allowed + + Token Validation + + + Buyer RegulatedToken TokenValidator IDChecker SpendLimiter + + + + + + + | buy() | | | | + +------------------------> | check() | | | + | +-----------------------> | check() | | + | | +-----------------------> | | + | | | | | + | | | Status [0x10] | | + | | Status [0x10] | <-----------------------+ | + | revert() | <-----------------------+ | | + | <------------------------+ | | | + | | | | | ++---------------------------+ | | | | +| | | | | | +| Updates ID with provider | | | | | +| | | | | | ++---------------------------+ | | | | + | | | | | + | buy() | | | | + +------------------------> | check() | | | + | +-----------------------> | check() | | + | | +-----------------------> | | + | | | | | + | | | Status [0x11] | | + | | | <-----------------------+ | + | | | | | + | | | | check() | + | | +-------------------------------------------> | + | | | | | + | | | | Status [0x11] | + | | Status [0x11] | <-------------------------------------------+ + | Status [0x01] | <-----------------------+ | | + | <------------------------+ | | | + | | | | | + | | | | | + | | | | | + + + + + + +``` + +## Rationale + +### Encoding + +ESCs are encoded as a `byte`. Hex values break nicely into high and low nibbles: +`category` and `reason`. For instance, `hex"01"` stands for general success +and `hex"00"` for general failure. + +`byte` is quite lightweight, and can be easily packed with multiple codes into +a `bytes32` (or similar) if desired. It is also easily interoperable with `uint8`, +cast from `enum`s, and so on. + +#### Alternatives + +Alternate schemes include `bytes32` and `uint8`. While these work reasonably +well, they have drawbacks. + +`uint8` feels even more similar to HTTP status codes, and enums don't require +as much casting. However does not break as evenly as a square table +(256 doesn't look as nice in base 10). + +Packing multiple codes into a single `bytes32` is nice in theory, but poses additional +challenges. Unused space may be interpeted as `0x00 Failure`, you can only efficiently +pack four codes at once, and there is a challenge in ensuring that code combinations +are sensible. Forcing four codes into a packed representation encourages multiple +status codes to be returned, which is often more information than strictly nessesary. +This can lead to paradoxical results (ex `0x00` and `0x01` together), +or greater resorces allocated to interpreting 2564 (4.3 billion) permutations. + +### Multiple Returns + +While there may be cases where packing a byte array of ESCs may make sense, the simplest, +most forwards-compatible method of transmission is as the first value of a multiple return. + +Familiarity is also a motivating factor. A consistent position and encoding together +follow the principle of least surprise. It is both viewable as a "header" in the HTTP analogy, +or like the "tag" in BEAM tagged tupples. + +### Human Readable + +Developers should not be required to memorize 256 codes. However, they break nicely into a table. +Cognitive load is lowered by organizing the table into categories and reasons. +`0x10` and `0x11` belong to the same category, and `0x04` shares a reason with `0x24` + +While this repository includes helper enums, we have found working directly in +the hex values to be quite natural. ESC `0x10` is just as comfortable as HTTP 401, for example. + +### Extensiblilty + +The `0xA` category is reserved for application-specific statuses. +In the case that 256 codes become insufficient, `bytes1` may be embedded in larger byte arrays. + +### EVM Codes + +The EVM also returns a status code in transactions; specifically `0x00` and `0x01`. +This proposal both matches the meanings of those two codes, and could later be used +at the EVM level. + +### Empty Space + +Much like how HTTP status codes have large unused ranges, there are totally empty +sections in this proposal. The intent is to not impose a complete set of codes up front, +and to allow users to suggest uses for these spaces as time progresses. + +### Nibble Order + +Nibble order makes no difference to the machine, and is purely mnemonic. +This design was originally in opposite order, but changed it for a few convenience factors. +Since it's a different scheme from HTTP, it may feel strange initially, +but becomes very natural after a couple hours of use. + +#### Short Forms + +Generic is `0x0_`, general codes are consistent with their integer representations + +```solidity +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. + +```solidity +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: + +```solidity +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 +} +``` + +## Implementation + +Reference cases and helper library can be found [here](https://github.com/Finhaven/EthereumStatusCodes) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).