ERC-7831: 多链寻址
地址和链的文本表示。
Authors | Sam Wilson (@SamWilsn) <sam@binarycake.ca> |
---|---|
Created | 2024-08-30 |
Discussion Link | https://ethereum-magicians.org/t/erc-7831-multi-chain-addressing/21942 |
Requires | EIP-55, EIP-137, EIP-155, EIP-165, EIP-2304 |
Table of Contents
摘要
本提案引入了一种链特定的地址格式,允许指定一个账户以及该账户打算在其上进行交易的链。这些链特定的地址采用 (example.eth:optimism)
、6A10161835a36302BfD39bDA9B44f5734442234e:ethereum:11155111
等形式。目标链使用存储在 ENS 上的注册表来解析。
动机
以太坊生态系统正变得越来越分散。这意味着一个 20 字节的地址本身不足以完全指定一个账户。如果资金被发送到错误链上无法访问的地址,这可能会出现问题。
与其使用不易于人阅读的链标识符,不如使用易于人阅读的链名称扩展地址,然后可以将其解析为链标识符。EIP-155 以来,从链名称到标识符的映射一直使用中心化列表在链下维护。此解决方案有两个主要缺点:
- 它无法随着 L2 数量的增长而扩展。
- 列表维护者是受信任的中心化实体。
本 ERC 提议使用 ENS 将链名称映射到标识符,同时通过更改根链来保持最大的灵活性。
为什么不使用带有 ERC-2304 的 ENS?
虽然 ERC-2304 允许注册者指定每个链的地址,但它不提供接收资产的默认链(也不应该提供。)接收链的选择过多地依赖于链下因素,因此不需要通过交易来更改。
规范
本文档中的关键词“必须 (MUST)”、“禁止 (MUST NOT)”、“必需 (REQUIRED)”、“应该 (SHALL)”、“不应 (SHALL NOT)”、“应 (SHOULD)”、“不应 (SHOULD NOT)”、“推荐 (RECOMMENDED)”、“不推荐 (NOT RECOMMENDED)”、“可以 (MAY)”和“可选 (OPTIONAL)”应按照 RFC 2119 和 RFC 8174 中的描述进行解释。
本提案中的语法片段以增强巴科斯范式 (ABNF) 给出,如 RFC 5234 和 RFC 7405 中所定义。
定义
以下术语在本提案中使用:
- agent - 负责将链特定地址解析为其精确账户和链条的软件/工具。
- bridge - 将根链连接到目标链的合约(例如,用于转移代币、代理函数调用。)
- root chain - 包含 bridge 和名称解析器合约的区块链。
- target chain - 被识别账户打算在其上进行交易的区块链;可以是根链上具有 bridge 的任何链。
语法
从高层次上讲,链特定地址由三个组件组成,这些组件由冒号 (:
) 分隔,从最右边的最通用到最左边的最具体排列:
- 一个
local-part
,用于标识目标链上的账户; - 一个
chain-part
,用于标识目标链;和 - 可选地,一个
root-part
,用于标识根链。
这些组件可以用括号 ((
和 )
) 括起来,以消除解析歧义。
更正式地说,有效的链特定地址必须符合以下语法:
address = OPEN bare-address CLOSE /
bare-address
bare-address = local-part SEP chain-part [SEP root-part]
OPEN = '('
CLOSE = ')'
SEP = ':'
Local Part
local-part
是链特定地址中最具体的部分。它标识目标链上的账户。它可以是十六进制字符串 (hex-address
) 或类似 ENS 的名称 (ens-like
)。
有效的 local-part
片段必须匹配以下语法:
local-part = hex-address /
ens-like
十六进制地址
当 local-part
是十六进制字符串时,它必须包括校验和字母大小写(参见 校验和),并且可以省略前导零。它不得包含前导 0x
前缀。
请注意,local-part
可以编码比 20 字节(40 个十六进制数字)更长或更短的地址。实现必须支持 1 个十六进制数字到 40 个数字的 local-part
长度。实现应该支持任意大小(在合理限制内)的 local-part
组件。
形式上,hex-address
必须匹配以下语法:
hex-address = 1*HEXDIG
类似 ENS 的名称
为了区分 ENS 名称和十六进制字符串,与标准 ENS 名称不同,在链特定地址的 local-part
中使用的名称必须至少包含一个点 (.
)。如果存在,则在解析名称之前应删除放置在最右侧位置的点(例如 eth.
或 example.eth.
)。除非不存在其他点,否则链特定地址不应在最右侧位置包含点。
以下语法仅用于说明。有关 ENS 名称的定义,请参见 ERC-137。
; 粗略的 ENS 名称近似值,附加要求是它
; 至少包含一个“.”
ens-like = 1*NOTSEP DOT *(1*NOTSEP [DOT])
NOTSEP = %x01-39 / %x3b-ff
Chain Part
chain-part
标识目标链。它必须是 ERC-137 中定义的有效 ENS 名称。
以下语法仅用于说明。有关 ENS 名称的定义,请参见 ERC-137。
chain-part = ens-name
; ENS 名称的粗略近似值,没有其他要求
ens-name = 1*NOTSEP
Root Part
root-part
标识针对其解析其他名称的根链。如果存在,root-part
应该是根链的 EIP-155 chainid
,采用十进制格式。当 chainid == 1
时,不应存在 root-part
,当 chainid != 1
时,必须存在 root-part
。
root-part = 1*DIGIT
解析
解析链特定地址从右侧开始,然后向左移动。
根链
如果不存在 root-part
,则假定为 1
。将根的 chainid
设置为 root-part
的值。代理必须能够针对此链解析 ENS 名称。这可能意味着它具有 RPC 访问权限和已知的 ENS 解析器地址,但是任何解析地址的方法都足够了。
请注意,这使得 example.eth:optimism
与 example.eth:optimism:10
不同。在前一种情况下,example.eth
和 optimism
都是使用部署在主网上的 ENS 解析的。在第二种情况下,这两个名称将针对部署在 Optimism 链上的 ENS 进行解析,这是一种不寻常的情况。
链标识符的分配在 EIP-155 中定义。
目标链
接下来,通过将 chain-part
的值与 .tbd.eth
连接起来,构造目标链的 ENS 名称(例如,example.eth:foobar
的目标链为 foobar.tbd.eth
)。针对根链上的 ENS 部署解析目标链的地址(即使用 ERC-137 的 addr
)。此地址上的合约是“bridge 合约”。
代理必须验证 bridge 合约是否支持地址中的 chain-part
。首先,代理必须使用 ChainMetadata
的接口标识符(参见 Bridge 接口)在 bridge 合约上调用 ERC-165 的 supportsInterface
,如果它返回 false,则代理应中止解析。接下来,代理必须使用与上述调用 addr
相同的名称哈希(参见 ERC-137)调用 bridge 合约的 acceptsName
函数。如果 acceptsName
返回 false,则代理应中止解析。
bridge 合约应提供使代理能够与目标链交互的功能/元数据。此外,它应支持用于接口发现的 ERC-165 机制,并且可以支持其他方法来完成相同的操作。Bridge 合约必须实现 ChainMetadata
(参见 Bridge 接口)。有关桥接的更多细节留待以后的提案。
本地地址
十六进制
验证校验和(请参见 校验和)。
本地地址是目标链原生地址的二进制表示形式的十六进制编码。例如,对于原生以太坊地址 0x6A10161835a36302BfD39bDA9B44f5734442234e
,本地地址将为 6A10161835a36302BfD39bDA9B44f5734442234e
。
类似 ENS
如果本地地址以点 (.
) 结尾,请将其删除。使用 ERC-2304 的 addr
针对根链上的 ENS 部署解析地址,其中 coinType
派生自目标链的 chainid
(从 bridge 合约中检索)。
Bridge 接口
Bridge 合约必须实现以下接口:
interface ChainMetadata {
function chainId() external view returns (uint64);
function coinType() external view returns (uint256);
function acceptsName(bytes32 keccak) external view returns (bool);
}
当使用 ERC-165 的 supportsInterface
查询时,bridge 合约必须为 0x00000000
返回 true。
校验和
十六进制字符串根据略作修改的 ERC-55 算法区分大小写。该算法通过包装 nibble_index
以适合 keccak 哈希来修改。
修改后的 ERC-55 算法的 Python 实现
```python from Crypto.Hash import keccak # from pycryptodome def checksum_encode(addr): hex_addr = addr.hex() checksummed_buffer = "" # Treat the hex address as ascii/utf-8 for keccak256 hashing # 将十六进制地址视为 ascii/utf-8 以进行 keccak256 哈希 k = keccak.new(digest_bits=256) k.update(hex_addr.encode("utf-8")) hashed_address = k.hexdigest() # Iterate over each character in the hex address # 迭代十六进制地址中的每个字符 for nibble_index, character in enumerate(hex_addr): if character in "0123456789": # We can't upper-case the decimal digits # 我们不能将十进制数字大写 checksummed_buffer += character elif character in "abcdef": # Check if the corresponding hex digit (nibble) in the hash is 8 or higher # 检查哈希中相应的十六进制数字(半字节)是否为 8 或更高 nibble_index_wrapped = nibble_index % len(hashed_address) hashed_address_nibble = int(hashed_address[nibble_index_wrapped], 16) if hashed_address_nibble > 7: checksummed_buffer += character.upper() else: checksummed_buffer += character else: raise Exception( f"Unrecognized hex character {character!r} at position {nibble_index}" ) return "0x" + checksummed_buffer def test(addr_str: str): padded_addr_str = addr_str.removeprefix("0x") if len(padded_addr_str) % 2 == 1: # Pad to an even number of nibbles. # 填充为偶数个半字节。 padded_addr_str = "0" + padded_addr_str addr_bytes = bytes.fromhex(padded_addr_str) checksum_encoded = checksum_encode(addr_bytes) if checksum_encoded != addr_str: print(f"{checksum_encoded} != expected {addr_str}") test("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed") test("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359") test("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB") test("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb") test("0x004f67dAbb603AAA58eD52641CCafF09C559704A") test("0x4F67dABB603aAa58Ed52641cCAff09C559704A") test( "0x" "5aaeB6053f3e94c9B9a09f33669435E7" "ef1BEaED5AaEB6053f3e94C9B9a09F33" "669435e7ef1bEAed5aaEb6053f3e94C9" "b9a09f33669435E7ef1beAED5aaEB605" "3f3e94c9b9a09F33669435e7ef1beAED" ) ```例如,以下字符串的大小写正确:
004f67dAbb603AAA58eD52641CCafF09C559704A
04f67dAbb603AAA58eD52641CCafF09C559704A
4F67dABB603aAa58Ed52641cCAff09C559704A
理由
组件顺序
组件的排序方式是从最具体到最概括,因为…
分隔符选择
冒号 (:
) 是分隔符的合理选择,因为它不是 ENS 名称中允许的字符,并且很熟悉(例如,IPv6),并且不如 @
符号那样重载。
替代方案:@
@
符号是地址的常见选择,并在电子邮件和多种联合通信协议中使用。英语阅读(foo-AT-example-DOT-com)很自然,并暗示了左侧和右侧组件之间的层次结构。
不幸的是,由于 @
符号被广泛使用,因此将其包含在链特定地址中会使所有这些协议标识符更加混乱(甚至无效)。例如,foo@foo.eth@ethereum
不是有效的电子邮件地址。
替代方案:/
作为子域的目标链
虽然在技术上可以将 chain-part
解析为根 ENS 名称(例如,ethereum.eth
而不是 ethereum.tbd.eth
),但是使用子域允许预先注册众所周知的链名称,以便在切换到开放注册之前进行名称的初始分发。
如果没有这种预注册,攻击者可以在合法项目之前注册众所周知的名称。
在预注册期之后,开放注册是可以接受的,因为新链可以在公开宣布之前注册其名称。
向后兼容性
始终可以确定特定字符串是链特定地址、普通地址还是普通 ENS 名称。由于此属性,向后不兼容的机会很少:链特定地址不是有效的遗留地址或 ENS 名称,因此不支持它们的工具将直接拒绝它们。
测试用例
ENS 配置
主网 (1)
名字 | 代币类型 | 记录 |
---|---|---|
ethereum.tbd.eth |
- | 到主网的 bridge 合约 (1) |
rollup1.tbd.eth |
- | 到汇总 1 的 bridge 合约 (1608) |
example.eth |
2147483649 |
0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa |
example.eth |
2147485256 |
0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB |
Sepolia (11155111)
名字 | 代币类型 | 记录 |
---|---|---|
ethereum.tbd.eth |
- | 到 sepolia 的 bridge 合约 (11155111) |
example.eth |
输入和预期输出
有效
输入 | 目标链 | 本地地址 |
---|---|---|
(example.eth:ethereum) |
主网 (1) | 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa |
example.eth:rollup1 |
汇总 1 (1608) | 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB |
example.eth.:ethereum |
主网 (1) | 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa |
0:ethereum |
主网 (1) | 0x0000000000000000000000000000000000000000 |
无效
输入 | 失败原因 |
---|---|
(0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa:ethereum) |
无效的十六进制 |
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:ethereum) |
无效校验和 |
(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:ethereum) |
无效校验和 |
(eth:ethereum) |
无效的十六进制 |
(:ethereum) |
缺少 local-part |
参考实现
安全考虑
Unicode 和域名抢注攻击
攻击者可以注册类似于众所周知的链名称的 ENS 名称。例如,etherium
和 ehtereum
相当接近 ethereum
。虽然许多 Unicode 同形字被 ENS 库捕获,但代理仍然应该意识到它们构成的风险。
版权
版权和相关权利已通过 CC0 放弃。
Citation
Please cite this document as:
Sam Wilson (@SamWilsn) <sam@binarycake.ca>, "ERC-7831: 多链寻址 [DRAFT]," Ethereum Improvement Proposals, no. 7831, August 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7831.