Alert Source Discuss
🚧 Stagnant Standards Track: ERC

ERC-1066: 状态码

Authors Brooklyn Zelenka (@expede), Tom Carchrae (@carchrae), Gleb Naumenko (@naumenkogs)
Created 2018-05-05
Discussion Link https://ethereum-magicians.org/t/erc-1066-ethereum-status-codes-esc/

简单总结

广泛适用于智能合约的状态码。

摘要

本标准概述了一组类似于 HTTP 状态的通用状态码。这提供了一组共享的信号,允许智能合约自主地对情况做出反应,向用户公开本地化的错误消息等等。

目前最先进的做法是,除了明确的成功之外,对任何事情都进行 revert(即:需要人为干预),或者返回一个低上下文的 truefalse。状态码与使用原因进行 revert 在相似但正交,但目标是自动化、调试和最终用户反馈(包括翻译)。它们与 revert 和使用原因进行 revert 完全兼容。

与 HTTP 的情况一样,拥有一组已知的标准代码对开发人员有很多好处。它们消除了为每个合约开发自己的方案的需求带来的摩擦,使合约间的自动化更容易,并且更容易广泛地理解你的请求产生了哪些有限状态。重要的是,它使得区分预期错误状态、需要停止执行的真正异常情况、正常状态转换和各种成功案例变得更加容易。

动机

语义密度

HTTP 状态码被广泛用于此目的。BEAM 语言使用原子和带标签的元组来表示几乎相同的信息。两者都为程序员(例如调试)和需要决定下一步做什么的程序提供了大量信息。

状态码传递的信息比 布尔值 丰富得多,并且能够像任意字符串一样自主地做出反应。

用户体验 (UX)

最终用户几乎没有得到任何反馈,并且没有翻译层。

由于 ERC1066 状态码是有限的并且是预先知道的,我们可以利用 ERC-1444 来提供全局的、人类可读的状态消息集。这些也可以翻译成任何语言,不同级别的技术细节,添加为 revert 消息,natspecs 等等。

状态码传递的信息比布尔值丰富得多,并且能够像任意字符串一样自主地做出反应。

开发者体验 (DX)

开发者目前几乎没有智能合约公开的上下文。

在撰写本文时,除了逐步执行 EVM 执行并直接检查内存转储之外,很难理解智能合约执行期间发生了什么。通过返回更多上下文,开发人员可以编写分解良好的测试,并断言返回某些代码,以表达智能合约到达的位置。这包括作为裸值的状态码、eventrevert

拥有一组固定的代码也使得编写通用辅助函数以对某些信号做出常见反应成为可能。这可以存在于链下或链上的库中,从而降低了构建智能合约的开销,并通过受信任的共享组件帮助提高代码质量。

我们还在 交易中 发现了对此的需求,并且没有理由 EVM 本身不能使用这些状态码。

智能合约自治

智能合约对请求结果的了解很少,除了通过/失败;它们可以通过更多上下文变得更智能。

智能合约很大程度上是为了实现自治。虽然每个合约都可以定义一个特定的接口,但拥有一组通用的语义代码可以帮助开发人员编写能够对各种情况做出适当反应的代码。

虽然显然相关,但状态码是对带有原因的 revert 的补充。状态码不限于回滚交易,并且可能表示已知的错误状态而无需停止执行。它们还可以表示链下条件,提供一个字符串来 revert,发出时间延迟信号等等。

所有这些使得合约可以共享状态转换、结果和内部更改的通用词汇表,而无需深入理解自定义状态枚举或协作合约的内部业务逻辑。

规范

格式

代码可以单独返回,也可以作为多个返回值的第一个值返回。

// 仅状态

function isInt(uint num) public pure returns (byte status) {
    return hex"01";
}

// 状态和值

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);
    }
}

代码表

代码可以很好地分解成一个 16x16 的矩阵,表示为一个 2 位十六进制数。高位表示代码的种类或“类别”,低位包含状态或“原因”。为了便于解释和布局,我们在下面将它们表示为每个范围的单独表格。

注意:未指定的代码_不能_随意使用,而是开放供进一步指定。

0x0* 通用

