Alert Source Discuss
🚧 Stagnant Standards Track: ERC

ERC-2770: 元交易转发器合约

Authors Alex Forshtat (@forshtat), Dror Tirosh (@drortirosh)
Created 2020-07-01
Discussion Link https://ethereum-magicians.org/t/erc-2770-meta-transactions-forwarder-contract/5391
Requires EIP-712, EIP-2771

简述

用于可扩展的元交易转发的标准合约接口。

摘要

本提案定义了一个可扩展的转发器的外部 API,其职责是在链上验证交易签名,并将签名者暴露给目标合约,该合约有望适应所有用例。 转发请求的 ERC-712 结构可以扩展,允许钱包显示可读数据,即使对于在转发器合约部署期间未知的类型。

动机

人们越来越有兴趣使以太坊合约能够接受来自没有 ETH 支付 gas 费用的外部账户的调用。

这可以通过元交易来实现,元交易是由一个外部账户首先签名作为普通数据,然后由另一个账户将其包装到以太坊交易中的交易。

msg.sender 是一个交易参数,合约可以检查该参数以确定谁签署了交易。该参数的完整性由以太坊 EVM 保证,但对于元交易,验证 msg.sender 是不够的,并且还必须恢复签名者地址。

此处描述的转发器合约允许多个 Gas Relays 和 Relay Recipient 合约依赖于签名验证代码的单个实例,从而提高任何参与的元交易框架的可靠性和安全性,并避免链上代码重复。

规范

转发器合约通过接受带签名的类型化数据及其 ERC-712 签名来运行,对传入数据执行签名验证,将签名者地址附加到数据字段并执行对目标的调用。

转发器数据类型注册

请求结构必须按照以下确切顺序包含以下字段:

struct ForwardRequest {
   address from;
   address to;
   uint256 value;
   uint256 gas;
   uint256 nonce;
   bytes data;
   uint256 validUntil;
}

from - 发出请求的外部账户
to - 目标地址,通常是智能合约
value - 要转移到目标的以太币数量
gas - 为执行设置的 gas 限制量
nonce - 链上跟踪的交易 nonce
data - 要发送到目标的数据
validUntil - 可以在其中转发请求的最高区块号,如果请求有效期不受时间限制,则为 0

如果需要,请求结构可以包含任何其他字段,包括嵌套结构。 为了使转发器能够强制执行此结构的字段名称,仅允许注册类型。

注册必须通过调用以下方法提前执行:

function registerRequestType(string typeName, string typeSuffix)

typeName - 要注册的类型的名称
typeSuffix - 类型的 ERC-712 兼容描述

例如,在调用

registerRequestType("ExtendedRequest", "uint256 x,bytes z,ExtraData extraData)ExtraData(uint256 a,uint256 b,uint256 c)")

之后,以下 ERC-712 类型将注册到转发器:

/* primary type */
struct ExtendedRequest {
   address from;
   address to;
   uint256 value;
   uint256 gas;
   uint256 nonce;
   bytes data;
   uint256 validUntil;
   uint256 x;
   bytes z;
   ExtraData extraData;
}

/* subtype */
struct ExtraData {
   uint256 a;
   uint256 b;
   uint256 c;
}

签名验证

以下方法对请求执行 ERC-712 签名检查:

function verify(
   ForwardRequest forwardRequest,
   bytes32 domainSeparator,
   bytes32 requestTypeHash,
   bytes suffixData,
   bytes signature
) view;

forwardRequest - ForwardRequest 结构的实例 domainSeparator - 调用者提供的域分隔符,以防止跨 dapps 重用签名(请参阅 ERC-712) requestTypeHash - 注册的中继请求类型的哈希 suffixData - 请求结构其余部分的 RLP 编码 signature - forwardRequestsuffixData 连接的 ERC-712 签名

命令执行

为了使转发器执行操作,需要调用以下方法:

function execute(
   ForwardRequest forwardRequest,
   bytes32 domainSeparator,
   bytes32 requestTypeHash,
   bytes suffixData,
   bytes signature
)
public
payable
returns (
   bool success,
   bytes memory ret
)

在内部执行 “verify”,如果成功,则执行以下调用:

bytes memory data = abi.encodePacked(forwardRequest.data, forwardRequest.from);
...
(success, ret) = forwardRequest.to.call{gas: forwardRequest.gas, value: forwardRequest.value}(data);

无论内部调用成功还是回退,nonce 都会递增,使签名无效并防止重放请求。

请注意,gas 参数的行为符合 EVM 规则,特别是 EIP-150。转发器在内部验证是否有足够的 gas 用于内部调用。如果 forwardRequest 指定了非零值,则会保留额外的 40000 gas,以防内部调用恢复或有剩余的以太币,因此需要从 Forwarder 转移价值:

uint gasForTransfer = 0;
if ( req.value != 0 ) {
   gasForTransfer = 40000; // buffer in case we need to move Ether after the transaction.
}
...
require(gasleft()*63/64 >= req.gas + gasForTransfer, "FWD: insufficient gas");

如果转发器中没有足够的 value,则内部调用的执行将失败。
请注意,如果内部调用最终将以太币转移到最初没有 valueForwarder,则在交易完成后,此以太币将保留在 Forwarder 内部。

ERC-712 和 ‘suffixData’ 参数

suffixData 字段必须提供有效的 ERC-712 类型化数据的“尾部”。 例如,为了对 ExtendedRequest 结构进行签名,数据将是以下块的串联:

  • forwardRequest 字段将按原样进行 RLP 编码,并且可变长度的 data 字段将被哈希
  • uint256 x 将完全按原样附加
  • bytes z 将首先被哈希
  • ExtraData extraData 将被哈希为类型化数据

因此,有效的 suffixData 计算如下:

function calculateSuffixData(ExtendedRequest request) internal pure returns (bytes) {
    return abi.encode(request.x, keccak256(request.z), hashExtraData(request.extraData));
}

function hashExtraData(ExtraData extraData) internal pure returns (bytes32) {
    return keccak256(abi.encode(
            keccak256("ExtraData(uint256 a,uint256 b,uint256 c)"),
            extraData.a,
            extraData.b,
            extraData.c
        ));
}

接受转发的调用

为了支持通过转发器执行的调用,接收者合约必须从 msg.data 的最后 20 个字节读取签名者地址,如 ERC-2771 中所述。

理由

进一步依赖 msg.sender 通过其外部账户对最终用户进行身份验证正在将以太坊 dapp 生态系统带入死胡同。

用户在可以与任何合约交互之前需要拥有以太币的需求使智能合约的绝大部分用例不可行, 这反过来又限制了大规模采用并强制执行这种恶性循环。

validUntil 字段使用区块号而不是时间戳,以便更好地与其他基于区块的常见计时器进行精确和集成。

安全注意事项

所有引入对转发的请求支持的合约,从而授权此合约在任何帐户下执行任何操作。 至关重要的是,此合约没有漏洞或中心化问题。

版权

版权和相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Alex Forshtat (@forshtat), Dror Tirosh (@drortirosh), "ERC-2770: 元交易转发器合约 [DRAFT]," Ethereum Improvement Proposals, no. 2770, July 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2770.