EIP-7701 原生账户抽象协议详解:新交易类型与 CURRENT_ROLE / ACCEPT_ROLE / TXPARAM 操作码家族
2026/9/15 13:13:49 网站建设 项目流程

EIP-7701 原生账户抽象协议详解:新交易类型与 CURRENT_ROLE / ACCEPT_ROLE / TXPARAM 操作码家族

【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs

导读

EIP-7701(Native Account Abstraction)提出将一笔以太坊交易的执行过程拆分为"验证(validation)"与"执行(execution)"等多个步骤(frame),并引入一种新的 EIP-2718 类型交易(AA 交易)以及CURRENT_ROLEACCEPT_ROLETXPARAM*三组新操作码,让智能合约账户可以自定义交易的合法性校验与 gas 支付逻辑,从而原生实现账户抽象。读完本文,你将掌握 EIP-7701 的交易载荷编码格式、五类角色(Role)的生命周期、新操作码的语义与限制、完整的交易处理伪代码流程,以及它作为设计原型与后续 EIP-8141(Frame Transaction)之间的演进关系。

本文基于当前仓库中的 EIPS/eip-7701.md 与 assets/eip-7701/README.md 展开。该 EIP 目前状态为Withdrawn(已撤回),撤回原因是"已被 EIP-8141 取代"(见 EIPS/eip-8141.md),因此本文重点在于还原其完整的技术设计,而非描述当前主网状态。

一、协议概述:把交易拆成"验证 + 执行 + 后置操作"

EIP-7701 的核心思想可以用一句话概括:把传统交易隐含的"签名即授权"流程,替换为可编程的合约验证流程。提案的摘要明确指出:

  • 将以太坊交易的执行范围拆分为多个步骤:验证(validations)、执行(execution)与后置操作(post-operation)
  • 交易的有效性由交易的验证步骤结果决定——只有所有验证 frame 都成功,交易才能被打包进区块;
  • 进一步将"授权(authorization)"与"gas 费用支付"两件事分离,允许一个合约(Paymaster)为一笔由另一个合约(Sender)执行的交易支付 gas。

这样做的动机很直接:原生账户抽象允许自定义交易验证逻辑与自定义 gas 支付逻辑,为钱包与 dApp 打开新的用例——例如无 ECDSA 密钥的签名方案、社交恢复、批量操作、代付 gas(Sponsorship)等。提案作者包括 Vitalik Buterin、Yoav Weiss、Alex Forshtat、Dror Tirosh、Shahaf Nacson,创建于 2024-05-01。

二、核心术语与角色定义

在深入规范之前,先明确 EIP-7701 使用的一套术语(完整定义见 assets/eip-7701/README.md):

术语含义
Smart Contract Account(智能合约账户)作为用户账户与链上身份的智能合约,负责持有资产、验证用户请求并代表用户执行操作
Sender发起当前 AA 交易的智能合约账户
Paymaster被请求代表 Sender 为当前 AA 交易支付 gas 费用的智能合约
Deployer在当前 AA 交易上下文中,负责为新 Sender 合约执行部署(如CREATE2)的智能合约
Entity上述 Sender / Paymaster / Deployer 的统称
Transaction Validity(交易有效性)一笔交易能否在不违反执行与共识规则的前提下被打包进区块的性质;它同时取决于交易输入与当前链上状态,因此会随时间变化
EIP-7701 Transaction由 Sender 发起、以 EIP-2718 兼容信封对象表示的一整笔交易
Call Frame(调用帧)合约执行期间某次具体函数调用的上下文与状态,包括入参、局部变量与执行环境
Top-Level Call Frame交易访问合约时的初始执行上下文,即 EVM 代码的"入口点"
EIP-7701 Call FrameEVM 代码执行的单个原子元素,即对某地址、以给定数据发起的一次顶层调用;其内部还可能包含内层调用帧,但内层帧不称为"EIP-7701 Call Frame";每个帧要么成功要么回滚
Frame's Role(帧角色)当前调用帧中被调用合约被要求执行的动作标识符;一个 Entity 在交易流程中可能承担一个或多个角色
Transaction Phase(交易阶段)一组 EIP-7701 Call Frame 构成交易流程中的单个步骤;AA 交易包含**验证(validation)执行(execution)**两个阶段
Validation phase(验证阶段)通过执行验证 EVM 代码来定义当前交易有效性的帧集合
Execution phase(执行阶段)根据 Sender 与 Paymaster 对用户输入的解释来执行动作的帧集合;这些帧不决定交易的有效性