通用代码。这些代码兼作裸“原因”,因为 0x01 == 1

代码 描述
0x00 失败
0x01 成功
0x02 等待他人操作
0x03 已接受
0x04 下限或不足
0x05 请求接收方操作
0x06 上限
0x07 [保留]
0x08 重复、不必要或不适用
0x09 [保留]
0x0A [保留]
0x0B [保留]
0x0C [保留]
0x0D [保留]
0x0E [保留]
0x0F 信息性或元数据

0x1* 权限与控制

也用于常见的状态机操作(例如“红绿灯”操作)。

代码 描述
0x10 不允许或停止
0x11 允许或继续
0x12 等待他人的许可
0x13 已请求许可
0x14 太开放/不安全
0x15 需要你的许可或继续请求
0x16 已撤销或已禁止
0x17 [保留]
0x18 不适用于当前状态
0x19 [保留]
0x1A [保留]
0x1B [保留]
0x1C [保留]
0x1D [保留]
0x1E [保留]
0x1F 权限详细信息或控制条件

0x2* 查找、不等式与范围

此范围广泛用于查找和匹配。数据查找和订单匹配是两个常见的用例。

代码 描述
0x20 未找到、不相等或超出范围
0x21 已找到、相等或在范围内
0x22 等待匹配
0x23 已发送匹配请求
0x24 低于范围或下溢
0x25 匹配请求
0x26 高于范围或溢出
0x27 [保留]
0x28 重复、冲突或碰撞
0x29 [保留]
0x2A [保留]
0x2B [保留]
0x2C [保留]
0x2D [保留]
0x2E [保留]
0x2F 匹配元数据或信息

0x3* 协商与治理

协商,以及非常广泛的此类交易流程。请注意,“另一方”可能不止一个参与者(不一定是发送方)。

代码 描述
0x30 发送方不同意或否决
0x31 发送方同意或赞成
0x32 等待批准
0x33 已发送报价或已投票
0x34 未达到法定人数
0x35 请求接收方批准
0x36 已达到报价或投票限制
0x37 [保留]
0x38 已经投票
0x39 [保留]
0x3A [保留]
0x3B [保留]
0x3C [保留]
0x3D [保留]
0x3E [保留]
0x3F 协商规则或参与信息

0x4* 可用性与时间

服务或操作可用性。

代码 描述
0x40 不可用
0x41 可用
0x42 已暂停
0x43 已加入队列
0x44 尚未可用
0x45 等待你的可用性
0x46 已过期
0x47 [保留]
0x48 已完成
0x49 [保留]
0x4A [保留]
0x4B [保留]
0x4C [保留]
0x4D [保留]
0x4E [保留]
0x4F 可用性规则或信息(例如,自…以来或直到…的时间)

0x5* 代币、资金与金融

特殊代币和金融概念。许多相关概念包含在其他范围中。

代码 描述
0x50 转账失败
0x51 转账成功
0x52 等待他人付款
0x53 扣留或托管
0x54 资金不足
0x55 已请求资金
0x56 超过转账量
0x57 [保留]
0x58 不需要资金
0x59 [保留]
0x5A [保留]
0x5B [保留]
0x5C [保留]
0x5D [保留]
0x5E [保留]
0x5F 代币或金融信息

0x6* 待定

目前未指定。(完整范围保留)

0x7* 待定

目前未指定。(完整范围保留)

0x8* 待定

目前未指定。(完整范围保留)

0x9* 待定

目前未指定。(完整范围保留)

0xA* 应用程序特定代码

合约可能具有它们需要发出信号的特殊状态。本提案仅概述了最广泛的含义,但实现者可能对每个状态码都有非常具体的含义,只要它们与更广泛的定义一致。

代码 描述
0xA0 应用程序特定失败
0xA1 应用程序特定成功
0xA2 应用程序特定等待他人操作
0xA3 应用程序特定接受
0xA4 应用程序特定低于条件
0xA5 应用程序特定请求接收方操作
0xA6 应用程序特定过期或限制
0xA7 [保留]
0xA8 应用程序特定不适用条件
0xA9 [保留]
0xAA [保留]
0xAB [保留]
0xAC [保留]
0xAD [保留]
0xAE [保留]
0xAF 应用程序特定元数据或信息

