RandomAura RNG Explainer
The following example uses methods from the RandomAura contract. A RANDAO methodology implemented by the validator nodes generates pseudorandom numbers.
The following is designed to work with OpenEthereum's AuRa consensus protocol v3.2.5. In this protocol, validators take turns sealing blocks, one after the other, in a prescribed order.

Collection Rounds

A series of collection rounds are used for random number generation. The collection round length is configurable - in this example we use 38 blocks for a round, split into two equal 19 block phases:
  • commit phase
  • reveal phase
The length of each phase is 19 blocks ( 38 / 2 = 19). When one collection round finishes, the next collection round starts, and so on. For example:
1
Block number | Phase
2
...
3
101 Commit phase <-- collection round #1 started here
4
102 Commit phase
5
103 Commit phase
6
...
7
117 Commit phase
8
118 Commit phase
9
119 Commit phase
10
120 Reveal phase
11
121 Reveal phase
12
122 Reveal phase
13
...
14
136 Reveal phase
15
137 Reveal phase
16
138 Reveal phase <-- collection round #1 finished here
17
139 Commit phase <-- collection round #2 started here
18
140 Commit phase
19
141 Commit phase
20
...
Copied!
During each collection round, the RandomAura contract (see below) collects the random numbers generated during that round.

Commit Phase

1) Each validator in the set generates a random number locally with their node, hashes the secret, and calls the commitHash function when it is their turn to create a block.
1
/// @dev Called by the validator's node to store a hash and a cipher of the validator's number on each collection
2
/// round. The validator's node must use its mining address (engine_signer) to call this function.
3
/// This function can only be called once per collection round (during the `commit phase`).
4
/// @param _numberHash The Keccak-256 hash of the validator's number.
5
/// @param _cipher The cipher of the validator's number. Can be used by the node to restore the lost number after
6
/// the node is restarted (see the `getCipher` getter).
7
function commitHash(bytes32 _numberHash, bytes calldata _cipher) external {
8
address miningAddress = msg.sender;
9
10
require(block.coinbase == miningAddress);
11
require(isCommitPhase()); // must only be called in `commit phase`
12
require(_numberHash != bytes32(0));
13
require(!isCommitted(currentCollectRound(), miningAddress)); // cannot commit more than once
14
15
uint256 collectRound = currentCollectRound();
16
17
_commits[collectRound][miningAddress] = _numberHash;
18
_ciphers[collectRound][miningAddress] = _cipher;
19
}
Copied!
2) This function accepts the hash of the secret number and its cipher. The cipher is the number encrypted with a validator's key, and is needed for the reveal phase (see below).
For example, if there are three validators, they will call commitHash in the following order (for the sample case above):
1
Block number | Phase | What happens
2
...
3
101 Commit phase Validator1 calls `commitHash`
4
102 Commit phase Validator2 calls `commitHash`
5
103 Commit phase Validator3 calls `commitHash`
6
104 Commit phase Nothing happens
7
105 Commit phase Nothing happens
8
106 Commit phase Nothing happens
9
...
10
117 Commit phase Nothing happens
11
118 Commit phase Nothing happens
12
119 Commit phase Nothing happens
13
120 Reveal phase Validator1 calls `revealSecret`
14
121 Reveal phase Validator2 calls `revealSecret`
15
122 Reveal phase Validator3 calls `revealSecret`
16
123 Reveal phase Nothing happens
17
125 Reveal phase Nothing happens
18
126 Reveal phase Nothing happens
19
...
20
136 Reveal phase Nothing happens
21
137 Reveal phase Nothing happens
22
138 Reveal phase Nothing happens
23
139 Commit phase Validator1 calls `commitHash`
24
140 Commit phase Validator2 calls `commitHash`
25
141 Commit phase Validator3 calls `commitHash`
26
...
Copied!
When the commit phase finishes, the reveal phase starts.

Reveal Phase