其中"Transaction Validity 随时间变化"这一点非常重要:同样一笔 AA 交易,在当前状态下可能有效、在下一个区块状态变化后可能失效(例如验证依赖的余额或授权状态发生改变),这与传统基于 ECDSA 签名的静态有效性有本质区别。

五类角色常量

规范定义了一组与交易生命周期各步骤对应的角色常量:

名称用途
ROLE_SENDER_DEPLOYMENT0xA0Sender 部署帧(可选,每个账户只发生一次)
ROLE_SENDER_VALIDATION0xA1Sender 验证帧(必需)
ROLE_PAYMASTER_VALIDATION0xA2Paymaster 验证帧(可选)
ROLE_SENDER_EXECUTION0xA3Sender 执行帧(必需)
ROLE_PAYMASTER_POST_OP0xA4Paymaster 后置操作帧(可选)

三、新交易类型:AA 交易(AA_TX_TYPE)

EIP-7701 引入一种新的 EIP-2718 类型交易,类型号为AA_TX_TYPE(规范中标记为 TBD,即尚未最终确定)。按照 EIP-2718 的约定,交易格式为TransactionType || TransactionPayload,其中TransactionType是 0x00–0x7f 范围内的单字节类型标识,payload 的解释权归该类型定义者所有。

AA 交易的 payload 按如下结构 RLP 编码:

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 ])

逐字段解读:

  • chain_id:链 ID,用于防止跨链重放;
  • nonce:与 EIP-1559 类型交易类似的重放保护计数;
  • sender/sender_validation_data:Sender 地址及其自定义验证数据,由 Sender 在验证帧中解释;
  • deployer/deployer_data:可选的部署器地址与部署数据——当 Sender 尚不存在(无代码)时用于先部署账户;
  • paymaster/paymaster_data:可选的代付方地址及其验证数据,用于实现 gas 抽象(Gas Abstraction);
  • sender_execution_data:真正要执行的业务数据(相当于传统交易的data);
  • max_priority_fee_per_gasmax_fee_per_gas:沿用 EIP-1559 的费用字段;
  • sender_validation_gaspaymaster_validation_gassender_execution_gaspaymaster_post_op_gas:四个帧各自的 gas 上限;
  • access_list:EIP-2930 风格的访问列表;
  • authorization_list:授权列表(可类比 EIP-7702 中的授权条目概念)。

可以看出,这笔交易把"谁是发送者、谁付钱、怎么验证、执行什么"全部显式地编码在交易载荷中,由协议层按固定流程驱动。

四、CURRENT_ROLEACCEPT_ROLE操作码:显式的角色生命周期

上下文变量current_context_role

规范引入一个上下文变量current_context_role,由 AA 交易在执行时设置:

  • 在 AA 交易期间,对于每个顶层调用,它被设置为该交易生命周期中当前所处的角色;
  • 在非 AA 交易期间,它始终ROLE_SENDER_EXECUTION
  • 它在DELEGATECALL时保持不变,但在CALL/STATICCALL/CALLCODE时被重置为ROLE_SENDER_EXECUTION——这个传播行为与msg.sender类似(见 EIPS/eip-7701.md)。

配套文档进一步说明:该角色值只对顶层帧以及通过不间断DELEGATECALL链建立的 Entity 上下文内层帧保持;用其他操作码发起的调用帧一律以ROLE_SENDER_EXECUTION运行。

CURRENT_ROLE操作码

CURRENT_ROLE返回当前的current_context_role值,供合约查询"我正在以什么角色被调用"。这是TXPARAM*之外另一条与交易类型相关的自省通道。

ACCEPT_ROLE操作码:授权与有效性闸门

ACCEPT_ROLE在语义上等价于RETURN——它复制一段内存切片、结束当前执行,并把该切片粘贴到父调用的 returndata 中——但有一处关键修改:

  • 它额外接受一个输入参数frame_role,如果该参数与current_context_role不一致,则直接回滚

换句话说,ACCEPT_ROLE是一个"带角色校验的 RETURN":合约只有在正确的角色上下文中显式声明"我接受当前角色"才能正常返回。交易生命周期中的每个角色,都必须有一次成功的ACCEPT_ROLE(其frame_role == 当前角色);只要任何一个验证帧没有执行与自身角色匹配的ACCEPT_ROLE,交易就通不过有效性检查,无法被打包

配套文档还给出了一条重要规则:如果 Entity 代码执行结束时current_context_role尚未通过ACCEPT_ROLE显式接受,则该 EIP-7701 Call Frame 回滚。因此,一笔 AA 交易有效当且仅当对role_sender_deploymentrole_sender_validationrole_paymaster_validation中的每一个角色同时满足:

  1. 顶层调用帧没有回滚;
  2. 在某个未回滚的帧中,ACCEPT_ROLE至少被调用一次,且传入的角色参数等于current_context_role

