摘要

TransactionType || TransactionPayload 是一个有效的交易格式,TransactionType || ReceiptPayload 是一个有效的交易收据格式,其中:

  • TransactionType 标识交易格式
  • *Payload 是交易/收据内容,由未来的EIP定义

动机

过去,当我们想要添加新的交易类型时,必须确保它们与所有其他交易向后兼容,这意味着只能根据编码后的有效载荷来区分它们,并且不可能存在匹配两种类型的交易。这在EIP-155中可以看到,新值被位打包到其中一个编码字段中

当前有多个提案正在讨论中,它们定义了新的交易类型,例如:

  • 允许EOA账户直接在其上下文中执行代码的交易
  • 允许msg.sender以外的人支付gas的交易
  • 与第1层多签交易相关的提案

所有这些都需要以相互兼容的方式定义,这很快成为EIP作者和客户端的负担,因为他们现在必须遵循复杂的规则来区分交易类型

通过引入信封交易类型,我们只需要确保与现有交易的向后兼容性,之后我们只需要解决确保TransactionType之间没有编号冲突这个更简单的问题


规范

定义

  • || 是字节/字节数组连接运算符

交易

FORK_BLOCK_NUMBER开始,区块头中的交易根必须patriciaTrie(rlp(Index) => Transaction)的根哈希,其中:

  • Index 是此交易在区块中的索引
  • Transaction 是以下之一:
    • TransactionType || TransactionPayload
    • LegacyTransaction
  • TransactionType 是一个在0到0x7f之间的无符号8位正整数,表示交易类型
  • TransactionPayload 是一个不透明的字节数组,其解释取决于TransactionType并由未来的EIP定义
  • LegacyTransactionrlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])

所有未来交易类型的签名应该将TransactionType作为签名数据的第一个字节。这样我们就不必担心一种交易类型的签名被用作另一种交易类型的签名

收据

FORK_BLOCK_NUMBER开始,区块头中的收据根必须patriciaTrie(rlp(Index) => Receipt)的根哈希,其中:

  • Index 是此收据对应的交易在区块中的索引
  • Receipt 是以下之一:
    • TransactionType || ReceiptPayload
    • LegacyReceipt
  • TransactionType 是一个在0到0x7f之间的无符号8位正整数,表示交易类型
  • ReceiptPayload 是一个不透明的字节数组,其解释取决于TransactionType并由未来的EIP定义
  • LegacyReceiptrlp([status, cumulativeGasUsed, logsBloom, logs])

收据的TransactionType必须与具有匹配Index的交易的TransactionType相匹配

原理阐述

TransactionType只到0x7f

在可预见的未来,0x7f已经足够,并且为扩展范围留下了多种选择,例如使用高位作为延续位。这也防止了与遗留交易类型的冲突,遗留交易类型总是以>= 0xc0的字节开头

签名对第一个字节的处理

虽然强烈建议所有未来交易签署第一个字节以确保没有签名重用问题,但作者承认这可能并不总是有意义或可能。一个不可能的例子是与遗留签名方案签名兼容的封装遗留交易。另一种潜在情况是交易没有传统意义上的签名,而是有其他确定有效性的机制

TransactionType选择算法

关于在本标准中定义TransactionType标识符分配/选择算法的讨论。虽然拥有标准化的分配机制会很好,但在编写本标准时并没有强烈需求,因此被认为超出了范围。如果需要,未来的EIP可能会引入TransactionType标识符分配标准

不透明的字节数组而不是RLP数组

通过将第二个字节设置为不透明字节,而不是RLP(或其他编码)列表,我们可以支持未来不同的交易有效载荷编码格式,如SSZ、LEB128或固定宽度格式

ORIGIN和CALLER

关于使ORIGIN和CALLER操作码依赖于交易类型的讨论,以便每种交易类型可以定义这些操作码返回什么。然而,有人希望使交易类型对合约不透明,以阻止合约以不同方式处理不同类型的交易。还有人担心与现有合约的向后兼容性,这些合约对ORIGIN和CALLER操作码有假设。展望未来,我们将假设所有交易类型都有一个合理代表第一个EVM帧CALLER的地址,并且ORIGIN在所有情况下都是相同的地址。如果交易类型需要向合约提供额外信息,它们将需要新的操作码

  • CALLER:当前 EVM 调用帧的直接调用者地址
    • 如果合约A调用合约B,B内部的 CALLER 是A
  • ORIGIN:发起交易的原始外部账户(EOA)地址
    • 不管交易经过多少层合约调用,ORIGIN 都是发起交易的EOA地址

向后兼容性

客户端可以通过查看第一个字节来区分遗留交易和类型化交易:

  • 如果它以[0, 0x7f]范围内的值开头,那么它是新的交易类型
  • 如果它以[0xc0, 0xfe]范围内的值开头,那么它是遗留交易类型

0xff对于RLP编码的交易来说是不现实的,因此它被保留作为未来使用的扩展哨兵值

安全考量

在设计新的2718交易类型时,强烈建议将交易类型作为签名有效载荷的第一个字节。如果未能这样做,您的交易可能与另一种类型的交易签名兼容,这可能为用户引入安全漏洞