0xB* 待定

目前未指定。(完整范围保留)

0xC* 待定

目前未指定。(完整范围保留)

0xD* 待定

目前未指定。(完整范围保留)

0xE* 加密、身份与证明

围绕签名、密码学、签名和应用程序级别身份验证的操作。

元代码 0xEF 通常用于发出描述所用算法或过程的有效负载信号。

代码 描述
0xE0 解密失败
0xE1 解密成功
0xE2 等待其他签名或密钥
0xE3 已签名
0xE4 未签名或不受信任
0xE5 需要签名
0xE6 已知被泄露
0xE7 [保留]
0xE8 已签名或未加密
0xE9 [保留]
0xEA [保留]
0xEB [保留]
0xEC [保留]
0xED [保留]
0xEE [保留]
0xEF 密码学、ID 或证明元数据

0xF* 链下

用于链下操作。与 0x0*: 通用 范围非常相似,0xF* 非常通用,并且几乎不修改原因。

除其他事项外,元代码 0xFF 可用于描述链下过程是什么。

代码 描述
0xF0 链下失败
0xF1 链下成功
0xF2 等待链下过程
0xF3 链下过程已启动
0xF4 链下服务无法访问
0xF5 需要链下操作
0xF6 链下过期或达到限制
0xF7 [保留]
0xF8 重复的链下请求
0xF9 [保留]
0xFA [保留]
0xFB [保留]
0xFC [保留]
0xFD [保留]
0xFE [保留]
0xFF 链下信息或元数据

作为表格

  0x0* 通用 0x1* 权限与控制 0x2* 查找、不等式与范围 0x3* 协商与治理 0x4* 可用性与时间 0x5* 代币,资金与金融 0x6* 待定 0x7* 待定 0x8* 待定 0x9* 待定 0xA* 应用程序特定代码 0xB* 待定 0xC* 待定 0xD* 待定 0xE* 加密,身份与证明 0xF* 链下
