CIP-37: 引入一种新的地址格式帮助用户区分 Conflux 和以太坊的地址

英文原文:https://github.com/Conflux-Chain/CIPs/blob/master/CIPs/cip-37.md

简要概括

本 CIP 引入了一种新的地址格式,帮助用户区分 Conflux 和以太坊的地址。

摘要

除了目前 Conflux 使用的包含类型前缀的十六进制地址,我们引入了一种新的 base32 编码地址(base32-encoded address)格式。这种新格式的地址直接由十六进制地址中衍生而来,包含一个容易辨认的前缀(如"cfx")和一个校验和。使用 base32 校验编码(base32check)能帮我们检测并避免输入错误,让我们简单分辨 Conflux 与其他链上的地址,减轻资产损失的风险。

新地址格式例如:cfx:aarc9abycue0hhzgyrr53m6cxedgccrmmyybjgh4xg.

目标

目前的 Conflux 和以太坊地址非常相似,很多情况下它们是可以互相兼容的,例如,一些以太坊上的地址(0x1开头地址)在 Conflux 上也是有效的,,Conflux地址在以太坊上有1/16的概率可行,(有资产丢失的可能)。这就引起了许多用户不小心将以太坊上的资产转移到一个 Conflux 地址中,产生丢失资产的情况,尤其是在进行跨链操作时。与以太坊不兼容的新的地址类型可以被 Metamask 这样的工具检测出来,从而避免将错误的交易提交至网络。

规范

(本章节部分内容参考自比特币现金 Bitcoin Cash 规范。)

本章节定义的转换是在以下两种地址类型之间的转换:

  • Conflux 十六进制地址:有效的20字节的类型前缀 Conflux 十六进制地址,如0x106d49f8505410eb4e671d51f7d96d2c87807b09。由公钥产生这种地址的方式在协议规范的公式(1)中有定义,这个地址的字符串可以选择性地使用大小写混合校验和地址编码EIP-55)。

  • Conflux base32 地址:网络前缀 Conflux base32 校验和地址。这种地址由网络前缀(network-prefix)、冒号(":")和 base32 编码负载组成。网络前缀表明该地址在哪个网络上有效,base32 编码负载(payload)表示地址的指向目标,包含一个校验和,如cfx:aarc9abycue0hhzgyrr53m6cxedgccrmmyybjgh4xg。同时,该地址中也可以选择在网络前缀和负载之间以 key.value 的形式包含一个键值对(key value pairs)列表,用冒号分开,如cfx:type.user:aarc9abycue0hhzgyrr53m6cxedgccrmmyybjgh4xg

编码

输入值:addr(20字节的 Conflux 十六进制地址),网络 ID network-id(4字节)

输出值:Conflux base32 地址

  1. 网络前缀(Network-prefix):
    match network-id:
        case 1029: "cfx"
        case 1:    "cfxtest"
        case n:    "net[n]"

有效网络前缀示例:"cfx""cfxtest""net17"

无效网络前缀示例:"bch""conflux""net1""net1029"

  1. 【可选】地址类型:
    match addr[0] & 0xf0
        case b00000000: "type.builtin"
        case b00010000: "type.user"
        case b10000000: "type.contract"

可以用"type.null"实现空地址(0x0000000000000000000000000000000000000000)。

  1. 版本字节:
    版本字节的最高位被保留,并且必须为0,接下来的4位表示地址类型,最末3位表示哈希大小。
类型位 含义 版本字节值
0 Conflux 0

新功能增加时,可能会添加更多类型。

大小位 后3位哈希大小
0 160
1 192
2 224
3 256
4 320
5 384
6 448
7 512

在版本域中编码哈希大小,我们就可以确保能够检查地址长度是否正确。

  1. base-32 编码地址:
    要创建负载,首先要把版本字节(version-type)和地址(addr)结合起来,得到一个21字节的数组,然后从左到右编码,将每一个5位序列号映射为对应的 ASCII 字母(对照表见下方)。在右边用零位填充,补充结尾处未满的位数。这种情况下,21字节的负载+2位零位填充就形成了一个34字节的base32编码字符串。

我们使用的字母有:abcdefghjkmnprstuvwxyz0123456789(移除了oilq )。

