本文介绍了一种新的以太坊交易类型(EIP-2718 类型 1),该类型包含一个访问列表。通过预先声明交易将访问的地址和存储键,可以降低这些操作的 Gas 成本,旨在缓解 EIP-2929 引入的合约兼容性风险,并为未来的状态访问优化(如区块级见证)奠定基础。
添加了一种包含访问列表的交易类型,该访问列表包含交易计划访问的地址和存储键的列表。访问列表之外的访问是可能的,但会变得更昂贵。
我们引入了一种新的 EIP-2718 交易类型,格式为 $0x01 || \text{rlp}([\text{chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signatureYParity, signatureR, signatureS}])$。
accessList 指定了地址和存储键的列表;这些地址和存储键被添加到 $accessed\_addresses$ 和 $accessed\_storage\_keys$ 全局集合中(在 EIP-2929 中引入)。会收取 gas 成本,但相对于访问列表之外的访问成本会有折扣。
本 EIP 具有两项功能:
SLOAD 和 EXT* 操作码将仅花费 $100$ gas:低到不仅可以防止由于该 EIP 造成的损坏,还可以“解除”由于 EIP 1884 而卡住的任何合约。TransactionType $1$。参见 EIP-2718
ChainId 交易仅在此 chainID 的网络上有效。
YParity secp256k1 签名 y 值的奇偶性(偶数取 $0$,奇数取 $1$)。
| Constant | Value |
|---|---|
FORK_BLOCK |
$12244000$ |
ACCESS_LIST_STORAGE_KEY_COST |
$1900$ |
ACCESS_LIST_ADDRESS_COST |
$2400$ |
从 FORK_BLOCK_NUMBER 开始,引入了一个新的 EIP-2718 交易,其 TransactionType 为 $1$。
该交易的 EIP-2718 TransactionPayload 是 rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signatureYParity, signatureR, signatureS])。
此交易的 $signatureYParity, signatureR, signatureS$ 元素表示对 keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])) 的 secp256k1 签名。
此交易的 EIP-2718 ReceiptPayload 是 rlp([status, cumulativeGasUsed, logsBloom, logs])。
为了使交易有效,accessList 必须是 [[{20 bytes}, [{32 bytes}...]]...] 类型,其中 ... 表示“零个或多个左侧的项目”。例如,以下是一个有效的访问列表(所有十六进制字符串在实际中都将是字节表示):
[
[
"0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
[
"0x0000000000000000000000000000000000000000000000000000000000000003",
"0x0000000000000000000000000000000000000000000000000000000000000007"
]
],
[
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
[]
]
]
在执行开始时(即在根据 EIP-2028 规则收取 $21000 + 4 * \text{zeroes} + 16 * \text{nonzeroes}$ 初始 gas 的同时),我们为访问列表收取额外的 gas:每个地址 $ACCESS\_LIST\_ADDRESS\_COST$ gas,每个存储键 $ACCESS\_LIST\_STORAGE\_KEY\_COST$ gas。例如,上述示例将收取 $ACCESS\_LIST\_ADDRESS\_COST * 2 + ACCESS\_LIST\_STORAGE\_KEY\_COST * 2$ gas。
请注意,不禁止非唯一地址和存储键,但它们将被多次收费,并且除了更高的 gas 成本之外,多次包含一个值与建议的单次包含在执行流程或结果上没有其他区别。
地址和存储键将立即加载到 $accessed\_addresses$ 和 $accessed\_storage\_keys$ 全局集合中;这可以通过以下逻辑完成(该逻辑也作为 RLP 解码访问列表验证的代码规范):
def process_access_list(access_list) -> Tuple[List[Set[Address], Set[Pair[Address, Bytes32]]], int]:
accessed_addresses = set()
accessed_storage_keys = set()
gas_cost = 0
assert isinstance(access_list, list)
for item in access_list:
assert isinstance(item, list) and len(item) == 2
# 验证并添加地址
address = item[0]
assert isinstance(address, bytes) and len(address) == 20
accessed_addresses.add(address)
gas_cost += ACCESS_LIST_ADDRESS_COST
# 验证并添加存储键
assert isinstance(item[1], list)
for key in item[1]:
assert isinstance(key, bytes) and len(key) == 32
accessed_storage_keys.add((address, key))
gas_cost += ACCESS_LIST_STORAGE_KEY_COST
return (
accessed_addresses,
accessed_storage_keys,
gas_cost
)
访问列表不按字节收取费用,如交易数据那样;上述每项成本旨在覆盖访问列表数据的带宽成本以及评估交易时访问这些账户和存储键的成本。
这样做是为了鼓励交易尽可能多地使用访问列表,并且因为当存储读取可预测时(因为客户端可以预加载数据库中的数据和/或在收到交易时请求见证,或者至少并行加载数据),处理交易更容易。
这样做是为了最大化简洁性,避免了关于要防止与什么重复的问题:只是访问列表中两个地址/键之间,还是访问列表与交易发送者/接收者/新创建合约之间,或者其他限制?由于 gas 按项目收取,因此在访问列表中两次包含一个值没有收益,只有成本,因此这在实践中不应导致额外的链膨胀。
这样做是为了确保交易不能被“重新解释”为不同类型的交易。
本 EIP 确实会使执行“意外”的 SLOAD 和账户访问的 gas 成本更高。由于 gas 是预付的,因此不影响固定 gas 的本地调用,它不会像以前的 gas 成本增加那样可能导致合约损坏。但是,它确实使严重依赖存储访问的应用程序在经济上变得不那么可行。
在许多情况下,实时构建访问列表很困难,在交易生成和签名之间存在较高时间延迟的环境中,或者在高度重视交易生成器简洁性(例如,两者之一或两者都可能适用于硬件钱包)的环境中,这种情况会加剧。
然而,本 EIP 仅提议对访问列表提供 $10\%$ 的初始折扣,因此不费心生成访问列表而只进行简单交易几乎没有成本。在工具开发和访问列表生成变得更成熟之后,预计未来硬分叉将逐步提高访问列表之外状态访问的成本。
由于使用访问列表,平均区块大小将增加。但是,访问列表的每字节成本对于存储键是 $1900 / 32 = 59.375$,对于地址是 $2400 / 20 = 120$,比 calldata 昂贵得多;因此,最坏情况下的区块大小不会增加。此外,平均区块大小的增加将通过在接收交易时预取存储和/或在接收区块时并行加载存储的能力得到部分补偿。
通过 CC0 放弃版权及相关权利。
- 原文链接: github.com/nerolation/EI...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!