Skip to content

Commit 1ff6097

Browse files
committed
feat: composite rng with fallback logic, removal of the block param from the RNG interface
1 parent 2866a7a commit 1ff6097

14 files changed

+246
-113
lines changed

contracts/deploy/00-home-chain-arbitration-neo.ts

+3-13
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
1212
const { ethers, deployments, getNamedAccounts, getChainId } = hre;
1313
const { deploy, execute } = deployments;
1414
const { ZeroAddress } = hre.ethers;
15-
const RNG_LOOKAHEAD = 20;
1615

1716
// fallback to hardhat node signers on local network
1817
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
@@ -62,16 +61,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
6261
const maxTotalStaked = PNK(2_000_000);
6362
const sortitionModule = await deployUpgradable(deployments, "SortitionModuleNeo", {
6463
from: deployer,
65-
args: [
66-
deployer,
67-
klerosCoreAddress,
68-
minStakingTime,
69-
maxFreezingTime,
70-
rng.address,
71-
RNG_LOOKAHEAD,
72-
maxStakePerJuror,
73-
maxTotalStaked,
74-
],
64+
args: [deployer, klerosCoreAddress, minStakingTime, maxFreezingTime, rng.address, maxStakePerJuror, maxTotalStaked],
7565
log: true,
7666
}); // nonce (implementation), nonce+1 (proxy)
7767

@@ -107,10 +97,10 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
10797

10898
// rng.changeSortitionModule() only if necessary
10999
const rngContract = (await ethers.getContract("RandomizerRNG")) as RandomizerRNG;
110-
const currentSortitionModule = await rngContract.sortitionModule();
100+
const currentSortitionModule = await rngContract.consumer();
111101
if (currentSortitionModule !== sortitionModule.address) {
112102
console.log(`rng.changeSortitionModule(${sortitionModule.address})`);
113-
await rngContract.changeSortitionModule(sortitionModule.address);
103+
await rngContract.changeConsumer(sortitionModule.address);
114104
}
115105

116106
const core = (await hre.ethers.getContract("KlerosCoreNeo")) as KlerosCoreNeo;

contracts/deploy/00-home-chain-arbitration.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { DisputeKitClassic, KlerosCore, RandomizerRNG } from "../typechain-types
1111
const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
1212
const { ethers, deployments, getNamedAccounts, getChainId } = hre;
1313
const { ZeroAddress } = hre.ethers;
14-
const RNG_LOOKAHEAD = 20;
1514

1615
// fallback to hardhat node signers on local network
1716
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
@@ -69,7 +68,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
6968
const maxFreezingTime = devnet ? 600 : 1800;
7069
const sortitionModule = await deployUpgradable(deployments, "SortitionModule", {
7170
from: deployer,
72-
args: [deployer, klerosCoreAddress, minStakingTime, maxFreezingTime, rng.target, RNG_LOOKAHEAD],
71+
args: [deployer, klerosCoreAddress, minStakingTime, maxFreezingTime, rng.target],
7372
log: true,
7473
}); // nonce (implementation), nonce+1 (proxy)
7574

@@ -104,10 +103,10 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
104103

105104
// rng.changeSortitionModule() only if necessary
106105
const rngContract = (await ethers.getContract("RandomizerRNG")) as RandomizerRNG;
107-
const currentSortitionModule = await rngContract.sortitionModule();
106+
const currentSortitionModule = await rngContract.consumer();
108107
if (currentSortitionModule !== sortitionModule.address) {
109108
console.log(`rng.changeSortitionModule(${sortitionModule.address})`);
110-
await rngContract.changeSortitionModule(sortitionModule.address);
109+
await rngContract.changeConsumer(sortitionModule.address);
111110
}
112111

113112
const core = (await hre.ethers.getContract("KlerosCore")) as KlerosCore;

contracts/deploy/upgrade-sortition-module.ts

-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { HomeChains, isSkipped } from "./utils";
55