0x00 => a    0x08 => j    0x10 => u    0x18 => 2
0x01 => b    0x09 => k    0x11 => v    0x19 => 3
0x02 => c    0x0a => m    0x12 => w    0x1a => 4
0x03 => d    0x0b => n    0x13 => x    0x1b => 5
0x04 => e    0x0c => p    0x14 => y    0x1c => 6
0x05 => f    0x0d => r    0x15 => z    0x1d => 7
0x06 => g    0x0e => s    0x16 => 0    0x1e => 8
0x07 => h    0x0f => t    0x17 => 1    0x1f => 9
  1. 计算校验和:我们采用比特币现金的校验和算法,定义如下:
uint64_t PolyMod(const data &v) {
    uint64_t c = 1;
    for (uint8_t d : v) {
        uint8_t c0 = c >> 35;
        c = ((c & 0x07ffffffff) << 5) ^ d;

        if (c0 & 0x01) c ^= 0x98f2bc8e61;
        if (c0 & 0x02) c ^= 0x79b76d99e2;
        if (c0 & 0x04) c ^= 0xf33e5fb3c4;
        if (c0 & 0x08) c ^= 0xae2eabe2a8;
        if (c0 & 0x10) c ^= 0x1e4f43e470;
    }

    return c ^ 1;
}

校验和根据以下数据计算而来:

  • 前缀每一个字母的后5位,如“cfx”变为 0x03, 0x06, 0x18, ...
  • 0作为分隔符(5个零位)。
  • 5位为一部分组成的负载,如果必要的话,负载用零位填充到右边,补充结尾处未满的位数。
  • 八个0作为校验和的模板

请注意,可选域(如address-type)不包含在校验和计算之中。

PolyMod 返回的40位数值被分为8个5位数值(最低位优先),随后,负载和校验和根据base32 字母表编码。

  1. 把以下几个部分结合起来得到最终地址:网络前缀([network-prefix])、":"、负载([payload])和校验和([checksum])
  • 可选项地址类型也可以包含在内:网络前缀([network-prefix])、":"、地址类型([address-type])、":"、负载([payload])和校验和([checksum])

解码

输入值:Conflux base32 地址

输出值:Conflux 十六进制地址(20字节)和网络 ID (4字节)

  1. 将地址当作一个 ASCII 字符串处理,如果是大小写混合,则拒绝。将地址转变为小写,用":"隔开。如果生成的数组少于2个项目,拒绝。将第一个项目当作network-prefix-raw(原始网络前缀),最后一个项目当作payload-raw(原始负载),中间的项目为可选键值对。

  2. 分析网络 ID:

    match network-prefix-raw
        case "cfx":     1029
        case "cfxtest": 1
        case "net1029": reject
        case "net1":    reject
        case "net[n]":  n if n fits into 4 bytes
        otherwise:      reject
  1. 使用对照表将 base32 格式的payload-raw转变为字节,验证校验和。
    如果payload-raw包含任何为验证的字母(不在对照表中),则拒绝。

验证 base32 格式地址,PolyMod 函数的输入数据(整数列表)包含以下几个部分:

  • 前缀每个字的后5位。
  • 零位分隔符(5个零位)。
  • 负载的每一个 base32 字符都被映射为对应的数字。

如果 PolyMod 返回非零,则表明地址损坏,拒绝。

  1. payload-raw转变为一个8位数组,分割成版本字节(version-byte)和地址字节(addr-bytes)。

  2. 验证版本字节(version-byte)中的类型和大小位,未知版本会被拒绝。

  3. 验证可选域:

  • 如果可选域包含type.*:根据以上规范验证地址类型(address-type)。
  • 忽略未知可选域(非type.*的可选域)

返回(addr-bytes, network-id)

*(请注意:Conflux 节点需要证实network-id并非它们的本地网络 ID,并且拒绝不符合的地址,这项检查并不包含在规范之内。)

示例

encode(0x1a2f80341409639ea6a35bbcab8299066109aa55, "cfx")

1. address-type: "type.user"
2. version-byte: 0x00
3. payload: [0x00, 0x1a, 0x2f, 0x80, 0x34, 0x14, 0x09, 0x63, 0x9e, 0xa6, 0xa3, 0x5b, 0xbc, 0xab, 0x82, 0x99, 0x06, 0x61, 0x09, 0xaa, 0x55]
   5-bit parts: [0x00, 0x00, 0x0d, 0x02, 0x1f, 0x00, 0x01, 0x14, 0x02, 0x10, 0x04, 0x16, 0x07, 0x07, 0x15, 0x06, 0x14, 0x0d, 0x0d, 0x1b, 0x19, 0x0a, 0x1c, 0x02, 0x13, 0x04, 0x03, 0x06, 0x02, 0x02, 0x0d, 0x0a, 0x0a, 0x14]
   base32-encoded: "aarc9abycue0hhzgyrr53m6cxedgccrmmy"
