跳到主要内容

view 函数与签名查询

公开 EVM 上的 eth_call 是简单的:任何人都能调用任何 view 函数,结果是公开事实。到了 OXN,view 函数可以运行在加密状态上——这带出一个问题:谁被授权看到结果?

OXN 用三种读取模式来回答。本页介绍全部三种、什么时候用哪种、以及 SDK 如何帮忙。

三种读取模式

模式认证结果机密性典型用例
明文 eth_call公开(任何调用者看到相同结果)任何人可无限制读取。
加密调用(无认证)只对发起会话加密机密的公开数据(罕见——通常也想要认证)。
签名查询调用者签名加密到调用者的会话密钥读取仅供本调用者的数据。

明文 eth_call

裸 ethers.js 以明文发送 eth_call。合约执行 view 函数,返回值明文回给调用者。

因为没有签名也没有加密:

  • 合约内 msg.sender零地址0x0)。
  • 结果明文传给发问者——客户端、RPC 提供商、网络观察者,全部能看到。

只有当函数本就打算返回公开数据时用明文 eth_call

加密 view 调用

Wrapped provider 自动加密 eth_call 请求并收到加密响应。这样能防 RPC 提供商与网络观察者看到结果,但认证调用者:

  • 合约内 msg.sender 依然是零地址
  • 结果只有发起调用的客户端能解密(具体是会话密钥)。

结果敏感但调用者身份无需证明时用加密 view。实际用例较窄。

签名查询

签名查询是附带调用者签名的 eth_call。这是你在机密 ERC-20 里读 balanceOf(msg.sender)、或在密封拍卖里读 myBid() 时用的方式。

机制:

  1. 客户端像平常一样构造 eth_call payload。
  2. 客户端用想以哪个账户视角读的私钥签名这个 payload。
  3. 客户端把 payload 和签名(加密后)发给 enclave。
  4. Enclave 验证签名。
  5. Enclave 在 view 函数内把 msg.sender 设为签名者地址。
  6. Enclave 执行 view 函数。
  7. Enclave 用客户端的会话密钥加密结果回传。

于是合约可以在 view 函数里用 msg.sender 强制"只能读你自己的余额":

mapping(address => uint256) private balances;

function myBalance() external view returns (uint256) {
return balances[msg.sender]; // 有效,因为签名查询填充了 msg.sender
}

SDK 处理签名过程:

const provider = wrapEthersProvider(raw);
const signer = wrapEthersSigner(new ethers.Wallet(PRIVATE_KEY, raw));
const contract = new ethers.Contract(addr, abi, signer); // 是 signer,不是 provider

const balance = await contract.myBalance(); // 底层就是签名查询

注意 contract 的传入方是 signer,不是 provider。这样 SDK 才知道用调用者的密钥去签查询。

Gas 语义

签名查询消耗调用者 gas。它们是 eth_call 请求——链下、不改状态。签名仅用于填充 msg.sender。如果你的 view 函数很重,RPC 提供商仍可能限流或计费,但不产生链上 gas。

常见模式

"读我自己的私密状态。" 最常见的模式。合约暴露 function myBalance() view returns (uint256) 这类函数,读 balances[msg.sender]。调用者签名查询。他人读不到他的余额。

"读他人的状态,前提是他授权给你了。" 合约维护 mapping(address => mapping(address => bool)) approvals。view 函数在返回 balances[owner] 前检查 approvals[owner][msg.sender] 是否为 true。需要签名查询。

"读只有管理员能看的数据。" 合约有 owner。view 函数 require(msg.sender == owner)。只有 owner 的签名查询能拿到有意义的返回。

陷阱

"我的 view 函数对所有人都返回 0。" 你大概发的是明文 eth_callmsg.sender 是零地址,按地址索引的 mapping 返回了默认值。用 wrapped signer(不是 wrapped provider)作为合约的执行方。

"签名验证失败。" 客户端 chain ID 或 enclave 期望的验证参数可能配错。检查 provider 是否连接到 Chain ID 186

"我想以地址 A 签名,但用地址 B 的会话读。" 签名查询不是这样工作的。签名者和会话是同一个账户。想要跨账户访问,在合约里用 approvals 显式建模。

下一步