作者:Alfred Lu,imToken Labs 实习生
本文受众:区块链初学者
本文接续前篇:抽象账户介绍(一):以太坊的账户现况
在之前我们提到的那些原生账户的问题中,其实都在于我们的「交易」被绑在以太坊的原生协议上,因此抽象账户是一种将交易验证与执行过程从底层拉到合约层的设计。
所以这个新的账户不只是一个「新形式的账户」而已,而是连验证交易、执行交易的过程都和过往不同了。
目录
这篇文章接续前文的账户介绍,由浅入深继续介绍:
- 什么是抽象账户(Account Abstraction,AA)
- 抽象账户为何重要:与原生合约账户(Contract Account,CA)的差别
- 未来账户:使用和开发体验
这边的抽象账户介绍基本上都以 EIP-4337 的提案为主,不过附注的参考文章与延伸阅读可能会有一些 EIP-2938 的内容,两者都是抽象账户的提案,只是设计上略有不同,大家有兴趣可以再自行深入探讨。
什么是抽象账户?
深入的说,过去节点会负责验证交易、收取手续费、使用 ECDSA 验证签名,抽象账户把这些东西拉到智能合约中变成合约代码的一部分,就能够对其进行编程。
所以,实现抽象账户有个目标:「有一个完善的,能正确验证交易,且将用户交易打包上链的智能合约系统设计」。听起来是不是非常拗口,我们可以继续看下去。
抽象账户下的交易如何运作
1. 用户会签名完一笔 Signed Message(这里我们将 EOA 签送一笔交易的行为改称为UserOperation),这个签完的交易(UserOperation)里面可能有数个 Calls,每个 Call 都是一个我们过往认识的交易行为(如:与合约函数互动、汇款给别人等)。
重点一:签完的交易叫做 UserOperation,里面可能有多个 Calls,在 EOA 的时候这些 Call 都必须是一笔一笔的交易,但现在他们可以被包装在一个交易(UserOperation)里面。
2. 用户把这个签完的交易(UserOperation)送到 UserOperation MemPool 之中,它类似当今的 Tx MemPool。进入 MemPool 的 UserOperation 就是在 Pending 的状态。
重点二:用户可以选择他要把自己的交易(UserOperation)送到谁运作的 MemPool,有可能是钱包商或矿工自己有的公开网络 MemPool。这个部分会与现在的 MemPool 并存。
3. Bundler 会从 MemPool 中挑选它喜欢的 UserOperation 们,捆绑成一笔 Bundle Transaction(这就是我们现在就在使用的 Ethereum 交易)。这个 Bundle Transaction 会去触发 Entry Point Smart Contract 的函数 handleOps。
重点三:Bundler 有可能是矿工,或是能作为 User 和 Miner 之间的中介人。他负责监听 MemPool 中新加入的 UserOperation,因为只要捆绑(Bundle)这些交易就可以从中收取手续费,因此他们有动机去替我们捆绑送给 Entry Point Smart Contract(以下都称 EntryPoint)。
4. EntryPoint 会确认每个用户都有他们的 Wallet Contract ,如果没有就帮用户部署一个,作为用户在这里将要使用的 Wallet Contract。这个 Wallet Contract 负责针对该用户送上来的 UserOperation 验证交易、支付手续费、执行交易。
重点四:EntryPoint 是一个早就被部署的合约,它将在未来被相关的 AA Transaction 呼叫使用,作为 AA 交易的统一进入点。
过往的交易中,节点会去检验 nonce 的合法性,确认签名合法,并还原出签名地址之后查看是否有足够的 balance 以及 gas fees。在 AA 中这些交易验证都需要移动到 Wallet Contract 里。
5. EntryPoint 会呼叫 Wallet Contract 上的函数,依序为:
- 验证(__validate__()):EntryPoint 呼叫了此函数之后,Wallet Contract 会检查交易是否合法,包含 nonce 是否正确,以及任何这个 Account(Wallet Contract)想要做的检查(如:多签、不同的 Sig. Algorithms 等)。
- 在确认验证通过之后会向 Wallet 收取手续费(直接汇款,Wallet Contract 会检验调用者是 EntryPoint)。
- 执行(__execute__()):EntryPoint 呼叫了此函数之后,Wallet 就真的去执行这笔UserOperation 要执行的内容(Calls 们),这些 Call 可能会是与合约互动等行为或汇款等。
抽象账户为何重要:与原生合约账户的差别
如果要说抽象账户与原生合约账户哪里有差别,不如说抽象账户能够继承所有原生合约账户的特性与优点,并能克服它的缺点,同时再优化更多交易形式的弹性。
既然大家已经知道什么是 AA 了,我们就可以先回过头看当初列出的六大问题表格作为综述,并同时加上 AA 抽象账户的新列:
首先是「问题(1)- 私钥保管问题」与「问题(4)- 多签授权交易」:AA 依然具有 Contract Account 的特性,能够实现多签或 Social Recovery 等特性,有机会缓解私钥保存问题。
私钥安全是一个很大的议题,这些特性非万能解也非唯一解,就是作为举例而已。
问题(2)- 可使用其他签名
我们在上一个小节讲述 AA 运作流程的时候,就有提到执行交易验证的地方是 EntryPoint 部署的 Wallet 中的验证(__validate__())函数。
在 AA 中,这里的验证机制就可以使用其他算法来验证交易签名,例如:Multisig verification、Schnorr sigs、BLS sigs、Quantum-resistant sigs (eg Lamport, Winternitz) 等。
这个部份取决于部署 EntryPoint 的服务商希望用户送上来的交易是以何种方式被验证的。
问题(3)- 可以其他方式支付交易手续费
更深入一点探讨以太坊原本的交易协议,在交易被处理跟验证之前,我们就必须先注入一定数量的 Ether 作为 Gas Fee。所以才会说我们有一个空的新账户,它没办法做任何事情,必须先被注入 Ether 才能使用(与合约互动)。
但现在在 AA 的情况,我们可以透过 Paymaster 的设计来以其他方式支付手续费。例如中心化的 Paymaster 可以收取信用卡,去中心化的 Paymaster 能够在合约中收取 ERC20 代币作为收费手段。最后 Paymaster 会替我们支付 Ether 给 EntryPoint 去真正执行交易。
在 EntryPoint 呼叫 Wallet Contract 上的函数时,能够插入一个角色叫做 Paymasters,Paymaster 可以储存每个用户的资产,然后使用 Ether 代替 Wallet Contract 支付手续费(验证与执行仍然是 Wallet 做)。
举例来说用户已经使用信用卡或 ERC20 等方式向 Paymaster 服务商支付手续费了,Paymaster 的合约中就会记上相对应的记录;且同时用户在送上 UserOperation 时会在交易中附带一些特别的参数,白话的说就是告诉 EntryPoint:「这笔交易的手续费请你跟某某某 Paymaster 拿,不是跟 Wallet Contract 拿。」。
待 EntryPoint 呼叫 Wallet Contract 的验证,确认此 UserOperation 合法后,就会去跟 Paymaster 讨 Ether 作为手续费,拿到钱之后才接着做执行的相关动作。
问题(5)- 可作为交易发起者
问题(3)- 以其他方式支付交易手续费的另外一个例子是 Tornado Cash(TC),我拉到第五点:「Contract Account 无法作为交易发起者,但 AA 可以」来讲。
AA 不仅可以收取其他形式的费用,还可以从我们提款的费用扣掉一部分作为手续费。并且交易成立关键在于 EntryPoint 收取 UserOperation 的时候,只需要用户送上「已签的交易对象(UserOperation)」,而非当今的送出一笔交易(Transaction)给节点验证打包。这样的设计使得 AA 能够作为交易发起者。
Image by Vitalik Buterin on Ethereum Account Abstraction
在 TC 的运作中,存款者向 TC Contract 存入资金,而提款者(去唤起合约函数示意我要提款的那个人)则须要先持有一定 Ether 来支付提款这个行为的交易手续费,才能将钱提出来。
问题就在于,我们如果用任何一个 Funded Account 注入提款者账户,都会在链上被串联起关系,隐私也就荡然无存了。现行的解决方法是有一个第三方的 Relayer 来接收 Off-Chain Message ,并且进行验证,成功后提交交易对象给 TC Contract(Relayer 同时获得 Gas refund 和 fee),最后 TC Contract 会汇出款项。
在 AA 的设计下:我们能够将 Bundler 当作 relayer 使用(不需要特别跑一个 TC relayer 或 relayer 网络):Bundler 打包我们汇出 TC 的 UserOperation,UserOperation 里直接使用汇出的 Ether 或代币来支付手续费给 Bundler。因此我们的提款者就不需要事先被注入资金,就能够呼叫合约函数。
以上详情可见:EIP-2938 简介的 Example 2 — Privacy Solution: Tornado.Cash。
问题(6)- 一笔交易可包含多个呼叫
用户们送给 Bundler 打包的交易对象以 UserOperation 的形式组成,UserOperation 作为以多个 Calls 捆绑而成的一个对象,在此可以将一连串的 operations(不同的 calls 们)包装成一个不可分割的(atomic)交易行为。
未来账户:使用和开发体验
更多特性
除了以上提到的原生账户问题优化之外,AA 还可以做到更多特性:
- 交易排序的自由度:执行交易的顺序检查由被部署的 Wallet Contract 自己决定,可以比只检查依序递增的 nonce 有更多样的解决方法。
- Application-Specific:不同的 DApp 能够针对他们的需求设计交易的验证和取款方式。
- Privacy Solution:如我们上述提到的 Tornado Cash 以及其他解决隐私方面问题的 DApp(如:PSE: ZKP DApp),都有机会因为 AA 的设计达到更高的私密性。
以上的特性能够被实现,原因都是来自于「交易验证」这件事情被拉到合约层做而不是单纯绑在以太坊的原生协议上。
普通用户视角
当我们的交易真的能够通过 AA 运作时,其实用户并不会感受到太大的差别,就像往日大多数在使用区块链服务的人们,可能都不会在自己的电脑里面生成交易跟签名,而是使用钱包的服务(如:imToken,MetaMask)以及 DApp 提供的前端(如:Uniswap Interface)。
在 AA 的情况下:每个账户都是 Contract Account,而从签名交易、送出交易、捆绑交易、验证交易、扣除手续费到最后的执行交易,一切过程都会是钱包商负责处理。
所以从用户的视角中,日常操作基本上是没有差别的,只会感受到钱包多了许多新颖的功能,某些情况下也会更值得信赖(与传统 CA 钱包商比较下,AA 钱包商不需要依赖 Relayer)。
钱包商 / 开发者视角
在面对 AA 这样新形态的交易模式时,第一个要解决的问题就是过度 Application-Specific,目前并没有一个绝对的协议决定一个合约钱包该长怎样,每一个 DApp 也可能会有不同的交易验证方式。所以开发者就需要针对不同的服务,去注意不同的 Operations。
另外一个问题是,随着合约钱包的功能不断增加,验证过程越趋复杂,安全漏洞出现的几率也就随之提升。这可能会造成开发团队不小的负担,也要花更多人力处理审计问题。
原本交易的验证这件事情只在底层被处理,普通的开发者只要写出安全的合约,交易验证交给矿工跟 Ethereum Native Protocol 开发者去烦恼就好。但现在交易过程都拉到合约层运作时,除了写出原本提供服务的智能合约,还要多处理与合约钱包配合的契合度与安全性。
更进一步的说,原本做 EOA 钱包的开发商为了应对 DApp 开始实践 AA 形态的交易模式,就也需要开始负担智能合约钱包的部分,这部分需要非常熟悉 AA 的运作。
可想而知 AA 与过往的交易形态是非常不同的,与既有的 EOA 钱包甚至合约钱包的工作流程都完全不同,因此大部分钱包商针对 AA 的开发动力可能会严重不足。
总结
最后,抽象账户做为近期风靡社群的存在,势必有它吸引人的地方,乍看之下它也好像能解决许多问题。但很多地方实际上都没有想像中美好(如:实现麻烦、Gas Fee 高等),一切都还需要开发者与提案者们更多的深入讨论,才有机会真正将 AA 带入应用场景。
希望大家到这里能够对抽象账户有一定的了解了!最后如果对实践 AA 以及系统设计的协议细节有兴趣,欢迎阅读我之前写过的两篇文章:
特别感谢 NIC Lin, Chang-Wu Chen和 imToken 同事们校对本文并提供改进建议。