4. checksum input: [0x03, 0x06, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x02, 0x1f, 0x00, 0x01, 0x14, 0x02, 0x10, 0x04, 0x16, 0x07, 0x07, 0x15, 0x06, 0x14, 0x0d, 0x0d, 0x1b, 0x19, 0x0a, 0x1c, 0x02, 0x13, 0x04, 0x03, 0x06, 0x02, 0x02, 0x0d, 0x0a, 0x0a, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
   checksum output: 688543492710
   checksum string: "ybjgh4xg"
5. concatenated result: "cfx:type.user:aarc9abycue0hhzgyrr53m6cxedgccrmmyybjgh4xg"

原理

网络前缀:我们选择一个容易辨认的网络前缀(cfx),这样用户可以简单地分辨出 Conflux 地址以及其他链的地址。我们也对 Conflux 链上的不同地址做了区分(如主网 vs 测试网)。这样,用户就不会在主网上意外提交测试网或私有链交易了。

地址类型:Conflux 十六进制地址有一个很好的特点,用户可以直接分辨出一个地址是外部地址(0x1)还是合约地址(0x8)。当这个信息是用 base32 编码地址表示时,就没有那么明显了。为了保留这个特点,我们在 base32 地址中包含了一个可选的可读地址类型。

版本字节:版本字节被保留,这样 Conflux 地址就可以被现有的 bch-base32 实现处理。目前还没有用到版本字节,在未来的拓展中会保留。版本字节在解码过程中进行验证,就可以避免用户使用同一个 Conflux 地址生成多个(可用的)base32 地址别名。

用点(.)作为键值分隔符:可选参数中,我们采用key.value的格式而不是更直白的key=value格式,因为这种方式使用了字母模式,在二维码中能更加高效地编码。

向后兼容性

十六进制地址和 base32 地址编码的底层字节序列是相同的,因此它们从共识和交易签名层面上看是相等的(如合约地址衍生)。

这次变更需要更新 Conflux 生态系统中的多个工具(钱包、IDE、SDK等)和 DApp 来解释 base32 地址。

测试案例

encode(0x85d80245dc02f5a89589e1f19c5c718e405b56cd, "cfx") = cfx:acc7uawf5ubtnmezvhu9dhc6sghea0403y2dgpyfjp
encode(0x85d80245dc02f5a89589e1f19c5c718e405b56cd, "cfxtest") = cfxtest:acc7uawf5ubtnmezvhu9dhc6sghea0403ywjz6wtpg
encode(0x85d80245dc02f5a89589e1f19c5c718e405b56cd, "cfxtest") = cfxtest:type.contract:acc7uawf5ubtnmezvhu9dhc6sghea0403ywjz6wtpg

encode(0x1a2f80341409639ea6a35bbcab8299066109aa55, "cfx") = cfx:aarc9abycue0hhzgyrr53m6cxedgccrmmyybjgh4xg
encode(0x1a2f80341409639ea6a35bbcab8299066109aa55, "cfxtest") = cfxtest:aarc9abycue0hhzgyrr53m6cxedgccrmmy8m50bu1p
encode(0x1a2f80341409639ea6a35bbcab8299066109aa55, "cfxtest") = cfxtest:type.user:aarc9abycue0hhzgyrr53m6cxedgccrmmy8m50bu1p

encode(0x19c742cec42b9e4eff3b84cdedcde2f58a36f44f, "cfx") = cfx:aap6su0s2uz36x19hscp55sr6n42yr1yk6r2rx2eh7
encode(0x19c742cec42b9e4eff3b84cdedcde2f58a36f44f, "cfxtest") = cfxtest:aap6su0s2uz36x19hscp55sr6n42yr1yk6hx8d8sd1
encode(0x19c742cec42b9e4eff3b84cdedcde2f58a36f44f, "cfxtest") = cfxtest:type.user:aap6su0s2uz36x19hscp55sr6n42yr1yk6hx8d8sd1

encode(0x84980a94d94f54ac335109393c08c866a21b1b0e, "cfx") = cfx:acckucyy5fhzknbxmeexwtaj3bxmeg25b2b50pta6v
encode(0x84980a94d94f54ac335109393c08c866a21b1b0e, "cfxtest") = cfxtest:acckucyy5fhzknbxmeexwtaj3bxmeg25b2nuf6km25
encode(0x84980a94d94f54ac335109393c08c866a21b1b0e, "cfxtest") = cfxtest:type.contract:acckucyy5fhzknbxmeexwtaj3bxmeg25b2nuf6km25

encode(0x1cdf3969a428a750b89b33cf93c96560e2bd17d1, "cfx") = cfx:aasr8snkyuymsyf2xp369e8kpzusftj14ec1n0vxj1
encode(0x1cdf3969a428a750b89b33cf93c96560e2bd17d1, "cfxtest") = cfxtest:aasr8snkyuymsyf2xp369e8kpzusftj14ej62g13p7
encode(0x1cdf3969a428a750b89b33cf93c96560e2bd17d1, "cfxtest") = cfxtest:type.user:aasr8snkyuymsyf2xp369e8kpzusftj14ej62g13p7

encode(0x0888000000000000000000000000000000000002, "cfx") = cfx:aaejuaaaaaaaaaaaaaaaaaaaaaaaaaaaajrwuc9jnb
encode(0x0888000000000000000000000000000000000002, "cfxtest") = cfxtest:aaejuaaaaaaaaaaaaaaaaaaaaaaaaaaaajh3dw3ctn
encode(0x0888000000000000000000000000000000000002, "cfxtest") = cfxtest:type.builtin:aaejuaaaaaaaaaaaaaaaaaaaaaaaaaaaajh3dw3ctn

更多测试案例请移步 conflux-chain#2034 查看。

实现

本地址结构已经在 conflux-chain#2034 中得到实现。

安全考虑

此提案不涉及明显的安全问题。

参考内容

比特币现金(Bitcoin Cash)规范 [链接]

EIPs #77:更安全的以太坊地址格式 [链接]

版权

版权及其他权力豁免参见 CC0

2 Likes

探讨一下

  1. 地址的冒号默认省略会不会好看一些
  2. type.user type.contract type.builtin 用单个单词替换掉如:
    type.user -> u cfxu00edyeb9mgmaem5skctwz4y9cnge5f8ru42rbphk8r
    type.contract -> c cfxc0229g2mmv57n9b1ka44kjf08t1ka46sv1s1vpcf0wh
    type.builtin -> b cfxb0229g2mmv57n9b1ka44kjf08t1ka46sv1s1vpcf0wh

这样缩短了地址, 也不会跟含有 : 字符串冲突了

多谢建议~

现在使用冒号分隔以及添加“type”主要是考虑到以后的扩展性。这里冒号分隔的中间每一项都是"key.value"的形式,之后可以根据需求添加别的可选的key,现在的"type.user"只是这种格式里的一种情况。

payload 和checksum用句点隔离开,这样的地址更加容易阅读。

现在主要是沿用了Bitcoin Cash的设计,checksum是地址固有的一部分,个人觉得不太有分开阅读的需求?应该不会有人不靠工具而是自己心算两种格式地址的转化吧:joy:

主要是想在payload部分生成靓号。
比如:cfx:*****wonderful.checkSumString

当然也不用刻意追求。

冒号中英文下不同,同样易出错吧
以后再改一次?

给大家分享一个新版本RPC :http://gpu100.mistgpu.xyz:30214,欢迎测试。

最近看经常遇到生态合作伙伴咨询通过api查寻交易和节点数据存放的问题

常出现

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": null
}

返回是null .

通常这种情况是需要自己的节点需要打开 persit_index 配置。而自己运行节点时,这个配置很容易忽略。
具体方法如下:
HTTP RPC端口在toml配置文件里通过jsonrpc_tcp_port指定,其它还有jsonrpc_ws_port、jsonrpc_tcp_port、jsonrpc_http_port、jsonrpc_local_tcp_port、jsonrpc_local_http_port。可以参考release里提供的tethys.toml默认配置文件。默认的数据存放在运行路径(pwd)下的blockchain_db和storage_db里。可以通过配置文件里的conflux_data_dir和block_db_dir分别配置。需要注意如果需要访问老的receipt数据的话,需要把persist_tx_index设置为true.