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() 时用的方式。
机制:
- 客户端像平常一样构造
eth_callpayload。 - 客户端用想以哪个账户视角读的私钥签名这个 payload。
- 客户端把 payload 和签名(加密后)发给 enclave。
- Enclave 验证签名。
- Enclave 在 view 函数内把
msg.sender设为签名者地址。 - Enclave 执行 view 函数。
- 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_call。msg.sender 是零地址,按地址索引的 mapping 返回了默认值。用 wrapped signer(不是 wrapped provider)作为合约的执行方。
"签名验证失败。" 客户端 chain ID 或 enclave 期望的验证参数可能配错。检查 provider 是否连接到 Chain ID 186。
"我想以地址 A 签名,但用地址 B 的会话读。" 签名查询不是这样工作的。签名者和会话是同一个账户。想要跨账户访问,在合约里用 approvals 显式建模。
下一步
- 访问控制模式——如何门控不同调用者能读什么
- 构建机密 ERC-20——端到端签名查询示例