When a validator's turn arrives to create a block:
1) The validator gets the cipher of the number using the getCommitAndCipher public getter.
1
/// @dev Returns the Keccak-256 hash and cipher of the validator's number for the specified collection round
2
/// and the specified validator stored by the validator through the `commitHash` function.
3
/// @param _collectRound The serial number of the collection round for which hash and cipher should be retrieved.
4
/// @param _miningAddress The mining address of validator (engine_signer).
5
function getCommitAndCipher(
6
uint256 _collectRound,
7
address _miningAddress
8
) public view returns(bytes32, bytes memory) {
9
return (_commits[_collectRound][_miningAddress], _ciphers[_collectRound][_miningAddress]);
10
}
Copied!
2) The validator decrypts the cipher with their key and retrieves the number.
3) The validator calls the revealNumberfunction to reveal their committed number (the function XORs the number with the previous seed to create a new random seed stored in the currentSeed state variable).
1
/// @dev Called by the validator's node to XOR its number with the current random seed.
2
/// The validator's node must use its mining address (engine_signer) to call this function.
3
/// This function can only be called once per collection round (during the `reveal phase`).
4
/// @param _number The validator's number.
5
function revealNumber(uint256 _number) external {
6
address miningAddress = msg.sender;
7
8
bytes32 numberHash = keccak256(abi.encodePacked(_number));
9
uint256 collectRound = currentCollectRound();
10
11
require(block.coinbase == miningAddress);
12
require(!isCommitPhase()); // must only be called in `reveal phase`
13
require(numberHash != bytes32(0));
14
require(numberHash == _commits[collectRound][miningAddress]); // the hash must be commited
15
require(!_sentReveal[collectRound][miningAddress]); // cannot reveal more than once during the same collect round
16
17
currentSeed = currentSeed ^ _number;
18
19
_sentReveal[collectRound][miningAddress] = true;
20
delete _commits[collectRound][miningAddress];
21
delete _ciphers[collectRound][miningAddress];
22
}
Copied!
Note: Randomness created in a deterministic manner, through computerized means, it is called pseudorandomness. Pseudorandom numbers exhibit the same properties as random numbers. The method described above is technically a pseudorandom number generator (PRNG)

RandomAura Contract Code

