引介 | 账户抽象化(EIP-2938):为什么 & 做了什么
以太坊有两种类型的账户:外部账户(Externally Owned Account, EOA)和合约账户(Contract Account, CA)。前者由用户私钥控制,而后者由存储在智能合约账户(有时也被称为智能钱包)内的合约代码控制。外部账户的权限要大于合约账户,因为只有外部账户可以通过支付 gas 启动交易的执行过程。账户抽象化(Account Abstraction, AA)则是一个可以让合约账户成为和外部账户一样的 “顶层” 账户的提案,实现了账户抽象化之后,合约账户也可以支付交易费和执行交易。
之所以提出账户抽象化,是想要在钱包、交易混淆器(mixer)、ÐApp 和 DeFi 等各种应用场景下,显著改善用户与以太坊链的交互体验。账户抽象化方案要为以太坊提供一个基础功能层,来确定何时支付,谁来支付以及怎样支付 gas。
以 Status Messenger 应用为例,其将隐私信使服务与以太坊钱包和 Web3 浏览器集成在一起。目前,Status 钱包仍然是外部账户钱包,因此其不能像智能合约钱包那样支持丰富的用户体验,比如多签安全、基于社交的钱包恢复(social recovery)、支付限额、地址黑/白名单以及无需 gas 费用的元交易。(之所以没有采用智能合约钱包,是因为)智能合约钱包的用户体验受累于 gas 价格波动,即使通过第三方的中继者也无法有效解决这个问题。账户抽象化旨在解决这个问题。
在本文中,我们先探讨智能合约钱包对账户抽象化的需求。然后描述协议的变更及其对节点的影响,借此深入到账户抽象化的关键部分。最后,我们讨论一些关于扩展功能的提案,并以阐释 Status 项目的计划路线图作为本文的总结,因为该项目要与以太坊配合因而势必受到账户抽象化的影响。
历史与动机
账户抽象化这个概念是在撰写于 2017 年的 EIP-86 里首次提出的,其目的是实现 “交易来源和签名的抽象”。不过其动机和想法可以追溯到 2016 年年初 vitalik 提交的一个 issue, 文中建议 “与其将 ECDSA 签名算法和默认的 nonce 机制写死在协议内作为 ‘标准’ 的账户安全机制,不如初步建立一个(统一的)账户模型,在未来把所有的账户都变成合约,让合约可以支付 gas,让用户可以自由定义自己的安全模型。”
要实现最初的提议非常有挑战,因为不仅要大幅修改协议,还要满足系统的安全保证。最近,Vitalik 等人提出了 EIP-2938 草案,该草案概述了一个更简单的实现。这个实现对 协议/共识 的改动最小,并且通过设置节点的内存池规则来满足所需的安全保证。Vitalik 的以太坊工程组 Meetup 演讲和 Sam Wilson 与 Ansgar Dietrichs(EIP 的另外 2 位作者)的ETH 线上演讲(以及附带的两篇文章:1 & 2)都对这个主题做了非常棒的介绍。本文会着重介绍这些资料的关键内容。
动机:账户抽象化背后的动机很简单,但会带来根本性的改变:当前,以太坊交易具备 功能可编程性(通过调用智能合约实现),但是 交易的验证方式 却是固定的。只有持有有效的 ECDSA 签名、有效的 nonce 值以及足够的账户余额,一笔交易才算有效。账户抽象化引入了一种新的交易类型 —— 抽象账户交易(AA Transaction)。这种交易总是由一个特殊地址产生,协议不会检查其签名,nonce 和余额。通过引入这种交易,账户抽象化实现了从固定验证方式到可编程验证方式的转变。抽象账户交易的有效性由其 target 字段指定的智能合约验证,通过验证之后,合约可以自行为该交易支付手续费。
那么,为什么账户抽象化这么有用呢?我们将用以太坊钱包为例,阐述它会带来哪些好处。
智能合约钱包:如今大多数以太坊钱包都是外部账户钱包,它们的安全性由助记词(seed phrase)生成的私钥保护。(一串遵循 BIP-39 标准的助记词由 12-24 个单词依序拼成,这些单词是从 2048 个单词中随机选择的。助记词为 PBKDF2 函数提供所需的信息熵,以生成一串二进制种子。接着,基于 BIP-32 标准的钱包使用该种子生成非对称密钥对。)为了以后能在其他钱包里恢复密钥,用户应当安全妥善地保管助记词。然而,这种钱包很容易因为私钥被盗或者助记词丢失导致用户的资产遭受损失。
(顾名思义)智能合约钱包是通过智能合约在链上实现的。这种钱包能够提供可编程的风险迁移和友好的用户体验,比如多签安全、基于社交或时间的钱包恢复、转账金额限制、地址黑白名单、无需 gas 费用的元交易和批量交易。
虽然智能合约面临因代码质量差而带来的安全风险,但是这种风险可以通过钱包提供方的安全审计来减轻。而外部账户钱包的风险完全由用户自己承担,用户无法获得智能合约那样的可编程的安全保障,用户必须自己保管好助记词。
当前可用的智能合约钱包有 Argent、Authereum、Dapper、Dharma、Gnos is Safe、Monolith 和 MYKEY。从下图可以看到,这类钱包的使用率看起来在增加。
Argent 通过守护者(Guardian)的概念实现了无需种子的基于社交的钱包恢复。守护者是用户信任的人或设备,可以帮助用户恢复钱包。Argent 还要实现类似银行的安全性(提供每日交易限额、账户锁定和可信联系人等功能)与类似 Venmo (译者注:Venmo 是 PayPal 旗下的一个移动支付服务)的可用性(用 ENS(译者注:Ethereum Name Servic,ENS 是一个可读的、去中心化且安全的域名系统)取代地址,支持元交易)。
Gnosis Safe 是一款多签智能合约钱包,专注于团队资金管理,任何一笔交易都要得到团队中最低人数(n 个成员中的至少 m 个)的批准才能发生。它还可以通过元交易实现无需 gas 的签名。
所有这些高级钱包功能都要用到灵活的智能合约。钱包用户要么通过外部账户发送交易,支付 gas 费以调用钱包合约,要么依赖钱包提供方支持元交易功能,借由钱包提供方的中继器或者第三方中继网络如 Gas Station Network 来使用钱包。前者通常是通过了 KYC 认证的用户依靠中心化交易所购买 ETH(以支付 gas 费);后者将用户的负担转移到中继器上,由钱包提供方或用户在链上或链下补偿中继器,从而提高用户体验。
然而,基于中继器的架构有三个主要缺点:(1)他们可能会面临中心化的诟病,引起交易审查的担忧;(2)由于中继器发出的交易需要支付额外 21000 单位的基础 gas 费,为了获得利润其必须收取更高的费用以覆盖这部分成本,这就导致了技术和经济上的低效;(3)对中继专用协议的使用,迫使应用不得不依赖非以太坊的基础设施,而较小的用户群无法支撑这些设施提供稳定可靠的服务。
账户抽象化将使智能合约能够接受用户的无需 gas 的元交易,并且无需依赖中继网络就能为其支付 gas 费。因此,这种底层能力在不牺牲去中心化的情况下,能够极大改善这些智能合约钱包的用户体验。
Tornodo Cash:另一个(将受益于账户抽象化的)相关应用是交易混淆器,例如 tornado.cash。Tornado 使用智能合约切断地址之间的链上关联以提升交易的隐私性,该合约允许用户存入 ETH 后通过另一个地址提款。用户在存款时提供一个秘密值的哈希,随后在提款时提供 zkSnark 证明。该证明可以在不泄露秘密值和存款信息的情况下,证明其知道这个秘密值。这样就把存款和提款脱钩了。
但是在提款时有一个鸡生蛋蛋生鸡的问题。为了在新生成的地址上执行提款交易,用户需要给该地址存入一些 ETH 以支付 gas 费。但这些 ETH 的来源(通常是一家交易所)本身又会破坏使用 Tornado 所带来的隐私性。首选的替代方案是再次使用中继网络,而这样做有上述的那些缺点。
账户抽象化就可以解决这个问题,Tornado 合约可以接受用户的提款抽象账户交易,验证 zkSnark,(从存款里)扣除 gas 费,然后把剩余的资金转给提款地址。
账户抽象化
在 EIP-2938 中提出的账户抽象化,接纳合约作为顶层账户,使之可以支付交易费以及启动交易的执行过程。实现账户抽象化需要变更协议以增加新的抽象账户交易类型、修改内存池规则以及引入扩展功能以支持高级用法。抽象账户交易需要增加两个新的操作码:NONCE 和 PAYGAS。账户类型仍然保持现有的两种(外部账户和合约账户),所有的变更都向后兼容以支持当前的交易、智能合约和协议。
账户抽象化应用可以分成两类:1) 单租户应用,如智能合约钱包,其会为每个用户创建一个新合约;2) 多租户应用,例如 tornado.cash 或 Uniswap,多个用户与同一组智能合约交互。
目前还不能支持多租户应用,这需要更多的研究工作,以期未来早日实现。所以本文将重点介绍单租户的账户抽象化。
协议变更
对协议唯一的变更就是引入了一种新的交易类型,以及相关的两个操作码 NONCE 和 PAYGAS。
抽象账户交易:协议引入了一种新的交易类型:AA_TX_TYPE。它的二进制数据 (payload) 会被解析为 RLP([nonce, target, data]) ,而不是现有交易的 RLP([nonce, gas_price, gas_limit, to, value, data, v, r, s]) 。
Nonce 检查和现有交易类似,需满足 tx.nonce == tx.target.nonce。如果检查失败,则交易无效,如果通过检查,则 tx.target.nonce 将会递增,交易继续进行。
抽象账户交易的基础 gas 消耗量将从 21000 降到 15000(以反映移除了内置的 ECDSA 签名所节省的成本)。此外,抽象账户交易没有内置的 gas 限额。当交易执行时,gas 限额只需设定为当前 block 还剩余的 gas 限额即可。
NONCE opcode:NONCE 操作码 (0x48) 推送被调用者 —— 即抽象账户交易的 target 字段所指定的合约 —— 的 nonce 值到 EVM 的栈上。借此,nonce 值被暴露给了 EVM, 因而抽象账户合约可以将交易的任何字段(包括 nonce)作为验签逻辑的一部分。
PAYGAS opcode:PAYGAS 操作码 (0x49) 从栈上弹出 2 个参数:(栈顶)version_number,(次栈顶)memory_start。
Version_number 允许未来通过新的实现变更操作码的语义。目前,操作码的语义如下:
检查 version_number == 0(否则抛出异常) 读取 gas_price = bytes_to_int(vm.memory[memory_start: memory_start + 32]) 读取 gas_limit = bytes_to_int(vm.memory[memory_start + 32: memory_start + 64]) 检查 contract.balance >= gas_price * gas_limit(否则抛出异常) 检查 globals.transaction_fee_paid == False(否则抛出异常) 检查 AA execution frame == top-level frame,也就是若当前 EVM 执行退出或回退,整个交易的 EVM 执行就暂停了(否则抛出异常) 设置 contract.balance -= gas_price * gas_limit 设置 globals.transaction_fee_paid = True 设置 globals.gas_price = gas_price,globals.gas_limit = gas_limit 设置当前的执行上下文的 remaining_gas = gas_limit - 已经消耗的 gas
内存池规则
扩展
SET_INDESTRUCTIBLE 操作码,它会禁用 SELFDESTRUCT,并允许抽象账户合约在验证阶段通过 DELEGATECALL 安全地调用库函数。 S_STATIC 操作码,如果当前的上下文是静态的则返回 true,并允许非抽象账户交易的调用者绕过之前的字节码前缀限制,安全地从抽象账户合约里读取数据。 RESERVE_GAS 操作码,当一笔非抽象账户交易试图对一个抽象账户合约写入状态时,设置一个 gas 消耗的下限。这样做是为了强制攻击者在试图让内存池的抽象账户交易无效时至少要消耗一定的 gas,以此抑制其攻击动机。
下一步
总结
(完)
(文内有许多超链接,可点击左下 ”阅读原文“ 从 EthFans 网站上获取)
原文链接:
https://our.status.im/account-abstraction-eip-2938/
作者: Rajeev Gopalakrishna
你可能还喜欢: