-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathBeaconHistoricalRNG.sol
101 lines (89 loc) · 3.79 KB
/
BeaconHistoricalRNG.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {IRNG} from "./IRNG.sol";
import {RLPReader} from "../../libraries/RLPReader.sol";
/**
* @title Random Number Generator using `prevrandao` from the historical beacon chain block headers.
* @dev The random numbers are provided in the RLP-encoded block headers and validated onchain against the block hashes.
* Adapted from kevincharm's https://github.com/kevincharm/randao-accessor/blob/312637b99be8d85f1afc6ca878c84adbb78fc7a6/contracts/RandaoAccessor.sol
*/
contract BeaconHistoricalRNG is IRNG {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
uint256 public constant LOOKAHEAD = 132; // Number of blocks that has to pass before obtaining the random number. 4 epochs + 4 slots, according to EIP-4399.
IRNG public blockhashRNGFallback; // Address of blockhashRNGFallback to fall back on.
mapping(uint256 blockNumber => uint256 randomNumber) public randomNumbers; // The random number for this block, 0 otherwise.
/**
* @dev Emitted when a block header is requested.
* @param _blockNumber The block number of the header requested.
*/
event Request(uint256 _blockNumber);
/**
* @dev Constructor.
* @param _blockhashRNGFallback The blockhash RNG deployed contract address.
*/
constructor(IRNG _blockhashRNGFallback) {
blockhashRNGFallback = _blockhashRNGFallback;
}
function contribute(uint256 _block) public payable override {} // Not used
/**
* @dev Request a random number.
* @param _block Block number of the header requested for verification by `verifyRecent()`.
*/
function requestRN(uint256 _block) public payable override {
emit Request(_block + LOOKAHEAD);
}
/**
* @dev Return the random number. If it has not been saved and is still computable compute it.
* @param _block Block number requested.
* @return rn Random Number. If the number is not ready or has not been requested 0 instead.
*/
function getRN(uint256 _block) public override returns (uint256 rn) {
if (block.difficulty <= 2**64) {
// Pre-Merge
rn = blockhashRNGFallback.getRN(_block);
} else {
// Post-Merge
if (block.number > _block + LOOKAHEAD) {
rn = randomNumbers[_block + LOOKAHEAD];
}
}
}
/**
* @dev Get an uncorrelated random number.
* @param _block Block number requested.
* @return rn Random Number. If the number is not ready or has not been required 0 instead.
*/
function getUncorrelatedRN(uint256 _block) public override returns (uint256 rn) {
rn = getRN(_block);
if (rn != 0) {
rn = uint256(keccak256(abi.encodePacked(msg.sender, rn)));
}
}
/**
* @notice Verify a recent block header and return the prevrandao value
* @param _blockHeaderRLP RLP-encoded block header
* @return Verified prevrandao
*/
function verifyRecent(bytes calldata _blockHeaderRLP) public returns (uint256) {
RLPReader.RLPItem[] memory blockHeader = _blockHeaderRLP.readList();
uint256 _inputBlockNumber = blockHeader[8].readUint256();
require(
_inputBlockNumber < block.number,
"Input block must be older than current"
);
require(
(_inputBlockNumber >= (block.number - 256)) &&
(_inputBlockNumber < block.number),
"Block too old"
);
bytes32 targetBlockHash = blockhash(_inputBlockNumber);
require(
targetBlockHash == keccak256(_blockHeaderRLP),
"RLP does not match blockhash"
);
uint256 randao = blockHeader[13].readUint256();
randomNumbers[_inputBlockNumber] = randao;
return randao;
}
}