本文是 Bloxis 对 Metamask 在 Gitcoin 上举办的普遍化元交易竞赛的回应。由于我们计划将这项功能整合进我们即将面世的产品中,因而在该领域投入了大量时间与精力,也正因如此,我们由衷地希望能够参与到该领域的讨论中来。如果您对我的观点有任何异议,欢迎指出,我非常乐于与您讨论。
引言
只要你愿意花钱,你可以在一笔以太坊交易内执行任何(不超出 Gas 限制的)操作 —— 在一个几乎是完全去中心化的无国界区块链网络上。听起来非常酷不是么?事实也确实如此。但是!问题就在于,执行交易需要耗费 Gas,而 Gas 是用以太币来支付的,可是新用户谁有以太币啊亲?(什么?你说赛博朋克、囤币党、传销组织都有?拜托那都不是正常人)
DeFi、区块链游戏和 dApp 生态的未来依赖于入场玩家的规模。这意味着我们需要解决新玩家的上手问题,并降低安全操作的知识门槛。当下最主要的两个问题是:玩家上手时需要拥有一个数字钱包以及一些以太币 —— 这也是行业内许多人正在努力攻克的方向。
所谓元交易(Meta transaction),就是让用户用自己的密钥来签名发起交易,但不需要用户来支付交易手续费(即 Gas 费用),而由 “中继者(relay)” 来为 TA 支付 Gas 费。中继者作为发送方,将交易提交至网络,并支付 Gas 费用。交易的目标合约可以确定原始用户及其意图,并相应地处理合约的调用。
在元交易出现之前,完成上述服务需要预存资金,即先将以太币存入终端用户的账户中以促进交易。Uport 推出的 Lambda Sensui 是这方面的一个典型应用。
如果你想更加深入地了解元交易,可以阅读下面这篇文章,其作者是社区中的标杆人物 —— Austin Griffith。
以太坊元交易:降低以太坊普及的门槛(编者注:中译本见文末)
实现
在链上,实现元交易需要一种编码标准来编码原始发送者发出的交易数据 —— 以使目标合约能复原他们的签名和数据,而不被 msg.sender (即中继者)影响。然而,在链下,必须通过 API 或者通信协议才能将终端用户连接到中继者。且中继者本身还必须拥有一些以太币和基础设施来获取并转发元交易。
很适合所在具体场景的解决方案,比如在 Dai 的 ERC-20 代币合约中使用的 permit() 方法,就很优雅地解决了链上部分的难题 —— 这是一种需要终端用户直接提交的 approve 方法。此外,这一方法还实现了 EIP-712,该标准用于表示和签名供链上使用的消息数据。可以说, Dai 只需要支持这一种元交易就足够了,因为所有其它的用例(例如发送 Dai 和协调 DeFi 的操作)都可以利用这一解决方案来实现。
Gas Station Network 在此基础上更进了一步,它创建了一个基于智能合约的网络来协调中继者,并创建了合约方法,使得任何智能合约只要经过很小的改动就可以连接到该网络。所有的中继者都通过一个中继集合站(relay hub)来跟合约进行交互,而中继集合站本身也是一个合约,维护着中继者的注册表。其它合约向中继集合站支付以太币以激励中继者帮用户支付 Gas 费,而且中继者只有成功转发一笔交易之后才能获得该费用。
合约钱包
诸如 Dapper Labs、Gnosis 和 Argent 在内的许多组织正在推出合约钱包,旨在兼得用户体验和安全性。合约钱包将用户的资金托管于智能合约之中,而合约内发送交易和保护账户安全的功能可以远超以太坊协议本身的水平。
在用户使用合约钱包时,得益于集成在钱包内的联合签名功能,终端用户能够仅凭其设备上的密钥来签名交易、表明自己想要进行交易的意图,并将该交易发送给共同签名者或者转发方(通常是钱包服务的提供商),后者会转发该交易并承担 Gas 费用。比如,在 Dapper Labs 的产品 CoreWallet 上调用 invokeN…() 方法,实际发生的事情就像我们这里说的一样。
有了这一方法,账户内即使没有以太币也可以发出交易,而且不需要整个行业都接受同一个标准 —— 合约钱包可以像外部所有者账户(即由标准密钥对控制的普通用户地址,EOA)一样调用其它合约。此外,它还带来了一系列安全功能,诸如 Authereum 的交易防火墙 。
合约钱包带来的困扰
对于合约钱包和诸如以太坊域名服务(ENS)等创举,以太坊社区内的呼声很高。然而,随着许多用户在其所有的链上活动中都使用 ENS 域名作为身份(并且域名可读部分使用了真实姓名),巨大的隐私和安全问题开始显现。
此外,大家一边关注保护隐私的协议(比如 AZTEC,可用于隐蔽发送 token)(这些协议都依赖于私钥的所有和保管),另一边又忽略了私钥管理的难度和传统方案的隐私方面。
以太坊的钱包地址本身是 “伪匿名的”(因此具有一定的隐私性),但现在许多钱包软件提供商都在鼓励其用户获取 ENS 地址,却不告知他们这样做的后果。简言之,以太坊账户的交易记录(包括其投资组合及与 dApp 之间的互动记录)在链上是公开的,一个地址注册 ENS 域名之后,这些交易活动就跟这个 ENS 域名关联起来了。而用户所注册的 ENS 名字很可能是用户在其它服务或是现实生活中已经使用过的名字。这就是我对(供个人使用的)合约钱包地址绑定 ENS 持怀疑态度的主要原因。
然而,对于将 ENS 用于识别智能合约以防止诈骗,以及可以从透明操作中获益的组织使用 合约钱包/多重签名钱包,我举双手支持。
通过分层确定性钱包规则(Hierarchical Deterministic Wallet,简称 HD 钱包),用户可以用单个私钥来生成许多 EOA 地址,以保管不同的资产;使用多个地址分别操作,即便用户会使用 dApp、DeFi 协议并发起大量交易,仍然可以保持一定程度的匿名性。使用这套规则,用户甚至可以只持有一把私钥,然后为每一个要用到的 dApp 和 token 专门创建一个 EOA 地址用于交互。合约钱包虽然也能做到这一点,但因为 Gas 费用的存在,为用户使用的每一个 dApp/代币 创建一个合约钱包显得十分昂贵而不切实际。就算不考虑 Gas 费用,这样做也会造成网络拥堵。
假设以太坊生态内所有的用户对于与其交互的每一个 dApp 都有一个对应的合约钱包,从而既能利用元交易功能,又能保护自己的隐私 —— 届时智能合约采用一个通用的标准会更加合理。
迈向普遍化标准
无论你对基于合约及合约钱包的解决方案持有何种立场,目前都还没有一个确定的标准 —— 方法调用因合约而异,用于中继交易的系统也各不相同。Gas Station Network 已经做了许多基础性的工作,但依旧不够灵活。收款方合约可能不会支付 Gas 费。那么用户又如何使用其资产来为中继者支付报酬呢?我们该如何让另一方来为这笔交易支付 Gas 费呢?这些都是我们希望在普遍化元交易竞赛中解决的问题 —— 真正有价值的提案就是要面对这些问题。
基于我们提出的想法,我们的总法律顾问 Vincent 提出了一个想法:让 dApp 的开发者预先为用户存入资金用于支付 gas 费,用户用法币来交换;为避免 dApp 项目被认定为交易所,这些以太币只能在 dApp 内部使用。这就是我们希望未来的普遍化元交易标准能够支持的机制(这是我们一年前在研究另一件事情的时候想到的)。通过 acceptRelayedCall() 方法的一个自定义实现,Gas Station Network为这一想法提供了技术支持。
此外,让中继者能够批处理交易,是不是也能降低 Gas 费用?
无论提议来源于何处,我们都希望在其中看到如下内容:
· 有机制可支持替代性的中继者 报销/支付方式
· 适合在资产 dApp 和 DeFi 的智能合约(而非合约钱包标准)中运行的方法
· 支持在目标合约或用于中继交易的合约内进行交易批处理的机制
我们的提议概述
如你所见,我们强烈支持 Gas Station Network提出的想法。由 RelayRecipient 提供的 getSender() 和 getMessageData() 方法实现了一个统一的接口,使所有合约的元交易可以相互兼容。然而,这些方法在其当前的形式下还存在两个短板 —— 无法防止钓鱼网络和重放攻击。详情:
· ethereum/EIP-1613:Gas Station Network
· ethereum/EIP-712
为了防止钓鱼,EIP-712 提出了一个巧妙的解决方案 —— 域名分隔符。该对象包含了(用户签名交易要转发给的)合约的详细信息 —— 也就是地址,以及 dApp 的名称和版本。这些数据都包含在签名中,并和所有其它的交易数据一并以一种非常友好的方式呈现给终端用户。这样就限制了用户签名交易的方式,防止他们发起预期以外类型的交易(比如有意把交易发到错误的合约)。
支持 EIP-712 的合约需要验证域名分隔符与合约自身是否匹配,如果不匹配则回绝该交易。从下面列出的 Dai 合约的代码中,你可以看到这一点。我们希望将 EIP-712 整合进 getSender() 和 getMessageData() 中,以便能在每一次方法调用时执行这项检查 —— 一旦签名不匹配,就回绝该交易。你可以点击下面的链接查看该函数的代码。(很抱歉,文章内嵌代码的格式无法正常显示)
Makerdao/dss:https://github.com/makerdao/dss/blob/b1fdcfc9b2ab7961bf2ce7ab4008bfcec1c73a88/src/dai.sol#L114-L138
此外,在上面的代码中,你还可以看到 MakerDAO 自己用 nonce 属性实现了重放保护 —— 如果没有该属性,得到授权的参与方可以通过重放交易来耗尽用户的资金。
一笔元交易经常属于一组操作的一部分,而这些操作可能依赖于前面的交易。举个例子,在我们构建的概念验证案例中,用户可以将 xDai 存入 Compound 协议。而在主网上,这意味着我们要批准 cDai 合约访问用户资金,然后调用 mint() 方法将该资金存入贷款协议。
如果用户的账户并非合约钱包,完成这一过程就需要一系列操作:首先得通过交易调用 approve()/permit() 方法,然后等待该交易被打包之后再调用 mint() 方法。而有了合约钱包之后,就可以一步到位,通过调用一个特殊的合约方法来批处理这些交易。
如果有一种人人可用的普遍化元交易批处理方法,那肯定是一个重大的创新。这会大大简化链上的活动,就像上文举过的种种例子。此外,该方法还要允许用户可以同时签名和转发多笔互不相关的交易。
不仅如此,该方法还应该学学 MakerDAO 是如何使用 permit() 方法处理 nonce 和重放保护的。这样,用户就可以为互不相关的合约转发多条消息,同时不用操心网络中交易的提交顺序 —— 例如,当用户为某些现实生活服务支付多笔资产的时候。
然而,在某些情况下,用户可能希望往某一个合约发送多笔元交易。例如,某一个只持有一种资产的 EOA 想要批量发薪水的时候。一个批处理元交易的系统在设计的时候必须考虑到这一点,并确保保持了交易发送的顺序。
此外,批处理交易也让中继者可以通过用户签名的交易集中地收取手续费,也就是在一批交易中加入一笔转账给中继者的交易,方法也是可以定制化的。可以是将 ERC-20 代币转给中继者,或者让用户的目标合约来支付该费用。
中继集合器应当支持交易批处理,并通过 sendRawTransactionSet() 方法来补充 sendRawTransaction() ,以便同一用户发送多笔交易。sendRawTransactionSetPlural() 方法也应该加以利用,以方便中继对来自多个用户的交易进行批处理,从而最小化 Gas 费用。
如果可以通过交易集中的一笔交易来为其提供资金,那么只需要对中继集合器现在的工作流程做很小的改动就可以实现上述操作,中继者可以将 gasPrice 和费用参数acceptRelayCall() 都设置为 0。中继者可以从交易集中不断提取出交易并按标准来处理,直至完成。
我们面临的真正挑战是如何创建一种标准将交易打包进交易集,以及解包并执行交易(同时确保交易的原子性)。如果交易的原子性无法得到保证,中继者就可以通过只转发付费的交易来蒙混过关,而无视集合中的其它交易。这才是工作的重头!对于如何实现这一点,我目前还毫无头绪,但请继续关注我的下一篇文章!希望我可以将这一切变成可行的方案来参加竞赛。
总结
在我们看来,合约钱包有用不假,但有点名过其实了,而且因为合约钱包常常鼓励用户做那些便利日常使用的措施,还可能会危害到链上的隐私。这会极大地影响我们希望在以太坊生态内实现普遍化元交易的方式。
我们希望在 Gas Station Network的基础上更进一步,以支持 EIP-712 和批量交易。这也意味着对 RelayHub 和 RelayRecipient 进行修改,并创建一个新标准,支持签名交易集合并保证交易的原子性。