EIP-4844 分片Blob事务以简单、向前兼容的方式扩展以太坊的数据可用性。
- 一、摘要
- 二、动机
- 三、Specification 规范
- 四、基本原理
- 五、Backwards Compatibility 向后兼容
- 六、Mempool issues 内存池问题
- 七、Test Cases 测试用例
- 八、Security Considerations 安全考虑
- 九、版权所有
- 十、Citation 引用
一、摘要
为“携带blob的交易”引入一种新的交易格式,其中包含大量 EVM 执行无法访问的数据,但可以访问其承诺。该格式旨在与将在全分片中使用的格式完全兼容。
二、动机
Rollups 在短期和中期,甚至可能在长期内,是以太坊唯一的去信任扩展解决方案。几个月来,L1 的交易费用一直非常高,并且迫切需要做任何必要的事情来帮助促进整个生态系统转向汇总。 Rollups 显着降低了许多以太坊用户的费用:Optimism 和 Arbitrum 经常提供比以太坊基础层本身低 3-8 倍的费用,而 ZK rollups 具有更好的数据压缩并且可以避免包含签名,费用比基础层低 40-100 倍。
然而,即使这些费用对许多用户来说也太贵了。长期解决 rollups 自身长期不足的长期解决方案一直是数据分片,这将为 rollups 可以使用的链增加每块专用数据空间约 16 MB。然而,数据分片仍然需要相当长的时间才能完成实施和部署。
这个 EIP 提供了一个权宜之计解决方案,通过实现将在分片中使用的交易格式,而不是实际分片这些交易。相反,这种交易格式的数据只是信标链的一部分,并由所有共识节点完全下载(但可以在相对较短的延迟后删除)。与完整数据分片相比,这个 EIP 减少了可以包含的交易数量上限,对应于每个区块约 0.25 MB 的目标和约 0.5 MB 的限制。
三、Specification 规范
3.1 Parameters 参数
Constant - 常量 | Value |
---|---|
BLOB_TX_TYPE | Bytes1(0x05) |
FIELD_ELEMENTS_PER_BLOB | 4096 |
BLS_MODULUS | 52435875175126190479447740508185965837690552500527637822603658699938581184513 |
BLOB_COMMITMENT_VERSION_KZG | Bytes1(0x01) |
POINT_EVALUATION_PRECOMPILE_ADDRESS | Bytes20(0x14) |
POINT_EVALUATION_PRECOMPILE_GAS | 50000 |
MAX_DATA_GAS_PER_BLOCK | 2**19 |
TARGET_DATA_GAS_PER_BLOCK | 2**18 |
MIN_DATA_GASPRICE | 1 |
DATA_GASPRICE_UPDATE_FRACTION | 2225652 |
MAX_VERSIONED_HASHES_LIST_SIZE | 2**24 |
MAX_CALLDATA_SIZE | 2**24 |
MAX_ACCESS_LIST_SIZE | 2**24 |
MAX_ACCESS_LIST_STORAGE_KEYS | 2**24 |
MAX_TX_WRAP_KZG_COMMITMENTS | 2**12 |
LIMIT_BLOBS_PER_TX | 2**12 |
DATA_GAS_PER_BLOB | 2**17 |
HASH_OPCODE_BYTE | Bytes1(0x49) |
HASH_OPCODE_GAS | 3 |
3.2 Type aliases 类型别名
Type | Base type 底座型 | Additional checks 额外检查 |
---|---|---|
BLSFieldElement | uint256 | x < BLS_MODULUS |
Blob | Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB] | |
VersionedHash | Bytes32 | |
KZGCommitment | Bytes48 | Same as BLS standard “is valid pubkey” check but also allows 0x00..00 for point-at-infinity 与 BLS 标准“是有效公钥”检查相同,但也允许 0x00..00 用于无穷远点 |
KZGProof | Bytes48 | Same as for KZGCommitment与 KZGCommitment 相同 |
3.3 Cryptographic Helpers 加密助手
在整个提案中,我们使用相应的共识 4844 规范中定义的加密方法和类。具体来说,我们使用来自 polynomial-commitments.md
的以下方法:
3.4 Helpers
1def kzg_to_versioned_hash(kzg: KZGCommitment) -> VersionedHash:
2 return BLOB_COMMITMENT_VERSION_KZG + sha256(kzg)[1:]
使用泰勒展开逼近 factor * e ** (numerator / denominator)
:
1def fake_exponential(factor: int, numerator: int, denominator: int) -> int:
2 i = 1
3 output = 0
4 numerator_accum = factor * denominator
5 while numerator_accum > 0:
6 output += numerator_accum
7 numerator_accum = (numerator_accum * numerator) // (denominator * i)
8 i += 1
9 return output // denominator
3.5 New transaction type 新的交易类型
我们引入了一种新的 EIP-2718 交易类型,格式为单字节 BLOB_TX_TYPE
后跟包含交易内容的 SignedBlobTransaction
容器的 SSZ 编码:
1class SignedBlobTransaction(Container):
2 message: BlobTransaction
3 signature: ECDSASignature
4
5class BlobTransaction(Container):
6 chain_id: uint256
7 nonce: uint64
8 max_priority_fee_per_gas: uint256
9 max_fee_per_gas: uint256
10 gas: uint64
11 to: Union[None, Address] # Address = Bytes20
12 value: uint256
13 data: ByteList[MAX_CALLDATA_SIZE]
14 access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE]
15 max_fee_per_data_gas: uint256
16 blob_versioned_hashes: List[VersionedHash, MAX_VERSIONED_HASHES_LIST_SIZE]
17
18class AccessTuple(Container):
19 address: Address # Bytes20
20 storage_keys: List[Hash, MAX_ACCESS_LIST_STORAGE_KEYS]
21
22class ECDSASignature(Container):
23 y_parity: boolean
24 r: uint256
25 s: uint256
max_priority_fee_per_gas
和 max_fee_per_gas
字段遵循 EIP-1559 语义, access_list
与 EIP-2930
相同。
EIP-2718
使用“包装数据”进行扩展,类型化的交易可以根据上下文以两种形式编码:
- Network (default):
TransactionType || TransactionNetworkPayload
或LegacyTransaction
- Minimal (as in execution payload):
TransactionType || TransactionPayload
或LegacyTransaction
执行负载/块使用交易的最小编码。在交易池和本地交易日志中使用网络编码。
对于以前类型的交易,网络编码没有什么不同,即 TransactionNetworkPayload == TransactionPayload
。
TransactionNetworkPayload
用附加数据包装 TransactionPayload
:应该在签名验证之前或之后直接验证此包装数据。
当 blob 事务通过网络传递时(请参阅下面的网络部分),事务的 TransactionNetworkPayload
版本还包括 blobs
和 kzgs
(承诺列表)。执行层在签名验证后针对部 TransactionPayload
验证包装器有效性:
blob_versioned_hashes
中的所有哈希必须以字节BLOB_COMMITMENT_VERSION_KZG
开头- 一个有效块中最多可能有
MAX_DATA_GAS_PER_BLOCK // DATA_GAS_PER_BLOB
总 blob 承诺。 - 有等量的版本化哈希、kzg 承诺和 blob。
- KZG 承诺哈希到版本哈希,即
kzg_to_versioned_hash(kzg[i]) == versioned_hash[i]
- KZG 承诺与 blob 内容相匹配。 (注意:这可以通过附加数据进行优化,使用从承诺和 blob 数据派生的两个点进行随机评估的证明)
签名被验证并且 tx.origin
被计算如下:
1def unsigned_tx_hash(tx: SignedBlobTransaction) -> Bytes32:
2 # The pre-image is prefixed with the transaction-type to avoid hash collisions with other tx hashers and types
3 return keccak256(BLOB_TX_TYPE + ssz.serialize(tx.message))
4
5def get_origin(tx: SignedBlobTransaction) -> Address:
6 sig = tx.signature
7 # v = int(y_parity) + 27, same as EIP-1559
8 return ecrecover(unsigned_tx_hash(tx), int(sig.y_parity)+27, sig.r, sig.s)
已签名的 blob 交易的哈希值应计算为:
1def signed_tx_hash(tx: SignedBlobTransaction) -> Bytes32:
2 return keccak256(BLOB_TX_TYPE + ssz.serialize(tx))
3.6 Header extension 头扩展
当前标头编码使用新的 256 位无符号整数字段 excess_data_gas
进行扩展。这是自此 EIP 激活以来链上消耗的过量数据气体的运行总量。如果数据气体总量低于目标, excess_data_gas
上限为零。
因此,生成的标头 RLP 编码为:
1rlp([
2 parent_hash,
3 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, # ommers hash
4 coinbase,
5 state_root,
6 txs_root,
7 receipts_root,
8 logs_bloom,
9 0, # difficulty
10 number,
11 gas_limit,
12 gas_used,
13 timestamp,
14 extradata,
15 prev_randao,
16 0x0000000000000000, # nonce
17 base_fee_per_gas,
18 withdrawals_root,
19 excess_data_gas
20])
excess_data_gas
的值可以使用块中的父标头和 blob 数来计算。
1def calc_excess_data_gas(parent: Header, new_blobs: int) -> int:
2 consumed_data_gas = new_blobs * DATA_GAS_PER_BLOB
3 if parent.excess_data_gas + consumed_data_gas < TARGET_DATA_GAS_PER_BLOCK:
4 return 0
5 else:
6 return parent.excess_data_gas + consumed_data_gas - TARGET_DATA_GAS_PER_BLOCK
对于第一个分叉后区块, parent.excess_data_gas
被评估为 0
。
3.7 Beacon chain validation 信标链验证
在共识层上,blob 现在在信标块主体中被引用,但未完全编码。不是将全部内容嵌入正文,而是将 blob 的内容单独传播,作为“sidecar”。
这种“边车”设计通过黑盒 is_data_available()
为进一步增加的数据提供前向兼容性:完全分片 is_data_available()
可以用数据可用性采样 (DAS) 代替,从而避免所有 blob 被网络上的所有信标节点下载网络。
请注意,共识层的任务是持久化数据可用性的 blob,而执行层则不是。
ethereum/consensus-specs
存储库定义了此 EIP 中涉及的以下信标节点更改:
- Beacon chain信标链:处理更新的信标块并确保 blob 可用。
- P2P 网络:Goosip和同步更新的信标块类型和新的 blob 边车。
- Honest validator 诚实验证器:使用 blob 生成信标块,发布 blob sidecars。
3.8 Opcode to get versioned hashes 获取版本哈希的操作码
我们添加了一个操作码 DATAHASH
(字节值为 HASH_OPCODE_BYTE
),它从栈顶读取 index
作为大端 uint256
,如果是 tx.message.blob_versioned_hashes[index]
则用 index < len(tx.message.blob_versioned_hashes)
替换它在栈上,并且否则使用归零的 bytes32
值。操作码的 gas 成本为 HASH_OPCODE_GAS
。
3.9 Point evaluation precompile 点评估预编译
在 POINT_EVALUATION_PRECOMPILE_ADDRESS
添加预编译,用于验证 KZG 证明,该证明声称blob(由承诺表示)在给定点计算为给定值。
预编译花费 POINT_EVALUATION_PRECOMPILE_GAS
并执行以下逻辑:
1def point_evaluation_precompile(input: Bytes) -> Bytes:
2 """
3 Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG proof.
4 Also verify that the provided commitment matches the provided versioned_hash.
5 """
6 # The data is encoded as follows: versioned_hash | z | y | commitment | proof |
7 versioned_hash = input[:32]
8 z = input[32:64]
9 y = input[64:96]
10 commitment = input[96:144]
11 kzg_proof = input[144:192]
12
13 # Verify commitment matches versioned_hash
14 assert kzg_to_versioned_hash(commitment) == versioned_hash
15
16 # Verify KZG proof
17 assert verify_kzg_proof(commitment, z, y, kzg_proof)
18
19 # Return FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values
20 return Bytes(U256(FIELD_ELEMENTS_PER_BLOB).to_be_bytes32() + U256(BLS_MODULUS).to_be_bytes32())
预编译必须拒绝非规范的字段元素(即提供的字段元素必须严格小于 BLS_MODULUS
)。
3.10 Gas accounting
我们将数据气体作为一种新型气体引入。它独立于普通气体并遵循自己的目标规则,类似于 EIP-1559。我们使用 excess_data_gas
标头字段来存储计算数据 gas 价格所需的持久数据。目前,只有 blob 以数据 gas 计价。
1def calc_data_fee(tx: SignedBlobTransaction, parent: Header) -> int:
2 return get_total_data_gas(tx) * get_data_gasprice(header)
3
4def get_total_data_gas(tx: SignedBlobTransaction) -> int:
5 return DATA_GAS_PER_BLOB * len(tx.message.blob_versioned_hashes)
6
7def get_data_gasprice(header: Header) -> int:
8 return fake_exponential(
9 MIN_DATA_GASPRICE,
10 header.excess_data_gas,
11 DATA_GASPRICE_UPDATE_FRACTION
12 )
区块有效性条件被修改为包括数据气体检查:
1def validate_block(block: Block) -> None:
2 ...
3
4 for tx in block.transactions:
5 ...
6
7 # the signer must be able to afford the transaction
8 assert signer(tx).balance >= tx.message.gas * tx.message.max_fee_per_gas + get_total_data_gas(tx) * tx.message.max_fee_per_data_gas
9
10 # ensure that the user was willing to at least pay the current data gasprice
11 assert tx.message.max_fee_per_data_gas >= get_data_gasprice(parent(block).header)
通过 data_fee
计算的实际 calc_data_fee
在交易执行前从发送方余额中扣除并销毁,并且在交易失败的情况下不予退还。
3.11 Networking 网络
节点不得自动向其对等节点广播 blob 事务。相反,这些交易仅使用 NewPooledTransactionHashes
消息公布,然后可以通过 GetPooledTransactions
手动请求。
交易在执行层网络上显示为 TransactionType || TransactionNetworkPayload
,有效负载是一个 SSZ 编码的容器:
1class BlobTransactionNetworkWrapper(Container):
2 tx: SignedBlobTransaction
3 # KZGCommitment = Bytes48
4 blob_kzgs: List[KZGCommitment, MAX_TX_WRAP_KZG_COMMITMENTS]
5 # BLSFieldElement = uint256
6 blobs: List[Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB], LIMIT_BLOBS_PER_TX]
7 # KZGProof = Bytes48
8 kzg_aggregated_proof: KZGProof
我们对 BlobTransactionNetworkWrapper
对象进行网络级验证,如下所示:
1def validate_blob_transaction_wrapper(wrapper: BlobTransactionNetworkWrapper):
2 versioned_hashes = wrapper.tx.message.blob_versioned_hashes
3 commitments = wrapper.blob_kzgs
4 blobs = wrapper.blobs
5 # note: assert blobs are not malformatted
6 assert len(versioned_hashes) == len(commitments) == len(blobs)
7
8 # Verify that commitments match the blobs by checking the KZG proof
9 assert verify_aggregate_kzg_proof(blobs, commitments, wrapper.kzg_aggregated_proof)
10
11 # Now that all commitments have been verified, check that versioned_hashes matches the commitments
12 for versioned_hash, commitment in zip(versioned_hashes, commitments):
13 assert versioned_hash == kzg_to_versioned_hash(commitment)
四、基本原理
4.1 On the path to sharding 在分片的路上
该 EIP 引入了 blob 事务,其格式与最终分片规范中预期存在的格式相同。通过允许它们最初扩展到每个槽 0.25 MB,这为汇总提供了一个临时但显着的扩展缓解,一个单独的费用市场允许费用非常低,同时该系统的使用受到限制。
rollup 缩放权宜之计的核心目标是提供临时的缩放缓解,而不会给 rollup 施加额外的开发负担以利用这种缓解。今天,汇总使用调用数据。将来,rollup 将别无选择,只能使用分片数据(也称为“blob”),因为分片数据会便宜得多。因此,rollups 不可避免地要对其处理数据的方式进行一次大升级。但我们能做的是确保 rollups 只需要升级一次。这立即意味着有两种可能的权宜之计:(i)降低现有调用数据的 gas 成本,以及(ii)提出将用于分片数据但尚未实际分片的格式。之前的EIP都是第(i)类的解决方案;该 EIP 是类别 (ii) 的解决方案。
设计这个 EIP 的主要权衡是现在实施更多与以后必须实施更多:我们是在实现完全分片的过程中实施 25% 的工作,还是 50%,还是 75%?
该 EIP 中已经完成的工作包括:
- 一种新的交易类型,其格式完全相同,需要存在于“全分片”中
- 全分片所需的所有执行层逻辑
- 全分片所需的所有执行/共识交叉验证逻辑
BeaconBlock
验证和数据可用性采样 blob 之间的层分离- 完全分片所需的大部分
BeaconBlock
逻辑 - blob 的自我调整的独立 gasprice
要实现完全分片,还有待完成的工作包括:
- 共识层
blob_kzgs
的低度扩展,允许二维采样 - 数据可用性采样的实际实现
- PBS(提议者/构建者分离),以避免要求单个验证者在一个时隙中处理 32 MB 的数据
- 每个验证器的保管证明或类似的协议内要求,以验证每个块中分片数据的特定部分
该 EIP 还为长期协议清理奠定了基础:
- 新增SSZ交易类型,开创了所有新交易类型都应该是SSZ的先例
- 它定义了
TransactionNetworkPayload
来分隔交易类型的网络和块编码 - 它的(更清洁的)gas 价格更新规则可以应用于主要基本费用
4.2 How rollups would function
rollups 不是将 rollup 块数据放在交易调用数据中,而是希望 rollup 块提交者将数据放入 blob 中。这保证了可用性(这是 rollup 所需要的),但比 calldata 便宜得多。汇总需要数据一次可用,足够长以确保诚实的参与者可以构建汇总状态,但不是永远可用。
Optimistic rollups 只需要在提交欺诈证明时实际提供基础数据。欺诈证明可以以更小的步骤验证转换,通过 calldata 一次最多加载 blob 的几个值。对于每个值,它将提供一个 KZG 证明,并使用点评估预编译来根据之前提交的版本化哈希验证该值,然后像今天一样对该数据执行欺诈证明验证。
ZK rollups将为其交易或状态增量数据提供两个承诺:blob 中的 kzg 和使用 ZK 汇总内部使用的任何证明系统的一些承诺。他们将使用等价协议的承诺证明,使用点评估预编译来证明 kzg(协议确保指向可用数据)和 ZK rollup 自己的承诺引用相同的数据。
4.3 Versioned hashes & precompile return data 版本化哈希和预编译返回数据
我们使用版本化哈希(而不是 kzgs)作为对执行层中 blob 的引用,以确保与未来更改的向前兼容性。例如,如果我们出于量子安全原因需要切换到 Merkle 树 + STARKs,那么我们将添加一个新版本,允许点评估预编译使用新格式。 Rollup 不必对其工作方式进行任何 EVM 级别的更改;排序者只需要在适当的时候切换到使用新的交易类型。
但是,点评估发生在有限域内,并且只有在场模已知的情况下才能明确定义。智能合约可以包含一个将承诺版本映射到模数的表,但这不允许智能合约考虑未来对未知模数的升级。通过允许访问 EVM 内部的模数,可以构建智能合约,以便它可以使用未来的承诺和证明,而无需升级。
为了不添加另一个预编译,我们直接从点评估预编译返回模数和多项式次数。然后它可以被调用者使用。它也是“免费”的,因为调用者可以忽略这部分返回值而不会产生额外成本——在可预见的未来保持可升级的系统现在可能会使用这条路线。
4.4 Data gasprice update rule 数据 gasprice 更新规则
数据 gasprice 更新规则旨在近似公式 data_gasprice = MIN_DATA_GASPRICE * e**(excess_data_gas / DATA_GASPRICE_UPDATE_FRACTION)
,其中 excess_data_gas
是该链相对于“目标”数量(每个块 TARGET_DATA_GAS_PER_BLOCK
)消耗的数据 gas 总量的“额外”量。与EIP-1559 一样,它是一个自我修正的公式:随着超额增加, data_gasprice
呈指数增长,减少使用并最终迫使超额回落。
逐块行为大致如下。如果 N
块消耗 X
数据gas,那么在 N+1
块中 excess_data_gas
增加 X -TARGET_DATA_GAS_PER_BLOCK
,所以 data_gasprice
块的 N+1
增加 e**((X - TARGET_DATA_GAS_PER_BLOCK) / DATA_GASPRICE_UPDATE_FRACTION)
。因此,它与现有的 EIP-1559 具有相似的效果,但在某种意义上更“稳定”,因为无论其分布如何,它都以相同的方式响应相同的总使用量。
参数 DATA_GASPRICE_UPDATE_FRACTION
控制 blob gas 价格的最大变化率。选择它的目标是每个块 e(TARGET_DATA_GAS_PER_BLOCK / DATA_GASPRICE_UPDATE_FRACTION) ≈ 1.125
的最大更改率。
4.5 吞吐量
选择 TARGET_DATA_GAS_PER_BLOCK
和 MAX_DATA_GAS_PER_BLOCK
的值对应于每个块 2 个 blob (0.25 MB) 和最多 4 个 blob (0.5 MB) 的目标。这些小的初始限制旨在最大程度地减少此 EIP 对网络造成的压力,并且随着网络在更大的块下展示可靠性,预计在未来的升级中会增加。
五、Backwards Compatibility 向后兼容
5.1 Blob non-accessibility Blob 不可访问
该 EIP 引入了一种交易类型,它具有不同的内存池版本 ( BlobTransactionNetworkWrapper
) 和执行负载版本 ( SignedBlobTransaction
),两者之间只能进行单向转换。 blob 在 BlobTransactionNetworkWrapper
中而不是在 SignedBlobTransaction
中;相反,他们进入了 BeaconBlockBody
。这意味着现在有一部分交易无法从 web3 API 访问。
六、Mempool issues 内存池问题
Blob 交易在 mempool 层具有较大的数据大小,这会带来 mempool DoS 风险,尽管这并不是前所未有的风险,因为这也适用于具有大量 calldata 的交易。
通过只广播 blob 交易的公告,接收节点将可以控制接收哪些交易和接收多少交易,从而允许它们将吞吐量限制在可接受的水平。 EIP-5793 将通过扩展 NewPooledTransactionHashes
公告消息以包括交易类型和大小来进一步细粒度地控制节点。
此外,我们建议在 mempool 交易替换规则中包含 1.1 倍的数据 gasprice 涨幅要求。
七、Test Cases 测试用例
待定
八、Security Considerations 安全考虑
该 EIP 最多将每个信标块的存储要求增加约 0.5 MB。这比今天区块的理论最大大小大 4 倍(30M gas / 每个调用数据字节 16 gas = 1.875M 字节),因此它不会大大增加最坏情况下的带宽。合并后,块时间预计是静态的,而不是不可预测的泊松分布,从而为大块的传播提供了保证的时间段。
这个 EIP 的持续负载远低于降低调用数据成本的替代方案,即使调用数据是有限的,因为没有现有的软件可以无限期地存储 blob,并且不期望它们需要存储多长时间执行负载。这使得更容易实施一项政策,即这些斑点应该在例如之后被删除。 30-60 天,与建议的(但尚未实施)执行负载历史的一年轮换时间相比,延迟要短得多。
九、版权所有
通过 CC0 放弃版权和相关权利。
十、Citation 引用
Vitalik Buterin ( @vbuterin)、Dankrad Feist ( @dankrad)、Diederik Loerakker ( @protolambda)、George Kadianakis ( @asn-d6)、Matt Garnett ( @lightclient)、Mofi Taiwo ( @Inphi)、Ansgar Dietrichs ( @adietrichs),“EIP-4844:分片 Blob 交易 [草案],”以太坊改进提案,第 1 期。 4844,2022 年 2 月。[在线连载]。可用:https://eips.ethereum.org/EIPS/eip-4844。