The RandomAura Contract interfaces with the Authority Round consensus process to store and iterate the currentSeed , control when the seed is revealed, and report on skipped reveals by Validators.
Below is the full RandomAura contract code. The POSDAO implementation is located at https://github.com/poanetwork/posdao-contracts/blob/master/contracts/RandomAuRa.sol
1
pragma solidity 0.5.10;
2
3
import "./interfaces/IRandomAuRa.sol";
4
import "./interfaces/IStakingAuRa.sol";
5
import "./interfaces/IValidatorSetAuRa.sol";
6
import "./upgradeability/UpgradeableOwned.sol";
7
8
9
/// @dev Generates and stores random numbers in a RANDAO manner (and controls when they are revealed by AuRa
10
/// validators) and accumulates a random seed. The random seed is used to form a new validator set by the
11
/// `ValidatorSetAuRa.newValidatorSet` function.
12
contract RandomAuRa is UpgradeableOwned, IRandomAuRa {
13
14
// =============================================== Storage ========================================================
15
16
// WARNING: since this contract is upgradeable, do not remove
17
// existing storage variables, do not change their order,
18
// and do not change their types!
19
20
mapping(uint256 => mapping(uint256 => bytes)) internal _ciphers;
21
mapping(uint256 => mapping(uint256 => bytes32)) internal _commits;
22
mapping(uint256 => uint256[]) internal _committedValidators;
23
24
/// @dev The length of the collection round (in blocks).
25
uint256 public collectRoundLength;
26
27
/// @dev The current random seed accumulated during RANDAO or another process
28
/// (depending on implementation).
29
uint256 public currentSeed;
30
31
/// @dev A boolean flag defining whether to punish validators for unrevealing.
32
bool public punishForUnreveal;
33
34
/// @dev The number of reveal skips made by the specified validator during the specified staking epoch.
35
mapping(uint256 => mapping(uint256 => uint256)) internal _revealSkips;
36
37
/// @dev A boolean flag of whether the specified validator has revealed their number for the
38
/// specified collection round.
39
mapping(uint256 => mapping(uint256 => bool)) internal _sentReveal;
40
41
/// @dev The address of the `ValidatorSetAuRa` contract.
42
IValidatorSetAuRa public validatorSetContract;
43
44
// ============================================== Modifiers =======================================================
45
46
/// @dev Ensures the caller is the BlockRewardAuRa contract address.
47
modifier onlyBlockReward() {
48
require(msg.sender == validatorSetContract.blockRewardContract());
49
_;
50
}
51
52
/// @dev Ensures the `initialize` function was called before.
53
modifier onlyInitialized {
54
require(isInitialized());
55
_;
56
}
57
58
/// @dev Ensures the caller is the ValidatorSetAuRa contract address.
59
modifier onlyValidatorSet() {
60
require(msg.sender == address(validatorSetContract));
61
_;
62
}
63
64
// =============================================== Setters ========================================================
65
66
/// @dev Clears commit and cipher for the given validator's pool if the pool
67
/// hasn't yet revealed their number.
68
/// Called by the ValidatorSetAuRa.changeMiningAddress function
69
/// when a validator creates a request to change their mining address.
70
function clearCommit(uint256 _poolId) external onlyValidatorSet {
71
uint256 collectRound = currentCollectRound();
72
if (!_sentReveal[collectRound][_poolId]) {
73
delete _commits[collectRound][_poolId];
74
delete _ciphers[collectRound][_poolId];
75
}
76
}
77
78
/// @dev Called by the validator's node to store a hash and a cipher of the validator's number on each collection
79
/// round. The validator's node must use its mining address to call this function.
80
/// This function can only be called once per collection round (during the `commits phase`).
81
/// @param _numberHash The Keccak-256 hash of the validator's number.
82
/// @param _cipher The cipher of the validator's number. Can be used by the node to restore the lost number after
83
/// the node is restarted (see the `getCipher` getter).
84
function commitHash(bytes32 _numberHash, bytes calldata _cipher) external onlyInitialized {
85
address miningAddress = msg.sender;
86
87
require(commitHashCallable(miningAddress, _numberHash));
88
require(_getCoinbase() == miningAddress); // make sure validator node is live
89
90
uint256 collectRound = currentCollectRound();
91
uint256 poolId = validatorSetContract.idByMiningAddress(miningAddress);
92
93
_commits[collectRound][poolId] = _numberHash;
94
_ciphers[collectRound][poolId] = _cipher;
95
_committedValidators[collectRound].push(poolId);
96
}
97
98
/// @dev Called by the validator's node to XOR its number with the current random seed.
99
/// The validator's node must use its mining address to call this function.
100
/// This function can only be called once per collection round (during the `reveals phase`).
101
/// @param _number The validator's number.
102
function revealNumber(uint256 _number) external onlyInitialized {
103
_revealNumber(_number);
104
}
105
106
/// @dev The same as the `revealNumber` function (see its description).
107
/// The `revealSecret` was renamed to `revealNumber`, so this function
108
/// is left for backward compatibility with the previous client
109
/// implementation and should be deleted in the future.
110
/// @param _number The validator's number.
111
function revealSecret(uint256 _number) external onlyInitialized {
112
_revealNumber(_number);
113
}
114
115
/// @dev Changes the `punishForUnreveal` boolean flag. Can only be called by an owner.
116
function setPunishForUnreveal(bool _punishForUnreveal) external onlyOwner {
117
punishForUnreveal = _punishForUnreveal;
118
}
119
120
/// @dev Initializes the contract at network startup.
121
/// Can only be called by the constructor of the `InitializerAuRa` contract or owner.
122
/// @param _collectRoundLength The length of a collection round in blocks.
123
/// @param _validatorSet The address of the `ValidatorSet` contract.
124
/// @param _punishForUnreveal A boolean flag defining whether to punish validators for unrevealing.
125
function initialize(
126
uint256 _collectRoundLength, // in blocks
127
address _validatorSet,
128
bool _punishForUnreveal
129
) external {
130
require(_getCurrentBlockNumber() == 0 || msg.sender == _admin());
131
require(!isInitialized());
132
IValidatorSetAuRa validatorSet = IValidatorSetAuRa(_validatorSet);
133
require(_collectRoundLength % 2 == 0);
134
require(_collectRoundLength % validatorSet.MAX_VALIDATORS() == 0);
135
require(IStakingAuRa(validatorSet.stakingContract()).stakingEpochDuration() % _collectRoundLength == 0);
136
require(_collectRoundLength > 0);
137
require(collectRoundLength == 0);
138
require(_validatorSet != address(0));
139
collectRoundLength = _collectRoundLength;
140
validatorSetContract = IValidatorSetAuRa(_validatorSet);
141
punishForUnreveal = _punishForUnreveal;
142
}
143
144
/// @dev Checks whether the current validators at the end of each collection round revealed their numbers,
145
/// and removes malicious validators if needed.
146
/// This function does nothing if the current block is not the last block of the current collection round.
147
/// Can only be called by the `BlockRewardAuRa` contract (by its `reward` function).
148
function onFinishCollectRound() external onlyBlockReward {
149
if (_getCurrentBlockNumber() % collectRoundLength != 0) return;
150
151
// This is the last block of the current collection round
152
153
address[] memory validators;
154
address validator;
155
uint256 i;
156
157
address stakingContract = validatorSetContract.stakingContract();
158
159
uint256 stakingEpoch = IStakingAuRa(stakingContract).stakingEpoch();
160
uint256 startBlock = IStakingAuRa(stakingContract).stakingEpochStartBlock();
161
uint256 endBlock = IStakingAuRa(stakingContract).stakingEpochEndBlock();
162
uint256 currentRound = currentCollectRound();
163
164
if (_getCurrentBlockNumber() > startBlock + collectRoundLength * 3) {
165
// Check whether each validator didn't reveal their number
166
// during the current collection round
167
validators = validatorSetContract.getValidators();
168
for (i = 0; i < validators.length; i++) {
169
validator = validators[i];
170
if (!sentReveal(currentRound, validator)) {
171
uint256 poolId = validatorSetContract.idByMiningAddress(validator);
172
_revealSkips[stakingEpoch][poolId]++;
173
}
174
}
175
}
176
177
// If this is the last collection round in the current staking epoch
178
// and punishing for unreveal is enabled.
179
if (
180
punishForUnreveal &&
181
(_getCurrentBlockNumber() == endBlock || _getCurrentBlockNumber() + collectRoundLength > endBlock)
182
) {
183
uint256 maxRevealSkipsAllowed =
184
IStakingAuRa(stakingContract).stakeWithdrawDisallowPeriod() / collectRoundLength;
185
186
if (maxRevealSkipsAllowed > 1) {
187
maxRevealSkipsAllowed -= 2;
188
} else if (maxRevealSkipsAllowed > 0) {
189
maxRevealSkipsAllowed--;
190
}
191
192
// Check each validator to see if they didn't reveal
193
// their number during the last full `reveals phase`
194
// or if they missed the required number of reveals per staking epoch.
195
validators = validatorSetContract.getValidators();
196
197
address[] memory maliciousValidators = new address[](validators.length);
198
uint256 maliciousValidatorsLength = 0;
199
200
for (i = 0; i < validators.length; i++) {
201
validator = validators[i];
202
if (
203
!sentReveal(currentRound, validator) ||
204
revealSkips(stakingEpoch, validator) > maxRevealSkipsAllowed
205
) {
206
// Mark the validator as malicious
207
maliciousValidators[maliciousValidatorsLength++] = validator;
208
}
209
}
210
211
if (maliciousValidatorsLength > 0) {
212
address[] memory miningAddresses = new address[](maliciousValidatorsLength);
213
for (i = 0; i < maliciousValidatorsLength; i++) {
214
miningAddresses[i] = maliciousValidators[i];
215
}
216
validatorSetContract.removeMaliciousValidators(miningAddresses);
217
}
218
}
219
220
// Clear unnecessary info about previous collection round.
221
_clearOldCiphers(currentRound);
222
}
223
224
// =============================================== Getters ========================================================
225
226
/// @dev Returns the length of the commits/reveals phase which is always half of the collection round length.
227
function commitPhaseLength() public view returns(uint256) {
228
return collectRoundLength / 2;
229
}
230
231
/// @dev Returns the serial number of the current collection round.
232
function currentCollectRound() public view returns(uint256) {
233
return (_getCurrentBlockNumber() - 1) / collectRoundLength;
234
}
235
236
/// @dev Returns the number of the first block of the current collection round.
237
function currentCollectRoundStartBlock() public view returns(uint256) {
238
return currentCollectRound() * collectRoundLength + 1;
239
}
240
241
/// @dev Returns the cipher of the validator's number for the specified collection round and the specified validator
242
/// stored by the validator through the `commitHash` function.
243
/// For the past collection rounds the cipher is empty as it's erased by the internal `_clearOldCiphers` function.
244
/// @param _collectRound The serial number of the collection round for which the cipher should be retrieved.
245
/// @param _miningAddress The mining address of validator.
246
function getCipher(uint256 _collectRound, address _miningAddress) public view returns(bytes memory) {
247
uint256 poolId = validatorSetContract.idByMiningAddress(_miningAddress);
248
return _ciphers[_collectRound][poolId];
249
}
250
251
/// @dev Returns the Keccak-256 hash of the validator's number for the specified collection round and the specified
252
/// validator stored by the validator through the `commitHash` function. Note that for the past collection rounds
253
/// it can return empty results because there was a migration from mining addresses to staking addresses.
254
/// @param _collectRound The serial number of the collection round for which the hash should be retrieved.
255
/// @param _miningAddress The mining address of validator.
256
function getCommit(uint256 _collectRound, address _miningAddress) public view returns(bytes32) {
257
uint256 poolId = validatorSetContract.idByMiningAddress(_miningAddress);
258
return _commits[_collectRound][poolId];
259
}
260
261
/// @dev Returns the Keccak-256 hash and cipher of the validator's number for the specified collection round
262
/// and the specified validator stored by the validator through the `commitHash` function.
263
/// For the past collection rounds the cipher is empty. Note that for the past collection rounds
264
/// it can return empty results because there was a migration from mining addresses to staking addresses.
265
/// @param _collectRound The serial number of the collection round for which hash and cipher should be retrieved.
266
/// @param _miningAddress The mining address of validator.
267
function getCommitAndCipher(
268
uint256 _collectRound,
269
address _miningAddress
270
) public view returns(bytes32, bytes memory) {
271
uint256 poolId = validatorSetContract.idByMiningAddress(_miningAddress);
272
return (_commits[_collectRound][poolId], _ciphers[_collectRound][poolId]);
273
}
274
275
/// @dev Returns a boolean flag indicating whether the specified validator has committed their number's hash for the
276
/// specified collection round. Note that for the past collection rounds it can return false-negative results
277
/// because there was a migration from mining addresses to staking addresses (on xDai chain).
278
/// Also, it intentionally returns a false-positive result during the commit phase when the specified
279
/// mining address of a validator is about to be changed. This is needed to prevent committing hash/cipher pair
280
/// by Ethereum node to guarantee that a new validator's node (with a new mining address) won't try to wrongly
281
/// decrypt the cipher stored by the previous node (created by the private key of the previous mining address).
282
/// @param _collectRound The serial number of the collection round for which the checkup should be done.
283
/// @param _miningAddress The mining address of the validator.
284
function isCommitted(uint256 _collectRound, address _miningAddress) public view returns(bool) {
285
uint256 poolId = validatorSetContract.idByMiningAddress(_miningAddress);
286
287
if (poolId != 0 && isCommitPhase()) {
288
(uint256 requestPoolId,) = validatorSetContract.miningAddressChangeRequest();
289
if (poolId == requestPoolId) {
290
return true;
291
}
292
}
293
294
return _commits[_collectRound][poolId] != bytes32(0);
295
}
296
297
/// @dev Returns a boolean flag indicating whether the current phase of the current collection round
298
/// is a `commits phase`. Used by the validator's node to determine if it should commit the hash of
299
/// the number during the current collection round.
300
function isCommitPhase() public view returns(bool) {
301
return ((_getCurrentBlockNumber() - 1) % collectRoundLength) < commitPhaseLength();
302
}
303
304
/// @dev Returns a boolean flag indicating if the `initialize` function has been called.
305
function isInitialized() public view returns(bool) {
306
return validatorSetContract != IValidatorSetAuRa(0);
307
}
308
309
/// @dev Returns a boolean flag indicating whether the current phase of the current collection round
310
/// is a `reveals phase`. Used by the validator's node to determine if it should reveal the number during
311
/// the current collection round.
312
function isRevealPhase() public view returns(bool) {
313
return !isCommitPhase();
314
}
315
316
/// @dev Returns a boolean flag of whether the `commitHash` function can be called at the current block
317
/// by the specified validator. Used by the `commitHash` function and the `TxPermission` contract.
318
/// @param _miningAddress The mining address of the validator which tries to call the `commitHash` function.
319
/// @param _numberHash The Keccak-256 hash of validator's number passed to the `commitHash` function.
320
function commitHashCallable(address _miningAddress, bytes32 _numberHash) public view returns(bool) {
321
if (!isCommitPhase()) return false; // must only be called in `commits phase`
322
323
if (_numberHash == bytes32(0)) return false;
324
325
if (!validatorSetContract.isValidator(_miningAddress)) return false;
326
327
if (isCommitted(currentCollectRound(), _miningAddress)) return false; // cannot commit more than once
328
329
return true;
330
}
331
332
/// @dev Returns the number of the first block of the next (future) collection round.
333
function nextCollectRoundStartBlock() public view returns(uint256) {
334
uint256 currentBlock = _getCurrentBlockNumber();
335
uint256 remainingBlocksToNextRound = collectRoundLength - (currentBlock - 1) % collectRoundLength;
336
return currentBlock + remainingBlocksToNextRound;
337
}
338
339
/// @dev Returns the number of the first block of the next (future) commit phase.
340
function nextCommitPhaseStartBlock() public view returns(uint256) {
341
return nextCollectRoundStartBlock();
342
}
343
344
/// @dev Returns the number of the first block of the next (future) reveal phase.
345
function nextRevealPhaseStartBlock() public view returns(uint256) {
346
if (isCommitPhase()) {
347
return currentCollectRoundStartBlock() + commitPhaseLength();
348
} else {
349
return nextCollectRoundStartBlock() + commitPhaseLength();
350
}
351
}
352
353
/// @dev Returns a boolean flag of whether the `revealNumber` function can be called at the current block
354
/// by the specified validator. Used by the `revealNumber` function and the `TxPermission` contract.
355
/// @param _miningAddress The mining address of validator which tries to call the `revealNumber` function.
356
/// @param _number The validator's number passed to the `revealNumber` function.
357
function revealNumberCallable(address _miningAddress, uint256 _number) public view returns(bool) {
358
return _revealNumberCallable(_miningAddress, _number);
359
}
360
361
/// @dev The same as the `revealNumberCallable` getter (see its description).
362
/// The `revealSecretCallable` was renamed to `revealNumberCallable`, so this function
363
/// is left for backward compatibility with the previous client
364
/// implementation and should be deleted in the future.
365
/// @param _miningAddress The mining address of validator which tries to call the `revealSecret` function.
366
/// @param _number The validator's number passed to the `revealSecret` function.
367
function revealSecretCallable(address _miningAddress, uint256 _number) public view returns(bool) {
368
return _revealNumberCallable(_miningAddress, _number);
369
}
370
371
/// @dev Returns the number of reveal skips made by the specified validator during the specified staking epoch.
372
/// @param _stakingEpoch The number of staking epoch.
373
/// @param _miningAddress The mining address of the validator.
374
function revealSkips(uint256 _stakingEpoch, address _miningAddress) public view returns(uint256) {
375
uint256 poolId = validatorSetContract.idByMiningAddress(_miningAddress);
376
return _revealSkips[_stakingEpoch][poolId];
377
}
378
379
/// @dev Returns a boolean flag indicating whether the specified validator has revealed their number for the
380
/// specified collection round. Note that for the past collection rounds it can return false-negative results
381
/// because there was a migration from mining addresses to staking addresses.
382
/// @param _collectRound The serial number of the collection round for which the checkup should be done.
383
/// @param _miningAddress The mining address of the validator.
384
function sentReveal(uint256 _collectRound, address _miningAddress) public view returns(bool) {
385
uint256 poolId = validatorSetContract.idByMiningAddress(_miningAddress);
386
return _sentReveal[_collectRound][poolId];
387
}
388
389
// ============================================== Internal ========================================================
390
391
/// @dev Removes the ciphers of all committed validators for the collection round
392
/// preceding to the specified collection round.
393
/// @param _collectRound The serial number of the collection round.
394
function _clearOldCiphers(uint256 _collectRound) internal {
395
if (_collectRound == 0) {
396
return;
397
}
398
399
uint256 collectRound = _collectRound - 1;
400
uint256[] storage poolIds = _committedValidators[collectRound];
401
uint256 poolIdsLength = poolIds.length;
402
403
for (uint256 i = 0; i < poolIdsLength; i++) {
404
delete _ciphers[collectRound][poolIds[i]];
405
}
406
407
poolIds.length = 0;
408
}
409
410
/// @dev Used by the `revealNumber` function.
411
/// @param _number The validator's number.
412
function _revealNumber(uint256 _number) internal {
413
address miningAddress = msg.sender;
414
415
require(revealNumberCallable(miningAddress, _number));
416
require(_getCoinbase() == miningAddress); // make sure validator node is live
417
418
uint256 poolId = validatorSetContract.idByMiningAddress(miningAddress);
419
420
currentSeed = currentSeed ^ _number;
421
_sentReveal[currentCollectRound()][poolId] = true;
422
}
423
424
/// @dev Returns the current `coinbase` address. Needed mostly for unit tests.
425
function _getCoinbase() internal view returns(address) {
426
return block.coinbase;
427
}
428
429
/// @dev Returns the current block number. Needed mostly for unit tests.
430
function _getCurrentBlockNumber() internal view returns(uint256) {
431
return block.number;
432
}
433
434
/// @dev Used by the `revealNumberCallable` public getter.
435
/// @param _miningAddress The mining address of validator which tries to call the `revealNumber` function.
436
/// @param _number The validator's number passed to the `revealNumber` function.
437
function _revealNumberCallable(address _miningAddress, uint256 _number) internal view returns(bool) {
438
if (!isRevealPhase()) return false; // must only be called in `reveals phase`
439
440
bytes32 numberHash = keccak256(abi.encodePacked(_number));
441
442
if (numberHash == bytes32(0)) return false;
443
444
if (!validatorSetContract.isValidator(_miningAddress)) return false;
445
446
uint256 collectRound = currentCollectRound();
447
448
if (sentReveal(collectRound, _miningAddress)) {
449
return false; // cannot reveal more than once during the same collectRound
450
}
451
452
if (numberHash != getCommit(collectRound, _miningAddress)) {
453
return false; // the hash must be commited
454
}
455
456
return true;
457
}
458
}
459
Copied!
Last modified 5mo ago