这套机制把"交易是否被授权"从静态签名校验变成了 EVM 内可编程、可审计的显式断言。

五、TXPARAM*操作码家族:交易参数的自省接口

指令形态与参数标识符

为了让验证合约能基于交易细节做出"接受或拒绝"的知情决策,EIP-7701 新增TXPARAMDLOADTXPARAMSIZETXPARAMCOPY三个操作码,它们遵循CALLDATA*/RETURNDATA*操作码家族的既有模式,但每个指令都额外接受一个栈输入作为第一个参数:参数标识符txparam_idn)。

参数标识符取值表

n返回值数据大小默认值说明
0x00当前交易类型32
0x01nonce32
0x02sender32
0x03sender_validation_data动态
0x04deployer0 或 32address(0)
0x05deployer_data动态空数组
0x06paymaster0 或 32address(0)
0x07paymaster_data动态空数组
0x08sender_execution_data动态
0x0Bmax_priority_fee_per_gas32
0x0Cmax_fee_per_gas32
0x0Dsender_validation_gas32
0x0Epaymaster_validation_gas320
0x0Fsender_execution_gas32
0x10paymaster_post_op_gas320
0x11access_list哈希32
0x12authorization_list哈希32
0xf1execution_status32交易作用域变量,见处理流程
0xf2execution_gas_used32交易作用域变量,见处理流程
0xfftx_hash_for_signature32不含签名的交易哈希

可见,验证合约可以读到交易的几乎所有关键字段(Sender、Paymaster、四段 gas 上限、费用参数、访问列表与授权列表的哈希等),其中deployer/paymaster在未提供时返回address(0),动态数据字段返回空数组,gas 相关字段在未提供时返回00xff提供的"不含签名的交易哈希"可用于在 EVM 内实现自定义签名/授权方案(例如验证基于非 ECDSA 曲线的签名)。

使用限制

TXPARAM*操作码有严格的使用边界(见 assets/eip-7701/README.md):

  • 只在所有角色的顶层帧中可用;在其他上下文调用时返回零值与零长度;
  • 请求execution_status(0xf1)与execution_gas_used(0xf2)参数时,只有role_paymaster_post_op角色的帧内才能读到有效值,其余上下文返回零值;
  • 合约可以通过CURRENT_ROLE来判断当前帧角色,进而决定能否安全使用这些参数。

六、受影响的全局变量与冷地址访问成本

顶层帧中的全局变量语义

在 AA 交易的所有顶层帧中,传统全局变量的含义被重新定义:

