同一个名字收比特币也收以太坊:ERC-2304 的币种编号与原生二进制编码 图 1
同一个名字收比特币也收以太坊:ERC-2304 的币种编号与原生二进制编码 · 图 1

同一个名字收比特币也收以太坊:ERC-2304 的币种编号与原生二进制编码

“我把 BTC 收款地址也挂在同一个名字上行不行”——多链钱包向名字服务提的就是这个需求。ERC-2304 给出的办法是给解析器加一个新的访问函数,用一个币种编号区分要哪条链的地址。它的文件头记录创建于 2019 年 9 月 9 日,仓库记录状态为 Stagnant,声明依赖 ERC-137。

查询与事件:函数签名与零长度约定

新函数写作 addr(bytes32 node, uint coinType),外部只读调用,返回一段字节,规范给出的接口标识是 0xf1cb7e06。规则是:必须返回该节点在该币种编号下的币地址,如果这个编号在这个节点上根本没有记录,必须返回零长度字符串。注意这里和地址类记录返回零地址不是一回事——返回的是空字节串,工具拿它去当地址用就出错了。

配套的写函数是推荐形式的 setAddr(bytes32 node, uint coinType, bytes calldata addr),新增或替换该币种下的地址。事件为 AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress),规范要求每次改动都必须发出。

同一个名字收比特币也收以太坊:ERC-2304 的币种编号与原生二进制编码 图 2
同一个名字收比特币也收以太坊:ERC-2304 的币种编号与原生二进制编码 · 图 2

币种编号不是随便起的名

coinType 取自 SLIP-44 的币种索引,不是本提案自定的表。规范列了几条常见链的对应关系:比特币是 0、莱特币 2、狗狗币 3、以太坊 60、以太坊经典 61、比特币现金 145、币安 714。这意味着“同一个币种编号在不同工具里应该指同一条链”是这份设计的公共前提,客户端不应该自己发明编号。

四类编码,坑在这里

通用原则是用地址的原生二进制表示,并且不带文本形式里那层校验和。规范给了几类具体编码。

第一类是带版本字节的 base58check 地址:解出来首字节是版本,后面跟二十字节的哈希,而规范的存储形式不是这两段拼起来,而是把它转成对应的脚本形式——普通公钥哈希地址存成二十五字节,脚本哈希地址存成二十三字节。第二类的SegWit 地址走 bech32 编码,规范特意转引了 BIP173 里那段警告:见证版本 n 存成对应操作码时,零是一个字节值,一到十六是另一段字节区间,转错了地址要么花不出去要么不安全。第三类是以太坊风格地址:去掉前缀做十六进制解码,存成二十字节;校验和按 ERC-55 及其扩展规则处理,规范明确要求必须检查文本地址的校验和,校验和无效且不是全大写或全小写的地址必须以错误拒绝,反向从二进制生成文本时必须带校验和。第四类是给没有地址形式的链定的形式,例如 XRP 的两种地址形式各自带版本字节,X 形式还多带一个标签。

把这几条串起来看,这份规范的真正内容不在“能不能存”,而在“存进去的字节到底长什么样”。同一个文本地址,如果客户端按 base58 解码直接存原始二十一段、而不是按脚本形式存二十一段或二十五段,读回来时校验和验证就会对不上——这类差异不会以报错的方式通知你。

兼容:一个名字两个入口必须一致

规范写了两条硬性要求。如果解析器同时支持旧版的地址查询接口,那么旧函数返回值必须始终等于新函数在币种编号 60 上的结果(以太坊就是 60);任何导致旧事件触发的事,都必须同时触发本提案的事件、币种编号写作 60,反之亦然。参考实现正好体现了这一点:写入时先广播新事件,若币种编号是以太坊再广播一次旧事件。

使用者该核对什么

对普通用户,这套机制带来的实际变化是:同一个名字在不同钱包里可能读出完全不同的字节串,一个是给 Ethereum 风格地址的,一个是给比特币脚本形式的。读到一个陌生编码时,比“它看着像地址”更可靠的做法是按币种编号回到规范表核对编码方式,以及注意空字节串代表没记录而不是地址为零。

对做多链收款的产品,还有两件事值得核对:解析器是否申报了 0xf1cb7e06 这个接口标识,以及它在旧事件与新事件之间是否做到了双向触发。任何一边漏掉,都会导致部分钱包读得到、部分读不到,而用户会把这看成“地址错了”。本文为机制说明,不构成任何投资建议。