加密合约存储
OXN 上每一个合约的存储都静态加密。Solidity 作者写普通代码——加密由运行时透明地在每一次 SSTORE 与 SLOAD 上施加。本页解释这意味着什么、节点运营方到底看到什么、以及模型的边角情况。
Solidity 作者写普通代码
mapping(address => uint256) private balances;
function deposit() external payable {
balances[msg.sender] += msg.value; // 就是普通的 SSTORE
}
function balanceOf(address who) external view returns (uint256) {
return balances[who]; // 就是普通的 SLOAD
}
这里没有任何显式加密的调用。然而在 OXN 上,balances 以密文形式落盘。运行时在每次写入时加密、每次读取时解密——都发生在 enclave 内。
开发体验:与以太坊完全一致。
密钥层级
每个合约有一把独有的存储密钥。密钥由以下两者确定性派生:
- 由 enclave 持有的主密钥(按周期轮换,通过分布式密钥管理协议在验证人之间备份)
- 合约地址
派生是单向的——你无法从某合约的存储密钥反推主密钥,也无法从一个合约的密钥推另一个合约 的。即便所有存储共享同一底层数据库,每个合约都看到私有的存储命名空间。
密钥轮换自动进行、不需要合约重新部署。运行时在存储层处理重加密,对 Solidity 代码不可见。
节点运营方实际能看到什么
如果节点运营方直接 dump 状态 trie,他们看到:
- 存储槽地址(mapping 是
keccak256(key . slot),scalar 是slot)——明文 - 存储数值——加密,Deoxys-II 密文,配每槽 nonce
他们能看到合约用了多少存储、哪些槽被填充过(也就是 mapping 有多稀疏 / 多密)、每个槽最近一次在什么时候被写入。他们看不到任何槽里存的是什么。
这一点很重要:存储布局不是机密的,存储内容才是。 如果你的槽模式泄漏了结构信息(例如"槽 N 只有在用户 X 的余额非零时才写入"),这个信号是公开的。指引见机密性陷阱。
Getter 函数与 public 存储
Solidity 的 public 存储变量会自动生成 getter 函数:
mapping(address => uint256) public balances;
// 生成:function balances(address) external view returns (uint256)
存储本身依然是加密的。但自动生成的 getter 是任何调用者都能通过 eth_call 触发的普通 view 函数,能把值读回来。把存储标为 public 实质上把它公开了。
对机密存储,用 private,并通过带门控的 view 函数暴露访问(可以要求签名查询做认证):
mapping(address => uint256) private balances;
function balanceOf(address who) external view returns (uint256) {
require(msg.sender == who, "not authorized");
return balances[who];
}
显式 encrypt / decrypt(进阶)
有时你需要把加密数据跨合约边界传递——例如存一份加密 blob,之后再交给另一个合约去解密。OXN 为此提供预编译:
Sapphire.encrypt(key, nonce, plaintext, additionalData)——在调用者提供的密钥下做 Deoxys-II 加密Sapphire.decrypt(key, nonce, ciphertext, additionalData)——反向
密钥由调用者自己管理。ABI 与 gas 成本见预编译:对称加密。
大部分合约不需要这个。"合约保存自己的秘密"这种常见情形,标准存储加密就够了。只有当你确实存在具体的跨合约或跨交易密钥管理需求时才用显式原语。
存储升级与迁移
因为存储密钥是从合约地址派生的,把状态迁移到新的合约地址需要显式迁移。可选方案:
- 通过迁移交易复制状态。 旧合约通过(签名查询触发的)门控 getter 暴露状态,新合约存入复制后的值。状态在 enclave 内经历一次"解密-再加密",明文从不外泄。
- 使用代理模式。 存储放在 proxy 地址下,实现合约通过
delegatecall读写。存储密钥跨实现升级保持不变。
标准 OpenZeppelin 代理模式(Transparent、UUPS)在 OXN 上无需修改即可使用。