ERC-1077: 用于合约调用的 Gas 中继
Authors | Alex Van de Sande <avsa@ethereum.org>, Ricardo Guilherme Schmidt (@3esmit) |
---|---|
Created | 2018-05-04 |
Discussion Link | https://ethereum-magicians.org/t/erc1077-and-1078-the-magic-of-executable-signed-messages-to-login-and-do-actions/351 |
Requires | EIP-20, EIP-191, EIP-1271, EIP-1344 |
Table of Contents
简单概要
智能合约顶部的 gas 抽象的标准接口。
允许用户提供 EIP-20 代币来支付调用中使用的 gas。
抽象
DApp 采用的主要障碍是在链上执行操作需要多种代币。允许用户签署消息以显示执行意图,但允许第三方中继器执行它们可以规避这个问题,虽然以太坊交易始终需要 ETH,但智能合约可以接受 EIP-191 签名,并将付款激励转发给不可信的第三方,并附带 ETH 来执行交易。
动机
标准化它们的通用格式,以及用户允许用代币支付交易的方式,为应用程序开发者提供了很大的灵活性,并可能成为应用程序用户与区块链交互的主要方式。
规范
方法
executeGasRelay
使用当前的 lastNonce()
执行 _execData
,并向 msg.sender
支付指定 _gasToken
中使用的 gas。
function executeGasRelay(bytes calldata _execData, uint256 _gasPrice, uint256 _gasLimit, address _gasToken, address _gasRelayer, bytes calldata _signature) external;
executeGasRelayMsg
返回用于签署消息的 executeGasRelay
消息。
function executeGasRelayMsg(uint256 _nonce, bytes memory _execData, uint256 _gasPrice, uint256 _gasLimit, address _gasToken, address _gasRelayer) public pure returns (bytes memory);
executeGasRelayERC191Msg
返回用于签署消息和验证执行的 executeGasRelayMsg
的 EIP-191。
function executeGasRelayERC191Msg(uint256 _nonce, bytes memory _execData, uint256 _gasPrice, uint256 _gasLimit, address _gasToken, address _gasRelayer) public view returns (bytes memory);
lastNonce
返回 gas 中继消息的当前 nonce。
function lastNonce() public returns (uint nonce);
签名消息
签名消息需要以下字段:
- Nonce:一个 nonce 或时间戳;
- 执行数据:要由帐户合约执行的字节码;
- Gas 价格:gas 价格(以所选代币支付);
- Gas 限制:为中继执行保留的 gas;
- Gas 代币:将用于支付 gas 的代币(以 0 表示以太币);
- Gas 中继器:本次调用的 gas 退款受益人(以 0 表示
block.coinbase
)。
签署消息
该消息必须按照 EIP-191 标准进行签名,并且被调用的合约必须也实现 EIP-1271,该标准必须验证签名消息。
消息必须由执行的帐户合约的所有者签名。如果所有者是合约,则它必须实现 EIP-1271 接口并将验证转发给它。
为了符合要求,交易必须请求签署一个 “messageHash”,它是多个字段的串联。
这些字段必须按以下方法构造:
第一个和第二个字段是为了使其符合 EIP-191。以 byte(0x19)
开始一个交易确保签名的数据不是一个 有效的以太坊交易。第二个参数是一个版本控制字节。第三个是验证器地址(帐户合约地址),根据 EIP-191 的版本 0。剩余的参数是 gas 中继的应用程序特定数据:每个 EIP-1344 的 chainID,执行 nonce,执行数据,约定的 gas 价格,gas 中继调用的 gas 限制,用于支付的 gas 代币以及授权接收奖励的 gas 中继器。
EIP-191 消息必须如下构造:
keccak256(
abi.encodePacked(
byte(0x19), //ERC-191 - the initial 0x19 byte
byte(0x0), //ERC-191 - the version byte
address(this), //ERC-191 - version data (validator address)
chainID,
bytes4(
keccak256("executeGasRelay(uint256,bytes,uint256,uint256,address,address)")
),
_nonce,
_execData,
_gasPrice,
_gasLimit,
_gasToken,
_gasRelayer
)
)
理由
用户痛点:
- 用户不想考虑以太币
- 用户不想考虑备份私钥或助记词
- 用户希望能够使用他们在系统上已有的东西来支付交易,无论是 apple pay、xbox 积分甚至是信用卡
- 用户不想在每次移动时都签署新的交易
- 用户不想下载应用程序/扩展程序(至少在桌面上)来连接到他们的应用程序
应用程序开发者痛点:
- 许多应用程序使用他们自己的代币,并且更喜欢使用这些代币作为主要的会计单位
- 应用程序希望能够在多个平台上拥有应用程序,而无需在设备之间共享私钥或花费交易成本在它们之间转移资金
- 代币开发者希望能够让他们的用户能够转移资金并以代币支付费用
- 虽然该系统为矿工提供了费用和激励,但是钱包开发者(或其他发起许多交易的应用程序)没有固有的商业模式
使用签名消息,特别是与持有资金的帐户合约以及可以代表其签名的多个一次性无以太币密钥相结合,可以解决许多这些痛点。
多个签名
具有相同参数的多个签名交易可以通过此功能同时执行,方法是在 messageSignatures
字段中传递所有签名。该字段将签名拆分为多个 72 个字符的单独签名并评估每一个签名。这用于一个动作可能需要多个方批准的情况下,在单个交易中。
如果需要多个签名,则所有签名应按帐户排序,并且帐户合约应在 EIP-1271 接口上本地 (JUMP
) 实现签名检查,该接口可能会将 EIP-1271 签名检查转发 (STATIC_CALL
) 到所有者合约.
跟踪 nonces:
请注意,executeGasRelay
函数不接受 _nonce
作为参数。该合约知道当前的 nonce 是什么,并且只能按顺序执行交易,因此没有理由
Nonces 的工作方式与普通的以太坊交易类似:只有当交易与最后一个 nonce + 1 匹配时才能执行,并且一旦发生交易,lastNonce
将更新为当前的 nonce。这可以防止交易无序执行或多次执行。
合约可以接受没有 nonce 的交易(nonce = 0)。然后,合约必须保留交易的完整哈希,以防止其被重放。这将允许合约具有更大的灵活性,因为您可以签署一个可以乱序或根本不执行的交易,但是它为每个交易使用了更多的内存。它可以用于,例如,用户想要在将来安排的交易,但无法知道其将来的 nonce,或者为状态通道合约进行的交易,这些合约不能保证被执行或仅在发生争议时执行。
执行交易
在签名验证之后,_execBytes
的评估取决于帐户合约的实现,钱包有责任正确使用帐户合约及其 gas 中继方法。
一种常见的模式是公开只能由合约本身调用的接口。_execBytes
可以完全以这种方式转发调用,例如:address(this).call.gas(_gasLimit)(_execData);
其中 _execData
可以调用合约本身的任何方法,例如:
call(address to, uint256 value, bytes data)
:允许执行任何类型的以太坊调用;create(uint256 value, bytes deployData)
:允许创建合约create2(uint256 value, bytes32 salt, bytes deployData)
:允许创建具有确定性地址的合约approveAndCall(address token, address to, uint256 value, bytes data)
:允许安全地批准和调用 ERC20 代币。delegatecall(address codeBase, bytes data)
:允许执行存储在其他合约上的代码changeOwner(address newOwner)
:一些帐户合约可能允许更改所有者foo(bytes bar)
:一些帐户合约可能具有任何格式的自定义方法。
帐户合约的标准化不在本 ERC 的范围内,此处仅用于说明可能的实现。
使用自调用来评估 _execBytes
不是强制性的,具体取决于帐户合约的逻辑,可以在本地完成评估。
Gas 记账和退款
实现合约必须跟踪花费的 gas。一种方法是在函数的开头首先调用 gasLeft()
,然后在执行所需的操作之后比较差异。
然后,合约将以 gasSpent * gasPrice
的价值将代币转移(如果 tokenAddress
为 nil,则转移以太币)到 _gasRelayer
,这是部署消息的帐户。
如果 _gasRelayer
为零,则资金必须转到 block.coinbase
。
如果没有足够的资金,或者总数超过 gasLimit
,则交易必须还原。
如果执行的交易在内部失败,则仍应更新 nonces 并且需要支付 gas。
合约没有义务支持他们不想要的以太币或任何其他代币,并且可以实现为仅接受以他们选择的几种代币进行退款。
使用示例
该方案为交互以及商业模式的不同实验开辟了巨大的可能性:
- 应用程序可以为其用户创建保存实际资金的个人身份合约,然后为他们登录的每个设备创建一个不同的私钥。其他应用程序可以使用相同的身份,只需请求添加授权的公钥来管理设备,这样,如果一个密钥丢失,则不会丢失任何以太币。
- 一个应用程序可以创建自己的代币,并且只向其用户收取其内部货币的任何以太坊交易费用。货币单位可以四舍五入,因此看起来更像实际的交易量:标准交易始终花费 1 个代币,非常复杂的交易恰好花费 2 个代币等。由于应用程序是交易的发行者,因此他们可以进行自己的 Sybil 验证,并向新用户免费提供一定数量的货币单位,以帮助他们入门。
- 一家游戏公司创建具有传统月度订阅的游戏,可以通过信用卡或特定于平台的微交易进行订阅。私钥永远不会离开设备,并且不保留以太币,只有公共帐户才会发送给公司。然后,游戏在设备上以 gas 价格 0 签署交易,将其发送给游戏公司,该公司检查谁是活跃的订阅者并批量处理所有交易并自己支付以太币。如果公司破产,游戏玩家可以自己设置类似的订阅系统或只是提高 gas 价格。最终结果是一个基于以太坊的游戏,游戏玩家可以通过花费 apple、google 或 xbox 积分来玩游戏。
- 创建一个标准代币,该代币不需要其用户拥有以太币,而是允许通过用代币支付来转移代币。创建一个钱包,该钱包签署消息并通过 whisper 将其发送到网络,其他节点可以在该网络上竞争下载可用的交易,检查当前的 gas 价格,并选择那些支付足够的代币来支付成本的交易。结果是一个终端用户永远不需要保留任何以太币并且可以用代币本身支付费用的代币。
- 创建一个 DAO,其中包含其员工的帐户列表。员工永远不需要拥有以太币,而是签署消息,将其通过 whisper 发送到去中心化的中继器列表,然后中继器部署交易。然后,DAO 合约检查交易是否有效,并将以太币发送给部署者。员工有动机不要过度使用公司的资源,因为他们是可以识别的。结果是 DAO 的用户不需要保留以太币,并且合约最终会支付自己的 gas 使用费用。
向后兼容性
向后兼容性没有问题,但是对于将来的升级,由于 _execData
包含由帐户合约评估的任意数据,因此合约有责任正确处理此数据,因此合约可以使用当前的接口进行 gas 中继任何行为。
测试用例
待定
实现
可以在 Status.im account-contracts 仓库 中找到此类合约的初始实现
另一个版本作为 Gnosis Safe 变体实现:https://github.com/status-im/safe-contracts
类似的实现
使用签名消息作为可执行意图的想法已经存在了一段时间,许多其他项目都在采用类似的方法,这使其成为保证互操作性的标准的绝佳候选者:
- EIP-877 试图做同样的事情,但协议有所更改
- Status
- Aragon (这可能不是显示他们在该领域工作的最佳链接)
- 预授权操作的代币标准函数
- 代币标准扩展 865
- Iuri Matias:交易中继
- uPort:元交易
- uPort:安全身份
- Gnosis safe contracts
Swarm city 使用类似的以太币交易的提议,称为 Gas Station Service,但它是一种不同的方法。不是使用签名消息,而是在无以太币帐户上签署一个传统的以太坊交易,然后将该交易发送给一个服务,该服务立即发送所需的确切数量的以太币,然后发布该交易。
安全注意事项
交易的部署者(中继器)应该能够调用不受信任的合约,这不能保证他们与之交互的合约正确实现了该标准,并且他们将获得 gas 的报销。为防止被不良实现所愚弄,中继器必须评估交易的结果,并且仅包括/签署具有所需结果的交易。
维护他们与之交互的合约的私人声誉,以及跟踪他们愿意部署交易的哪些代币以及哪些 gasPrice
也是中继器的兴趣所在。
版权
通过 CC0 放弃版权及相关权利。
参考文献
Citation
Please cite this document as:
Alex Van de Sande <avsa@ethereum.org>, Ricardo Guilherme Schmidt (@3esmit), "ERC-1077: 用于合约调用的 Gas 中继 [DRAFT]," Ethereum Improvement Proposals, no. 1077, May 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1077.