0x*0 0x00 失败 0x10 不允许或停止 0x20 未找到,不相等或超出范围 0x30 发送方不同意或否决 0x40 不可用 0x50 转账失败 0x60 [保留] 0x70 [保留] 0x80 [保留] 0x90 [保留] 0xA0 应用程序特定失败 0xB0 [保留] 0xC0 [保留] 0xD0 [保留] 0xE0 解密失败 0xF0 链下失败
0x*1 0x01 成功 0x11 允许或继续 0x21 已找到,相等或在范围内 0x31 发送方同意或赞成 0x41 可用 0x51 转账成功 0x61 [保留] 0x71 [保留] 0x81 [保留] 0x91 [保留] 0xA1 应用程序特定成功 0xB1 [保留] 0xC1 [保留] 0xD1 [保留] 0xE1 解密成功 0xF1 链下成功
0x*2 0x02 等待他人操作 0x12 等待他人的许可 0x22 等待匹配 0x32 等待批准 0x42 已暂停 0x52 等待他人付款 0x62 [保留] 0x72 [保留] 0x82 [保留] 0x92 [保留] 0xA2 应用程序特定等待他人操作 0xB2 [保留] 0xC2 [保留] 0xD2 [保留] 0xE2 等待其他签名或密钥 0xF2 等待链下过程
0x*3 0x03 已接受 0x13 已请求许可 0x23 已发送匹配请求 0x33 已发送报价或已投票 0x43 已加入队列 0x53 扣留或托管 0x63 [保留] 0x73 [保留] 0x83 [保留] 0x93 [保留] 0xA3 应用程序特定接受 0xB3 [保留] 0xC3 [保留] 0xD3 [保留] 0xE3 已签名 0xF3 链下过程已启动
0x*4 0x04 下限或不足 0x14 太开放/不安全 0x24 低于范围或下溢 0x34 未达到法定人数 0x44 尚未可用 0x54 资金不足 0x64 [保留] 0x74 [保留] 0x84 [保留] 0x94 [保留] 0xA4 应用程序特定低于条件 0xB4 [保留] 0xC4 [保留] 0xD4 [保留] 0xE4 未签名或不受信任 0xF4 链下服务无法访问
0x*5 0x05 请求接收方操作 0x15 需要你的许可或继续请求 0x25 匹配请求 0x35 请求接收方批准 0x45 等待你的可用性 0x55 已请求资金 0x65 [保留] 0x75 [保留] 0x85 [保留] 0x95 [保留] 0xA5 应用程序特定请求接收方操作 0xB5 [保留] 0xC5 [保留] 0xD5 [保留] 0xE5 需要签名 0xF5 需要链下操作
0x*6 0x06 上限 0x16 已撤销或已禁止 0x26 高于范围或溢出 0x36 已达到报价或投票限制 0x46 已过期 0x56 超过转账量 0x66 [保留] 0x76 [保留] 0x86 [保留] 0x96 [保留] 0xA6 应用程序特定过期或限制 0xB6 [保留] 0xC6 [保留] 0xD6 [保留] 0xE6 已知被泄露 0xF6 链下过期或达到限制
0x*7 0x07 [保留] 0x17 [保留] 0x27 [保留] 0x37 [保留] 0x47 [保留] 0x57 [保留] 0x67 [保留] 0x77 [保留] 0x87 [保留] 0x97 [保留] 0xA7 [保留] 0xB7 [保留] 0xC7 [保留] 0xD7 [保留] 0xE7 [保留] 0xF7 [保留]
0x*8 0x08 重复、不必要或不适用 0x18 不适用于当前状态 0x28 重复,冲突或碰撞 0x38 已经投票 0x48 已完成 0x58 不需要资金 0x68 [保留] 0x78 [保留] 0x88 [保留] 0x98 [保留] 0xA8 应用程序特定不适用条件 0xB8 [保留] 0xC8 [保留] 0xD8 [保留] 0xE8 已签名或未加密 0xF8 重复的链下请求
0x*9 0x09 [保留] 0x19 [保留] 0x29 [保留] 0x39 [保留] 0x49 [保留] 0x59 [保留] 0x69 [保留] 0x79 [保留] 0x89 [保留] 0x99 [保留] 0xA9 [保留] 0xB9 [保留] 0xC9 [保留] 0xD9 [保留] 0xE9 [保留] 0xF9 [保留]
0x*A 0x0A [保留] 0x1A [保留] 0x2A [保留] 0x3A [保留] 0x4A [保留] 0x5A [保留] 0x6A [保留] 0x7A [保留] 0x8A [保留] 0x9A [保留] 0xAA [保留] 0xBA [保留] 0xCA [保留] 0xDA [保留] 0xEA [保留] 0xFA [保留]
0x*B 0x0B [保留] 0x1B [保留] 0x2B [保留] 0x3B [保留] 0x4B [保留] 0x5B [保留] 0x6B [保留] 0x7B [保留] 0x8B [保留] 0x9B [保留] 0xAB [保留] 0xBB [保留] 0xCB [保留] 0xDB [保留] 0xEB [保留] 0xFB [保留]
0x*C 0x0C [保留] 0x1C [保留] 0x2C [保留] 0x3C [保留] 0x4C [保留] 0x5C [保留] 0x6C [保留] 0x7C [保留] 0x8C [保留] 0x9C [保留] 0xAC [保留] 0xBC [保留] 0xCC [保留] 0xDC [保留] 0xEC [保留] 0xFC [保留]
0x*D 0x0D [保留] 0x1D [保留] 0x2D [保留] 0x3D [保留] 0x4D [保留] 0x5D [保留] 0x6D [保留] 0x7D [保留] 0x8D [保留] 0x9D [保留] 0xAD [保留] 0xBD [保留] 0xCD [保留] 0xDD [保留] 0xED [保留] 0xFD [保留]
0x*E 0x0E [保留] 0x1E [保留] 0x2E [保留] 0x3E [保留] 0x4E [保留] 0x5E [保留] 0x6E [保留] 0x7E [保留] 0x8E [保留] 0x9E [保留] 0xAE [保留] 0xBE [保留] 0xCE [保留] 0xDE [保留] 0xEE [保留] 0xFE [保留]
0x*F 0x0F Informational or Metadata 0x1F Permission Details or Control Conditions 0x2F Matching Meta or Info 0x3F Negotiation Rules or Participation Info 0x4F Availability Rules or Info (ex. time since or until) 0x5F Token or Financial Information 0x6F [保留] 0x7F [保留] 0x8F [保留] 0x9F [保留] 0xAF App-Specific Meta or Info 0xBF [保留] 0xCF [保留] 0xDF [保留] 0xEF Cryptography, ID, or Proof Metadata 0xFF Off-Chain Info or Meta