66
const deployUpgradeSortitionModule: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
77
const { deployments, getNamedAccounts, getChainId } = hre;
8-
const RNG_LOOKAHEAD = 20;
98

109
// fallback to hardhat node signers on local network
1110
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
@@ -26,7 +25,6 @@ const deployUpgradeSortitionModule: DeployFunction = async (hre: HardhatRuntimeE
2625
1800, // minStakingTime
2726
1800, // maxFreezingTime
2827
rng.address,
29-
RNG_LOOKAHEAD,
3028
],
3129
});
3230
} catch (err) {

contracts/src/arbitration/SortitionModule.sol

+2-4
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,14 @@ contract SortitionModule is SortitionModuleBase, UUPSProxiable, Initializable {
3232
/// @param _minStakingTime Minimal time to stake
3333
/// @param _maxDrawingTime Time after which the drawing phase can be switched
3434
/// @param _rng The random number generator.
35-
/// @param _rngLookahead Lookahead value for rng.
3635
function initialize(
3736
address _governor,
3837
KlerosCore _core,
3938
uint256 _minStakingTime,
4039
uint256 _maxDrawingTime,
41-
RNG _rng,
42-
uint256 _rngLookahead
40+
RNG _rng
4341
) external reinitializer(1) {
44-
super._initialize(_governor, _core, _minStakingTime, _maxDrawingTime, _rng, _rngLookahead);
42+
super._initialize(_governor, _core, _minStakingTime, _maxDrawingTime, _rng);
4543
}
4644

4745
// ************************************* //

contracts/src/arbitration/SortitionModuleBase.sol

+8-15
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,10 @@ abstract contract SortitionModuleBase is ISortitionModule {
6363
uint256 public minStakingTime; // The time after which the phase can be switched to Drawing if there are open disputes.
6464
uint256 public maxDrawingTime; // The time after which the phase can be switched back to Staking.
6565
uint256 public lastPhaseChange; // The last time the phase was changed.
66-
uint256 public randomNumberRequestBlock; // Number of the block when RNG request was made.
6766
uint256 public disputesWithoutJurors; // The number of disputes that have not finished drawing jurors.
6867
RNG public rng; // The random number generator.
6968
uint256 public randomNumber; // Random number returned by RNG.
70-
uint256 public rngLookahead; // Minimal block distance between requesting and obtaining a random number.
69+
uint256 public rngLookahead; // DEPRECATED
7170
uint256 public delayedStakeWriteIndex; // The index of the last `delayedStake` item that was written to the array. 0 index is skipped.
7271
uint256 public delayedStakeReadIndex; // The index of the next `delayedStake` item that should be processed. Starts at 1 because 0 index is skipped.
7372
mapping(bytes32 treeHash => SortitionSumTree) sortitionSumTrees; // The mapping trees by keys.
@@ -94,16 +93,14 @@ abstract contract SortitionModuleBase is ISortitionModule {
9493
KlerosCore _core,
9594
uint256 _minStakingTime,
9695
uint256 _maxDrawingTime,
97-
RNG _rng,
98-
uint256 _rngLookahead
96+
RNG _rng
9997
) internal {
10098
governor = _governor;
10199
core = _core;
102100
minStakingTime = _minStakingTime;
103101
maxDrawingTime = _maxDrawingTime;
104102
lastPhaseChange = block.timestamp;
105103
rng = _rng;
106-
rngLookahead = _rngLookahead;
107104
delayedStakeReadIndex = 1;
108105
}
109106

@@ -137,15 +134,12 @@ abstract contract SortitionModuleBase is ISortitionModule {
137134
maxDrawingTime = _maxDrawingTime;
138135
}
139136

140-
/// @dev Changes the `_rng` and `_rngLookahead` storage variables.
141-
/// @param _rng The new value for the `RNGenerator` storage variable.
142-
/// @param _rngLookahead The new value for the `rngLookahead` storage variable.
143-
function changeRandomNumberGenerator(RNG _rng, uint256 _rngLookahead) external onlyByGovernor {
137+
/// @dev Changes the `rng` storage variable.
138+
/// @param _rng The new random number generator.
139+
function changeRandomNumberGenerator(RNG _rng) external onlyByGovernor {
144140
rng = _rng;
145-
rngLookahead = _rngLookahead;
146141
if (phase == Phase.generating) {
147-
rng.requestRandomness(block.number + rngLookahead);
148-
randomNumberRequestBlock = block.number;
142+
rng.requestRandomness();
149143
}
150144
}
151145

@@ -160,11 +154,10 @@ abstract contract SortitionModuleBase is ISortitionModule {
160154
"The minimum staking time has not passed yet."
161155
);
162156
require(disputesWithoutJurors > 0, "There are no disputes that need jurors.");
163-
rng.requestRandomness(block.number + rngLookahead);
164-
randomNumberRequestBlock = block.number;
157+
rng.requestRandomness();
165158
phase = Phase.generating;
166159
} else if (phase == Phase.generating) {
167-
randomNumber = rng.receiveRandomness(randomNumberRequestBlock + rngLookahead);
160+
randomNumber = rng.receiveRandomness();
168161
require(randomNumber != 0, "Random number is not ready yet");
169162
phase = Phase.drawing;
170163
} else if (phase == Phase.drawing) {

contracts/src/arbitration/SortitionModuleNeo.sol

+1-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ contract SortitionModuleNeo is SortitionModuleBase, UUPSProxiable, Initializable
4040
/// @param _minStakingTime Minimal time to stake
4141
/// @param _maxDrawingTime Time after which the drawing phase can be switched
4242
/// @param _rng The random number generator.
43-
/// @param _rngLookahead Lookahead value for rng.
4443
/// @param _maxStakePerJuror The maximum amount of PNK a juror can stake in a court.
4544
/// @param _maxTotalStaked The maximum amount of PNK that can be staked in all courts.
4645
function initialize(
@@ -49,11 +48,10 @@ contract SortitionModuleNeo is SortitionModuleBase, UUPSProxiable, Initializable
4948
uint256 _minStakingTime,
5049
uint256 _maxDrawingTime,
5150
RNG _rng,
52-
uint256 _rngLookahead,
5351
uint256 _maxStakePerJuror,
5452
uint256 _maxTotalStaked
5553
) external reinitializer(2) {
56-
super._initialize(_governor, _core, _minStakingTime, _maxDrawingTime, _rng, _rngLookahead);
54+
super._initialize(_governor, _core, _minStakingTime, _maxDrawingTime, _rng);
5755
maxStakePerJuror = _maxStakePerJuror;
5856
maxTotalStaked = _maxTotalStaked;
5957
}

contracts/src/rng/BlockhashRNG.sol

+15-11
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,44 @@ pragma solidity 0.8.24;
55
import "./RNG.sol";
66

77
/// @title Random Number Generator using blockhash with fallback.
8-
/// @author Clément Lesaege - <[email protected]>
98
/// @dev
109
/// Random Number Generator returning the blockhash with a fallback behaviour.
1110
/// In case no one called it within the 256 blocks, it returns the previous blockhash.
1211
/// This contract must be used when returning 0 is a worse failure mode than returning another blockhash.
1312
/// Allows saving the random number for use in the future. It allows the contract to still access the blockhash even after 256 blocks.
1413
contract BlockHashRNG is RNG {
14+
uint256 public immutable lookahead; // Minimal block distance between requesting and obtaining a random number.
15+
uint256 public requestBlock; // Block number of the current request
1516
mapping(uint256 block => uint256 number) public randomNumbers; // randomNumbers[block] is the random number for this block, 0 otherwise.
1617

18+
constructor(uint256 _lookahead) {
19+
lookahead = _lookahead + lookahead;
20+
}
21+
1722
/// @dev Request a random number.
18-
/// @param _block Block the random number is linked to.
19-
function requestRandomness(uint256 _block) external override {
20-
// nop
23+
function requestRandomness() external override {
24+
requestBlock = block.number;
2125
}
2226

2327
/// @dev Return the random number. If it has not been saved and is still computable compute it.
24-
/// @param _block Block the random number is linked to.
2528
/// @return randomNumber The random number or 0 if it is not ready or has not been requested.
26-
function receiveRandomness(uint256 _block) external override returns (uint256 randomNumber) {
27-
randomNumber = randomNumbers[_block];
29+
function receiveRandomness() external override returns (uint256 randomNumber) {
30+
uint256 expectedBlock = requestBlock;
31+
randomNumber = randomNumbers[expectedBlock];
2832
if (randomNumber != 0) {
2933
return randomNumber;
3034
}
3135

32-
if (_block < block.number) {
36+
if (expectedBlock < block.number) {
3337
// The random number is not already set and can be.
34-
if (blockhash(_block) != 0x0) {
38+
if (blockhash(expectedBlock) != 0x0) {
3539
// Normal case.
36-
randomNumber = uint256(blockhash(_block));
40+
randomNumber = uint256(blockhash(expectedBlock));
3741
} else {
3842
// The contract was not called in time. Fallback to returning previous blockhash.
3943
randomNumber = uint256(blockhash(block.number - 1));
4044
}
4145
}
42-
randomNumbers[_block] = randomNumber;
46+
randomNumbers[expectedBlock] = randomNumber;
4347
}
4448
}

contracts/src/rng/ChainlinkRNG.sol

+18-18
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
1515
// ************************************* //
1616

1717
address public governor; // The address that can withdraw funds.
18-
address public sortitionModule; // The address of the SortitionModule.
18+
address public consumer; // The address that can request random numbers.
1919
bytes32 public keyHash; // The gas lane key hash value - Defines the maximum gas price you are willing to pay for a request in wei (ID of the off-chain VRF job).
2020
uint256 public subscriptionId; // The unique identifier of the subscription used for funding requests.
2121
uint16 public requestConfirmations; // How many confirmations the Chainlink node should wait before responding.
@@ -29,13 +29,13 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
2929
// ************************************* //
3030

3131
/// @dev Emitted when a request is sent to the VRF Coordinator
32-
/// @param requestId The ID of the request
33-
event RequestSent(uint256 indexed requestId);
32+
/// @param _requestId The ID of the request
33+
event RequestSent(uint256 indexed _requestId);
3434

3535
/// Emitted when a request has been fulfilled.
36-
/// @param requestId The ID of the request
37-
/// @param randomWord The random value answering the request.
38-
event RequestFulfilled(uint256 indexed requestId, uint256 randomWord);
36+
/// @param _requestId The ID of the request
37+
/// @param _randomWord The random value answering the request.
38+
event RequestFulfilled(uint256 indexed _requestId, uint256 _randomWord);
3939

4040
// ************************************* //
4141
// * Function Modifiers * //
@@ -46,8 +46,8 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
4646
_;
4747
}
4848

49-
modifier onlyBySortitionModule() {
50-
require(sortitionModule == msg.sender, "SortitionModule only");
49+
modifier onlyByConsumer() {
50+
require(consumer == msg.sender, "Consumer only");
5151
_;
5252
}
5353

@@ -57,7 +57,7 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
5757

5858
/// @dev Constructor, initializing the implementation to reduce attack surface.
5959
/// @param _governor The Governor of the contract.
60-
/// @param _sortitionModule The address of the SortitionModule contract.
60+
/// @param _consumer The address that can request random numbers.
6161
/// @param _vrfCoordinator The address of the VRFCoordinator contract.
6262
/// @param _keyHash The gas lane key hash value - Defines the maximum gas price you are willing to pay for a request in wei (ID of the off-chain VRF job).
6363
/// @param _subscriptionId The unique identifier of the subscription used for funding requests.
@@ -66,15 +66,15 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
6666
/// @dev https://docs.chain.link/vrf/v2-5/subscription/get-a-random-number
6767
constructor(
6868
address _governor,
69-
address _sortitionModule,
69+
address _consumer,
7070
address _vrfCoordinator,
7171
bytes32 _keyHash,
7272
uint256 _subscriptionId,
7373
uint16 _requestConfirmations,
7474
uint32 _callbackGasLimit
7575
) VRFConsumerBaseV2Plus(_vrfCoordinator) {
7676
governor = _governor;
77-
sortitionModule = _sortitionModule;
77+
consumer = _consumer;
7878
keyHash = _keyHash;
7979
subscriptionId = _subscriptionId;
8080
requestConfirmations = _requestConfirmations;
@@ -91,10 +91,10 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
9191
governor = _governor;
9292
}
9393

94-
/// @dev Changes the sortition module of the contract.
95-
/// @param _sortitionModule The new sortition module.
96-
function changeSortitionModule(address _sortitionModule) external onlyByGovernor {
97-
sortitionModule = _sortitionModule;
94+
/// @dev Changes the consumer of the RNG.
95+
/// @param _consumer The new consumer.
96+
function changeConsumer(address _consumer) external onlyByGovernor {
97+
consumer = _consumer;
9898
}
9999

100100
/// @dev Changes the VRF Coordinator of the contract.
@@ -132,8 +132,8 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
132132
// * State Modifiers * //
133133
// ************************************* //
134134

135-
/// @dev Request a random number. SortitionModule only.
136-
function requestRandomness(uint256 /*_block*/) external override onlyBySortitionModule {
135+
/// @dev Request a random number. Consumer only.
136+
function requestRandomness() external override onlyByConsumer {
137137
// Will revert if subscription is not set and funded.
138138
uint256 requestId = s_vrfCoordinator.requestRandomWords(
139139
VRFV2PlusClient.RandomWordsRequest({
@@ -167,7 +167,7 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
167167

168168
/// @dev Return the random number.
169169
/// @return randomNumber The random number or 0 if it is not ready or has not been requested.
170-
function receiveRandomness(uint256 /*_block*/) external view override returns (uint256 randomNumber) {
170+
function receiveRandomness() external view override returns (uint256 randomNumber) {
171171
randomNumber = randomNumbers[lastRequestId];
172172
}
173173
}

contracts/src/rng/IncrementalNG.sol

+2-4
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,13 @@ contract IncrementalNG is RNG {
1515
}
1616

1717
/// @dev Request a random number.
18-
/// @param _block Block the random number is linked to.
19-
function requestRandomness(uint256 _block) external override {
18+
function requestRandomness() external override {
2019
// nop
2120
}
2221

2322
/// @dev Get the "random number" (which is always the same).
24-
/// @param _block Block the random number is linked to.
2523
/// @return randomNumber The random number or 0 if it is not ready or has not been requested.
26-
function receiveRandomness(uint256 _block) external override returns (uint256 randomNumber) {
24+
function receiveRandomness() external override returns (uint256 randomNumber) {
2725
unchecked {
2826
return number++;
2927
}

contracts/src/rng/RNG.sol

+2-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ pragma solidity 0.8.24;
44

55
interface RNG {
66
/// @dev Request a random number.
7-
/// @param _block Block linked to the request.
8-
function requestRandomness(uint256 _block) external;
7+
function requestRandomness() external;
98

109
/// @dev Receive the random number.
11-
/// @param _block Block the random number is linked to.
1210
/// @return randomNumber Random Number. If the number is not ready or has not been required 0 instead.
13-
function receiveRandomness(uint256 _block) external returns (uint256 randomNumber);
11+
function receiveRandomness() external returns (uint256 randomNumber);
1412
}

0 commit comments

Comments
 (0)