声明不上链:ERC-1812 用 EIP-712 签名的链下可验证声明 图 1
声明不上链:ERC-1812 用 EIP-712 签名的链下可验证声明 · 图 1

声明不上链:ERC-1812 用 EIP-712 签名的链下可验证声明

把“这个地址通过了某机构身份核验”这类声明写到链上,听起来公开透明,但摘要开头就点破矛盾:ERC-735 和 ERC-780 这类链上声明方案确实适合某些必须在链上核验的场景,可对多数情况来说,把含个人身份信息的声明记录进区块链这样的不可篡改公共数据库,既危险、在某些司法辖区还违法,它举的例子是欧盟的个人信息保护规则。这份文档创建于 2019 年 3 月 3 日,仓库记录状态为 Stagnant,属于长期无人推进的归档方案,答案是把声明放到链下,把可验证性留在链上。

三字段最小结构

规范给声明定的形式是:发行人就某主体做出“主体是某物或拥有某属性及其值”的声明,并要求同一签名人对同一声明多次签名的结果应当确定。结构体只强制三个字段:subject 是声明所指主体的地址类型,validFromvalidTo 分别是以秒计的生效与失效时间戳,类型 uint256。类型名不强制,规范建议沿用 schema.org 发展出的分类词表,并给了例子——一个名为 Know 的声明类型就只含这三个字段。两个工程细节值得单记:其一,若声明设计为永不过期,validTo 填全 F 的那个最大值,这是规范明文给出的哨兵值写法;其二,链上验证合约校验时间用的是 validFrom 不早于当前区块时间戳且当前区块时间戳小于 validTo 这一对不等式检查——时间不达标直接 require 失败,而不是返回一个可被忽略的标志。

声明不上链:ERC-1812 用 EIP-712 签名的链下可验证声明 图 2
声明不上链:ERC-1812 用 EIP-712 签名的链下可验证声明 · 图 2

验证合约:一个公开的 view 函数

格式的支柱是一个验证合约,规范要求它带一个公开的 verify 视图函数,让其他合约、状态通道和链下库都能用同一入口核验。声明本身还有一条确定性要求:同一签名人对同一声明多次签名,产出应当相同——这让“出示一份签过名的旧声明”成为常态操作,而不需要每次出示都重新找发行人。规范示例里的实现把流程摊开了:把 EIP-191 前缀两字节(0x19 与版本号 0x01)、域分隔符和声明哈希拼起来取 keccak256 摘要——这正是 EIP-712 结构化签名摘要的构造——随后在时间检查之后调用 ecrecover 从 v、r、s 恢复签名者地址,调用方拿恢复出的地址与可信发行名单比对即完成验证。发行与出示因此完全对称:发行人签一次名,持有者可以反复向不同验证方出示同一份声明,声明数据从未经上过链。

为什么选 EIP-712 而不是现成标准

动机部分把对照对象摆得很清楚。W3C 的可验证声明数据模型与 uPort 的验证消息规范都是链下方案,但分别建立在 JSON-LD 和 JWT 上,和以太坊生态的集成都不轻松;EIP-712 恰好同时给了两样东西——一种可被 Solidity 直接在链上解析的结构化摘要格式,和一套已有的钱包签名调用。对钱包来说签发这类声明不需要新能力;对链上合约来说验证不需要外部数据源。声明因此能同时活在两条世界线里:链下存着、链上可验。

这份归档标准把“哪些内容不该上链”说在了前面。对照今天的产品,检验链下凭证的清单可以直接从它的结构里抄:凭证有没有明确的时间对,验证入口是不是公开可读的 view 函数,摘要构造是否走 EIP-712 这类被钱包普遍支持的标准(自定义拼接格式最容易在换钱包时失效),以及出示时对方能否离线重放核验。反过来的教训同样成立:任何把姓名、证件号、住址直接写进交易输出的“身份上链”方案,应当先问一句签发者有没有考虑过 ERC-1812 动机部分写的那个违法问题——删除权与不可篡改,在协议层面是正面冲突。本文为机制说明,不构成任何投资建议。