EIP-7701: 原生账户抽象
原生账户抽象协议,依赖于一种新的交易类型和一系列操作码
Authors | Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Alex Forshtat (@forshtat), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn) |
---|---|
Created | 2024-05-01 |
Discussion Link | https://ethereum-magicians.org/t/eip-7701-native-account-abstraction/19893 |
Table of Contents
摘要
我们建议将以太坊交易范围拆分为多个步骤:验证、执行和后操作逻辑。交易的有效性由交易验证步骤的结果决定。
我们进一步分离交易验证,用于授权和 gas 费用支付,允许一个合约为另一个合约执行的交易支付 gas 费用。
动机
原生账户抽象允许自定义交易的验证逻辑和自定义 gas 支付逻辑,为钱包和 dApp 开辟了新的用例和功能。
关于此提案的更详细的动机可以在 README document 中找到。
规范
常量
Name | Value |
---|---|
AA_TX_TYPE |
TBD |
AA_ENTRY_POINT |
address(0x7701) |
AA_BASE_GAS_COST |
15000 |
ROLE_SENDER_DEPLOYMENT |
0xA0 |
ROLE_SENDER_VALIDATION |
0xA1 |
ROLE_PAYMASTER_VALIDATION |
0xA2 |
ROLE_SENDER_EXECUTION |
0xA3 |
ROLE_PAYMASTER_POST_OP |
0xA4 |
新的交易类型
引入一种新的 EIP-2718 交易类型,类型为 AA_TX_TYPE
。
这种类型的交易被称为“AA 交易”。
它们的 payload 应该被解释为:
AA_TX_TYPE || rlp([
chain_id,
nonce,
sender, sender_validation_data,
deployer, deployer_data,
paymaster, paymaster_data,
sender_execution_data,
max_priority_fee_per_gas, max_fee_per_gas,
sender_validation_gas, paymaster_validation_gas,
sender_execution_gas, paymaster_post_op_gas,
access_list,
authorization_list
])
CURRENT_ROLE
和 ACCEPT_ROLE
操作码
current_context_role
是一个由 AA 交易设置的上下文变量,表示当前角色。
在 AA 交易期间,对于每个顶级调用,它被设置为交易生命周期中的当前角色。
在非 AA 交易期间,它始终设置为 ROLE_SENDER_EXECUTION
。
它在 DELEGATECALL
上保持不变,但在 CALL
/ STATICCALL
/ CALLCODE
上重置为 ROLE_SENDER_EXECUTION
。此行为类似于 msg.sender
。
CURRENT_ROLE
操作码返回 current_context_role
值。
ACCEPT_ROLE
操作码等同于 RETURN
,因为它复制一个内存切片,结束执行,并将内存切片粘贴到父 returndata,只有一个修改:
- 它接受
frame_role
作为附加输入参数,如果它与current_context_role
不同,则恢复。
对于交易生命周期中的每个 role
,都需要成功执行 ACCEPT_ROLE
,其中 frame_role == role
。
如果任何验证 frame 未能执行与其角色匹配的 ACCEPT_ROLE
,则交易将无法通过有效性检查,并且无法被包含。
TXPARAM*
操作码
TXPARAMDLOAD
、TXPARAMSIZE
、TXPARAMCOPY
操作码遵循 CALLDATA*
/ RETURNDATA*
操作码族的模式。
与 CALLDATA*
等效项相比,每个 TXPARAM*
操作码都需要一个额外的堆栈输入值作为第一个输入。
此输入的值如下:
n |
Return value | Data size | Default | Comment |
---|---|---|---|---|
0x00 | current transaction type | 32 | ||
0x01 | nonce |
32 | ||
0x02 | sender |
32 | ||
0x03 | sender_validation_data |
dynamic | ||
0x04 | deployer |
0 or 32 | address(0) |
|
0x05 | deployer_data |
dynamic | empty array | |
0x06 | paymaster |
0 or 32 | address(0) |
|
0x07 | paymaster_data |
dynamic | empty array | |
0x08 | sender_execution_data |
dynamic | ||
0x0B | max_priority_fee_per_gas |
32 | ||
0x0C | max_fee_per_gas |
32 | ||
0x0D | sender_validation_gas |
32 | ||
0x0E | paymaster_validation_gas |
32 | 0 |
|
0x0F | sender_execution_gas |
32 | ||
0x10 | paymaster_post_op_gas |
32 | 0 |
|
0x11 | access_list hash |
32 | ||
0x12 | authorization_list hash |
32 | ||
0xf1 | execution_status |
32 | See transaction scoped vars in processing flow | |
0xf2 | execution_gas_used |
32 | See transaction scoped vars in processing flow | |
0xff | tx_hash_for_signature |
32 | Hash of the transaction without the signature |
受影响的操作码
在所有顶层 frame 中,全局变量具有以下含义:
Opcode Name | Solidity Equivalent | Value |
---|---|---|
CALLER |
msg.sender |
The AA_ENTRY_POINT address |
ORIGIN |
tx.origin |
The transaction sender address |
CALLDATA* |
msg.data |
Empty for all call frames except for the sender execution frame, for which it is set to sender_execution_data |
访问 Sender、Paymaster 和 Deployer 的冷地址的成本
Sender 地址已预先预热,作为 AA_BASE_GAS_COST
的一部分。
当为 Paymaster
或 Deployer
合约提供一个不等于 Sender
地址的非零地址时,
将收取额外的 EIP-2930 ACCESS_LIST_ADDRESS_COST
成本,即 2400 gas,并将该地址添加到 accessed_addresses
。
AA 交易处理流程
我们将 AA 交易的处理流程定义如下:
def state_transition_function(tx, block, state):
# Empty refunds, warm list, execution status and gas used (new), etc
# 清空退款、预热列表、执行状态和 gas 使用量(新),等等
state.transaction_scoped_vars = {}
max_gas = tx.sender_validation_gas + tx.paymaster_validation_gas + tx.sender_execution_gas + tx.paymaster_post_op_gas
gas_price = min(tx.max_fee_per_gas, block.base_fee_per_gas + tx.max_priority_fee_per_gas)
payer = tx.sender if tx.paymaster is None else tx.paymaster
total_max_cost = max_gas * gas_price
balances[payer] -= total_max_cost
gas_used = 0
if get_code(tx.sender) is None:
deployer_result = call(tx.deployer, [], tx.sender_validation_gas_limit, ROLE_SENDER_DEPLOYMENT)
assert deployer_result.accepted_role == ROLE_SENDER_DEPLOYMENT
gas_used += deployer_result.gas_used
sender_result = call(tx.sender, [], tx.sender_validation_gas_limit - gas_used, ROLE_SENDER_VALIDATION)
assert sender_result.accepted_role == ROLE_SENDER_VALIDATION
gas_used += sender_result.gas_used
if tx.paymaster:
paymaster_result = call(tx.paymaster, [], tx.paymaster_validation_gas, ROLE_PAYMASTER_VALIDATION)
assert paymaster_result.accepted_role == ROLE_PAYMASTER_VALIDATION
gas_used += paymaster_result.gas_used
checkpoint = state.take_snapshot()
sender_execution_result = call(tx.sender, [], tx.sender_execution_gas, ROLE_SENDER_EXECUTION)
gas_used += sender_execution_result.gas_used
state.transaction_scoped_vars[execution_status] = sender_execution_result.output_code
state.transaction_scoped_vars[execution_gas_used] = gas_used
if tx.paymaster:
postop_result = call(tx.paymaster, [], tx.paymaster_post_op_gas, ROLE_PAYMASTER_POST_OP)
gas_used += postop_result.gas_used
if postop_result.accepted_role != ROLE_PAYMASTER_POST_OP:
state.revert_snapshot(checkpoint)
balances[payer] += gas_price * (max_gas - gas_used)
理由
关于此提案中做出的决定的完整理由列表可以在 README document 中找到。
向后兼容性
安全考虑
由于 ACCEPT_ROLE
操作码表示一种通用方式来授权代表合约执行任何操作,
因此正确和安全地实现此代码至关重要。
我们期望以 EVM 为目标的编译器在启用和确保智能合约账户的安全性方面发挥重要作用。
对于智能合约安全审计员和面向安全性的开发人员工具来说,至关重要的是要确保不打算在 AA 交易中扮演角色的合约没有意外的 ACCEPT_ROLE
操作码。
否则,这些合约可能会构成直接的安全威胁。
例如,如果 block explorer 在其源代码中使用了 ACCEPT_ROLE
操作码,则应将合约标记为“用户帐户”或“paymaster”。
版权
版权及相关权利通过 CC0 放弃。
Citation
Please cite this document as:
Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Alex Forshtat (@forshtat), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn), "EIP-7701: 原生账户抽象 [DRAFT]," Ethereum Improvement Proposals, no. 7701, May 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7701.