英文原文: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 地址
- 网络前缀(Network-prefix):
match network-id:
case 1029: "cfx"
case 1: "cfxtest"
case n: "net[n]"
有效网络前缀示例:"cfx"
,"cfxtest"
,"net17"
无效网络前缀示例:"bch"
,"conflux"
,"net1"
,"net1029"
- 【可选】地址类型:
match addr[0] & 0xf0
case b00000000: "type.builtin"
case b00010000: "type.user"
case b10000000: "type.contract"
可以用"type.null"
实现空地址(0x0000000000000000000000000000000000000000
)。
- 版本字节:
版本字节的最高位被保留,并且必须为0,接下来的4位表示地址类型,最末3位表示哈希大小。
类型位 | 含义 | 版本字节值 |
---|---|---|
0 | Conflux | 0 |
新功能增加时,可能会添加更多类型。
大小位 | 后3位哈希大小 |
---|---|
0 | 160 |
1 | 192 |
2 | 224 |
3 | 256 |
4 | 320 |
5 | 384 |
6 | 448 |
7 | 512 |
在版本域中编码哈希大小,我们就可以确保能够检查地址长度是否正确。
- base-32 编码地址:
要创建负载,首先要把版本字节(version-type
)和地址(addr
)结合起来,得到一个21字节的数组,然后从左到右编码,将每一个5位序列号映射为对应的 ASCII 字母(对照表见下方)。在右边用零位填充,补充结尾处未满的位数。这种情况下,21字节的负载+2位零位填充就形成了一个34字节的base32编码字符串。
我们使用的字母有:abcdefghjkmnprstuvwxyz0123456789
(移除了o
、i
、l
、q
)。
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
- 计算校验和:我们采用比特币现金的校验和算法,定义如下:
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 字母表编码。
- 把以下几个部分结合起来得到最终地址:网络前缀(
[network-prefix]
)、":"
、负载([payload]
)和校验和([checksum]
)
- 可选项地址类型也可以包含在内:网络前缀(
[network-prefix]
)、":"
、地址类型([address-type]
)、":"
、负载([payload]
)和校验和([checksum]
)
解码
输入值:Conflux base32 地址
输出值:Conflux 十六进制地址(20字节)和网络 ID (4字节)
-
将地址当作一个 ASCII 字符串处理,如果是大小写混合,则拒绝。将地址转变为小写,用
":"
隔开。如果生成的数组少于2个项目,拒绝。将第一个项目当作network-prefix-raw
(原始网络前缀),最后一个项目当作payload-raw
(原始负载),中间的项目为可选键值对。 -
分析网络 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
- 使用对照表将 base32 格式的
payload-raw
转变为字节,验证校验和。
如果payload-raw
包含任何为验证的字母(不在对照表中),则拒绝。
验证 base32 格式地址,PolyMod 函数的输入数据(整数列表)包含以下几个部分:
- 前缀每个字的后5位。
- 零位分隔符(5个零位)。
- 负载的每一个 base32 字符都被映射为对应的数字。
如果 PolyMod 返回非零,则表明地址损坏,拒绝。
-
将
payload-raw
转变为一个8位数组,分割成版本字节(version-byte
)和地址字节(addr-bytes
)。 -
验证版本字节(
version-byte
)中的类型和大小位,未知版本会被拒绝。 -
验证可选域:
- 如果可选域包含
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。