跳到主要内容

安全随机数

Sapphire.randomBytes 是 OXN 的链上随机数源。它从 enclave 自身的熵中取值——不是从区块哈希、时间戳或验证人可控值中——对观察者不可预测,验证人也无法操纵。

签名

function randomBytes(uint256 numBytes, bytes memory personalization)
internal view returns (bytes memory);
  • numBytes——要多少字节。uint256 通常 32,更小范围可以更少。
  • personalization——可选上下文。不同 personalization 字符串在同一区块内产生不同随机流,需要多次独立抽取时有用。

返回长度为 numBytesbytes

基本用法

import "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol";

contract Raffle {
address[] public participants;

function pickWinner() external view returns (address) {
require(participants.length > 0, "no participants");
bytes memory r = Sapphire.randomBytes(32, "");
uint256 idx = uint256(bytes32(r)) % participants.length;
return participants[idx];
}
}

一次调用内两次抽取

一个函数里需要多个独立随机值时,用不同的 personalization

bytes memory r1 = Sapphire.randomBytes(32, "draw-1");
bytes memory r2 = Sapphire.randomBytes(32, "draw-2");

不用不同 personalization 时,同一区块内两次调用得到相同结果。这是故意的——熵按区块派生;personalization 字符串让你从同一底层源产生独立流。

安全性质

对观察者不可预测。 随机输出在 enclave 内生成,熵从不离开 enclave。任何 RPC 观察者、区块浏览器、节点运营方都无法在使用前预测这个值。

验证人无法操纵。 因为熵源在 enclave 内而不是从区块元数据派生,验证人无法通过重新排序交易或选择哪个区块包含调用来偏斜结果。

未来不可预测。 现在产出的随机值无法通过模拟未来区块预测——熵流依赖只往前推进的运行时状态。

不要用作随机数的东西

block.timestampblock.numberblock.hashPREVRANDAO——全都公开可预测。任何观察者都能读;验证人能操纵。任何基于随机性发放价值的场景都不要用。

keccak256(abi.encodePacked(block.timestamp, msg.sender))——常见的以太坊模式,零真实随机性。每一个输入都是公开的。

链下预言机——引入对预言机的信任。公开数据可以,但对机密合约毫无意义。

OXN 上只有 Sapphire.randomBytes 提供真正的密码学随机数。

常见模式

公平抽奖选择:

function selectWinner(address[] memory candidates) internal view returns (address) {
bytes memory r = Sapphire.randomBytes(32, "raffle");
return candidates[uint256(bytes32(r)) % candidates.length];
}

小数组随机洗牌:

function shuffle(uint256[] memory arr) internal view returns (uint256[] memory) {
for (uint256 i = arr.length - 1; i > 0; i--) {
bytes memory r = Sapphire.randomBytes(32, abi.encode(i));
uint256 j = uint256(bytes32(r)) % (i + 1);
(arr[i], arr[j]) = (arr[j], arr[i]);
}
return arr;
}

加密的每用户机密:

function generateSecret() internal view returns (bytes32) {
return bytes32(Sapphire.randomBytes(32, abi.encode(msg.sender)));
}

Gas 成本

Sapphire.randomBytes 有一个基础 gas 成本加上少量按字节成本。精确数字见 Gas 成本参考。经验值:生成 32 字节随机数的成本与 sha256 一个 32 字节输入相当。

下一步