示例函数更改

uint256 private startTime;
mapping(address => uint) private counters;

// 之前
function increase() public returns (bool _available) {
    if (now < startTime && counters[msg.sender] == 0) {
        return false;
    };

    counters[msg.sender] += 1;
    return true;
}

// 之后
function increase() public returns (byte _status) {
    if (now < start) { return hex"44"; } // 尚未提供
    if (counters[msg.sender] == 0) { return hex"10"; } // 未经授权

    counters[msg.sender] += 1;
    return hex"01"; // 成功
}

示例序列图

0x03 = 等待中
0x31 = 另一方(即:不是你)同意
0x41 = 可用
0x44 = 尚未可用


                          交易所


AwesomeCoin                 DEX                     交易机器人
     +                       +                          +
     |                       |       buy(AwesomeCoin)   |
     |                       | <------------------------+
     |         buy()         |                          |
     | <---------------------+                          |
     |                       |                          |
     |     状态 [0x44]     |                          |
     +---------------------> |       状态 [0x44]      |
     |                       +------------------------> |
     |                       |                          |
     |                       |        isDoneYet()       |
     |                       | <------------------------+
     |                       |                          |
     |                       |       状态 [0x44]      |
     |                       +------------------------> |
     |                       |                          |
     |                       |                          |
     |     状态 [0x41]     |                          |
     +---------------------> |                          |
     |                       |                          |
     |       buy()           |                          |
     | <---------------------+                          |
     |                       |                          |
     |                       |                          |
     |     状态 [0x31]     |                          |
     +---------------------> |      状态 [0x31]       |
     |                       +------------------------> |
     |                       |                          |
     |                       |                          |
     |                       |                          |
     |                       |                          |
     +                       +                          +
0x01 = 通用成功
0x10 = 禁止
0x11 = 允许

                                              代币验证


           买方                  受监管代币            代币验证器               ID Checker          支出限制器
             +                          +                         +                         +                   +
             |        buy()             |                         |                         |                   |
             +------------------------> |          check()        |                         |                   |
             |                          +-----------------------> |          check()        |                   |
             |                          |                         +-----------------------> |                   |
             |                          |                         |                         |                   |
             |                          |                         |         状态 [0x10]   |                   |
             |                          |       状态 [0x10]     | <-----------------------+                   |
             |        revert()          | <-----------------------+                         |                   |
             | <------------------------+                         |                         |                   |
             |                          |                         |                         |                   |
+---------------------------+           |                         |                         |                   |
|                           |           |                         |                         |                   |
| 使用提供程序更新 ID          |           |                         |                         |                   |
|                           |           |                         |                         |                   |
+---------------------------+           |                         |                         |                   |
             |                          |                         |                         |                   |
             |         buy()            |                         |                         |                   |
             +------------------------> |        check()          |                         |                   |
             |                          +-----------------------> |         check()         |                   |
             |                          |                         +-----------------------> |                   |
             |                          |                         |                         |                   |
             |                          |                         |       状态 [0x11]     |                   |
             |                          |                         | <-----------------------+                   |
             |                          |                         |                         |                   |
             |                          |                         |                         |   check()         |
             |                          |                         +-------------------------------------------> |
             |                          |                         |                         |                   |
             |                          |                         |                         |  状态 [0x11]    |
             |                          |       状态将多个 code 打包到一个 `bytes32` 中,理论上很美好,但会带来额外的挑战。未使用的空间可能被解释为 `0x00 Failure`,你只能有效地一次打包四个 code,并且确保 code 组合有意义存在挑战。强制将四个 code 打包成一种表示形式,会鼓励返回多个状态 code,但这通常比严格意义上需要的更多信息。这可能导致自相矛盾的结果(例如 `0x00` 和 `0x01` 一起出现),或者将更多的资源分配给解释 256<sup>4</sup>(43 亿)种排列组合。