操作码Solidity 对应
CALLERmsg.senderAA_ENTRY_POINT地址(address(0x7701)
ORIGINtx.origin交易的sender地址
CALLDATA*msg.data除 Sender 执行帧外所有调用帧均为空;Sender 执行帧中为sender_execution_data

也就是说:所有 Entity 帧看到的"调用者"都是固定的协议入口address(0x7701),而tx.origin始终指向 Sender 账户;业务数据只在执行帧通过msg.data暴露。这与传统交易中ORIGIN == CALLER == 签名地址的行为形成鲜明对比(配套文档对此有专门说明:传统交易中两者都等于由 ECDSA 签名(yParity, r, s)确定的地址)。

冷地址预热与 2400 gas 附加成本

  • Sender地址作为AA_BASE_GAS_COST(15000)的一部分被预预热(pre-warmed),避免重复支付冷访问成本;
  • 当为PaymasterDeployer提供非零且不等于 Sender 地址的地址时,额外收取一次 EIP-2930 的ACCESS_LIST_ADDRESS_COST,即2400 gas,并将该地址加入accessed_addresses

其余协议常量汇总(见 EIPS/eip-7701.md):

常量
AA_TX_TYPETBD
AA_ENTRY_POINTaddress(0x7701)
AA_BASE_GAS_COST15000

七、AA 交易处理流程

规范给出了完整的state_transition_function伪代码,这是理解整套协议最直观的入口:

def state_transition_function(tx, block, state): # Empty refunds, warm list, execution status and gas used (new), etc 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)

流程逐步拆解

  1. 初始化:清空退款、热地址列表、执行状态与已用 gas 等交易作用域变量。
  2. 费用预扣max_gas是四段 gas 上限之和;gas_price按 EIP-1559 规则取max_fee_per_gasbase_fee_per_gas + max_priority_fee_per_gas的较小值;付款方payer在有 Paymaster 时是 Paymaster,否则是 Sender;先从 payer 余额中预扣total_max_cost = max_gas * gas_price
  3. Sender 部署帧(可选):若get_code(tx.sender)为空(账户尚不存在),先以ROLE_SENDER_DEPLOYMENT角色调用 Deployer;只有其accepted_role == ROLE_SENDER_DEPLOYMENT断言通过才继续。
  4. Sender 验证帧(必需):以ROLE_SENDER_VALIDATION调用 Sender,断言其接受了对应角色。注意 gas 上限用的是sender_validation_gas_limit - gas_used,即扣除部署帧消耗后的剩余量。
  5. Paymaster 验证帧(可选):以ROLE_PAYMASTER_VALIDATION调用 Paymaster,同样断言角色接受。
  6. 快照与执行take_snapshot()建立检查点后,以ROLE_SENDER_EXECUTION执行 Sender;随后把execution_status(执行帧的 output code)与execution_gas_used写入交易作用域变量——这两个值正是TXPARAMLOAD0xf1 / 0xf2 的数据来源。
  7. Paymaster 后置操作帧(可选):以ROLE_PAYMASTER_POST_OP调用 Paymaster;若该帧没有接受角色,则revert_snapshot(checkpoint),连同 Sender 执行的改动一起回滚
  8. 结算退款:最后把gas_price * (max_gas - gas_used)退回给 payer。

完整的帧清单与两阶段划分

综合规范与配套文档,一笔 AA 交易可能产生的全部顶层帧为:

  1. 验证阶段(Validation Phase)
    • Sender 部署帧(可选,每账户一次)——role_sender_deployment(0xA0)
    • Sender 验证帧(必需)——role_sender_validation(0xA1)
    • Paymaster 验证帧(可选)——role_paymaster_validation(0xA2)
  2. 执行阶段(Execution Phase)
    • Sender 执行帧(必需)——role_sender_execution(0xA3)
    • Paymaster 后置操作帧(可选)——role_paymaster_post_op(0xA4)

验证阶段的所有帧必须全部成功且不回滚,交易才被认为在区块的某个位置有效;执行阶段(含 postOp)不参与有效性判定。

Paymaster 后置操作帧的设计意图

postOp 帧属于执行阶段而非验证阶段,它为 Paymaster 提供三个能力(详见配套文档 Rationale):

  • 在 Sender 执行结果已知后完成自身的记账/退款结算
  • 强制执行后置条件,确保 Sender 做了 Paymaster 预期的事;
  • 如果 postOp 回滚,Sender 执行的状态改动一并回滚:用户得不到交易价值(消除了利用 Paymaster 的动机),Paymaster 虽然仍支付 gas 但阻止了不合规操作完成,并能识别出违规 Sender、拒绝其后续交易。

典型触发 postOp 回滚的场景包括:用户没有完成 Paymaster 打算付费的特定动作;用户在验证时提供的信息在 postOp 检查时被证实为虚假;求解器(solver)未在 postOp 校验中正确履行某个"意图(intent)"。

八、交易执行上下文:跨帧的状态一致性

将交易拆成多个帧,并不意味着破坏 EVM 的"交易作用域"语义。配套文档明确指出,以下依赖交易上下文的特性不受多帧拆分影响

  1. SSTORE (0x55)的 gas 成本(按 EIP-2200);
  2. 冷地址与冷槽位的访问成本(按 EIP-2929);
  3. 瞬态存储(transient storage)中可用的值(按 EIP-1153);
  4. 执行后分配的最大 gas 退款额(按 EIP-3529)。

例如,某个帧中用TSTORE (0x5D)写入的值在下一个帧中依然可读。这一点保证了 Sender 验证帧与执行帧之间、以及 postOp 帧与执行帧之间可以共享状态,是实现"先验证后执行"且整体记账一致性的基础。

九、交易流程示意图

以下两张示意图直接取自本仓库的配套文档 assets/eip-7701/README.md,分别展示了最简流程与完整流程中AA_ENTRY_POINT与各 Entity 之间的调用与角色接受关系。

简单流程中,验证阶段仅包含 Sender 验证帧(ACCEPT_ROLE 0xA1),执行阶段包含 Sender 执行帧(ACCEPT_ROLE 0xA3),执行帧内部再向 Target Contract 发起业务调用。完整流程在此基础上叠加了可选的 Sender 部署(Deployer 通过CREATE2部署 Sender,ACCEPT_ROLE 0xA0)、Paymaster 验证(ACCEPT_ROLE 0xA2)与 Paymaster PostOp(ACCEPT_ROLE 0xA4)。两张图的原始 PlantUML 源码分别位于 assets/eip-7701/simple_flow.md 与 assets/eip-7701/complete_flow.md。

十、设计取舍(Rationale)

为什么引入TXPARAM*而不是为每个字段单独造操作码

智能合约账户的验证代码需要访问绝大多数交易细节才能做出接受/拒绝的知情决策。虽然CALLER (0x33)GASPRICE (0x3A)等现有操作码能提供一小部分数据,但为每个交易参数单独创建操作码既不现实也不可取。TXPARAM*家族以统一的txparam_id索引方式解决了这个问题。

同时,这些值不向交易的执行帧或传统交易类型开放。这一限制防止TXPARAM*成为新的"全局可观测状态(globally observable state)"来源,从而避免未来出现向后兼容问题——执行代码不应能根据只读的交易参数产生分支依赖,否则会增加重放与兼容风险。

为什么 postOp 回滚要连带回滚主执行

如第七节所述,postOp 帧回滚时回滚 Sender 执行的整体改动,本质上是让 Paymaster 的"后置条件"成为用户操作生效的硬约束:用户无法在未满足条件的情况下"白嫖"交易结果,Paymaster 也不会为不合规操作买单(虽然仍承担 gas),并能借此识别与封锁恶意 Sender。

十一、向后兼容与安全考虑

向后兼容

EIP-7701 以全新的 EIP-2718 交易类型引入,与传统交易(legacy、EIP-1559 等)在编码层天然隔离——类型字节落在[0x00, 0x7f]区间,不会与以>= 0xc0开头的 RLP 传统交易冲突(这是 EIP-2718 信封设计本身提供的保证,见 EIPS/eip-2718.md)。规范正文中该小节未展开额外细节。

安全考虑

ACCEPT_ROLE是一个通用的、代表合约授权任何操作的机制,因此其实现正确性与安全性至关重要:

  • 规范期望面向 EVM 的编译器在启用与保障智能合约账户安全性方面发挥主要作用——由编译器生成并约束ACCEPT_ROLE的使用,降低手写汇编出错的可能;
  • 对于智能合约安全审计人员与安全导向的开发工具,关键是确保不打算在 AA 交易中扮演角色的合约不会意外包含ACCEPT_ROLE操作码,否则这些合约可能构成直接的安全威胁——例如一个普通合约若在错误上下文里"接受"了角色,可能被攻击者利用来放行未经授权的操作;
  • 作为示例,区块浏览器应当在合约源码中检测到ACCEPT_ROLE时,将其标记为"用户账户(user account)"或"paymaster",帮助用户识别其语义。

十二、协议演进:从 EIP-7701 到 EIP-8141

需要特别说明的是,EIP-7701 的 front matter 明确标注status: Withdrawnwithdrawal-reason为 "Superseded by EIP-8141"。也就是说,这份规范在当前仓库中是以历史提案的形式存在的。

它的后继者 EIPS/eip-8141.md(Frame Transaction,Draft 状态)继承了"把交易分解为验证、批准 gas 支付与执行标准用户操作的帧序列"的核心理念,并做了大量演进:引入FRAME_TX_TYPE0x06)、ENTRY_POINT = address(0xaa)FRAME_TX_INTRINSIC_COST = 12000FRAME_TX_PER_FRAME_COST = 475等具体常量,将帧的定义、签名方案(含后量子方案)与费用结构系统化。因此,阅读 EIP-7701 的最佳方式,是把它看作"原生账户抽象"这一设计方向的重要早期蓝图——它的角色模型、ACCEPT_ROLE授权机制与TXPARAM*自省接口,为后续 frame 交易的设计奠定了概念基础。

十三、总结

EIP-7701 以一份交易类型 + 一族操作码的方式,把账户抽象的三大诉求落到了协议层:

  1. 可编程验证:通过ROLE_*角色 +ACCEPT_ROLE显式授权,替代固定 ECDSA 签名校验;
  2. 可编程付费:通过可选的 Paymaster 角色帧,实现"他人代付 gas",且 postOp 帧让代付方可执行后置约束;
  3. 可编程执行:通过TXPARAM*让验证代码基于完整交易上下文做决策,同时限制其在执行帧的可见性以规避可观测状态问题。

对于钱包、dApp 与 Layer2 研究者而言,EIPS/eip-7701.md 与 assets/eip-7701/README.md 是理解原生账户抽象设计空间的第一手资料;而 EIPS/eip-8141.md 则展示了这一设计后续的成熟形态。文中涉及的伪代码、常量表与参数表均可直接对照规范原文验证,未做任何推测性改写。

【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询