### 多重返回

虽然在某些情况下,打包状态 code 的字节数组可能是有意义的,但最简单、最具前向兼容性的传输方法是作为多重返回的第一个值。

熟悉也是一个驱动因素。一致的位置和编码共同遵循了最小惊讶原则。它既可以看作是 HTTP 类比中的“头部”,也可以看作是 BEAM 标记元组中的“标签”。

### 人工可读

开发者不应该被要求记住 256 个 code。然而,它们可以很好地分解成一个表格。通过将表格组织成类别和原因,可以降低认知负荷。`0x10` 和 `0x11` 属于同一类别,`0x04` 与 `0x24` 有共同的原因。

虽然这个仓库包含了辅助枚举,但我们发现直接使用十六进制值非常自然。例如,状态 code `0x10` 就像 HTTP 401 一样令人感到舒服。

#### 本地化

这个规范中一个常见的需求是 code 的人工可读翻译。这已被移至其自身的提案:[ERC-1444](/docs/eips/EIPS/eip-1444/),主要是因为希望保持两个规范的重点。

### 扩展性

`0xA` 类别是为应用程序特定的状态保留的。如果 256 个 code 不够用,则可以将 `bytes1` 嵌入到更大的字节数组中。

### EVM Codes

EVM 还在交易中返回一个状态 code;具体来说是 `0x00` 和 `0x01`。该提案既符合这两个 code 的含义,也可以稍后在 EVM 层面使用。

### 空白空间

就像 HTTP 状态 code 有大量未使用的范围一样,本提案中也有完全空白的部分。目的是不预先强制使用一套完整的 code,并允许用户随着时间的推移建议这些空间的使用方式。

### 超越错误

本规范旨在远不止是一组常见的错误。一个设计目标是支持更轻松的合约间通信、基于状态 code 构建的协议以及跨链流程。这些情况中的许多包括预期类型的异常状态(与真正的错误相反)、中性状态、时间逻辑和各种成功。

正如 HTTP 200 与 HTTP 201 的含义不同一样,ERC-1066 状态 code 可以在合约之间传递信息,而不仅仅是传递通过或失败。它们可以被认为是图中智能合约作为节点的边。

### 完全可 `revert`

_本规范与带有原因的 `revert` 完全兼容,并且不打算以任何方式取代它。_ 通过使用通用 code 进行 revert,开发人员可以从一组已知的错误状态中确定哪里出了问题。

此外,通过结合利用 ERC-1066 和翻译表(例如 ERC-1444),开发人员和最终用户都可以收到他们选择的语言和措辞的全自动人工可读错误消息。

### 半字节顺序

半字节顺序对机器没有影响,纯粹是助记符。最初,这个设计是相反的顺序,但为了方便性而进行了更改。由于它与 HTTP 的方案不同,因此最初可能会感到奇怪,但在使用几个小时后会变得非常自然。

#### 简短形式

通用的是 `0x0*`,通用 code 与其整数表示形式一致

```solidity
hex"1" == hex"01" == 1 // with casting

合约类别

许多应用程序将始终是同一类别的一部分。例如,验证通常在 0x10 范围内。

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;
    }
}

助手

以上也意味着使用应用程序特定的枚举会稍微容易一些,并且还可以节省 gas(所需的操作更少)。

enum Sleep {
    Awake,
    Asleep,
    BedOccupied,
    WindingDown
}

// 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
}

实施

参考案例和辅助库(Solidity 和 JS)可以在以下位置找到:

版权

通过 CC0 放弃版权和相关权利。

Citation

Please cite this document as:

Brooklyn Zelenka (@expede), Tom Carchrae (@carchrae), Gleb Naumenko (@naumenkogs), "ERC-1066: 状态码 [DRAFT]," Ethereum Improvement Proposals, no. 1066, May 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1066.