-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEntropyGenerator.sol
129 lines (107 loc) · 5.64 KB
/
EntropyGenerator.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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol";
import { IEntropyGenerator } from "./interfaces/IEntropyGenerator.sol";
import { AddressProviderResolver } from "./core/AddressProviderResolver.sol";
// EntropyGenerator is a contract designed to generate pseudo-random values for use in other contracts
contract EntropyGenerator is IEntropyGenerator, AddressProviderResolver, Pausable {
uint256 private constant MAX_SLOT_INDEX = 833;
uint256 private constant MAX_NUMBER_INDEX = 12;
uint256[MAX_SLOT_INDEX] private entropySlots; // Array to store entropy values
uint256 private lastInitializedIndex = 0; // Indexes to keep track of the initialization and usage of entropy values
uint256 public currentSlotIndex = 0;
uint256 public currentNumberIndex = 0;
// Constants to define the limits for slots and numbers within those slots
uint256 public slotIndexSelectionPoint;
uint256 public numberIndexSelectionPoint;
error EntropyGenerator__MaxSlotIndexReached();
constructor(address addressProvider) AddressProviderResolver(addressProvider) {
_writeEntropyBatch(); // M05
_initializeAlphaIndices();
}
function nextEntropy() public onlyEntropyAccessor returns (uint256) {
// this error should never be reverted as we control currentSlotIndex in the do while loop
if (currentSlotIndex >= MAX_SLOT_INDEX) revert EntropyGenerator__MaxSlotIndexReached();
uint256 entropy;
do {
entropy = getEntropy(currentSlotIndex, currentNumberIndex);
if (currentNumberIndex >= MAX_NUMBER_INDEX - 1) {
currentNumberIndex = 0;
if (currentSlotIndex >= MAX_SLOT_INDEX - 1) {
currentSlotIndex = 0;
} else {
currentSlotIndex++;
}
} else {
currentNumberIndex++;
}
} while (entropy == 0 || entropy < 100_000 || entropy > 999_999); // Ensure it's a 6-digit number and not 0
emit EntropyRetrieved(entropy);
return entropy;
}
// Select index points for 999999, triggered each generation increment
function initializeAlphaIndices() public onlyEntropyAccessor {
_initializeAlphaIndices();
}
// Public function to expose entropy calculation for a given slot and number index
function getPublicEntropy(uint256 slotIndex, uint256 numberIndex) public view returns (uint256) {
return getEntropy(slotIndex, numberIndex);
}
function getEntropySlot(uint256 index) external view returns (uint256) {
require(index < MAX_SLOT_INDEX, "Index out of bounds");
return entropySlots[index];
}
// Function to get the last initialized index for debugging or informational purposes
function getLastInitializedIndex() public view returns (uint256) {
return lastInitializedIndex;
}
function getInbredEntropy() public view returns (uint256) {
uint256 entropy = 1; // Start with 1 to ensure the first digit is 1
for (uint256 i = 0; i < 5; i++) {
uint256 bit = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, i))) % 3;
entropy = entropy * 10 + bit;
}
return entropy;
}
// Private function to calculate the entropy value based on slot and number index
function getEntropy(uint256 slotIndex, uint256 numberIndex) private view returns (uint256) {
require(slotIndex < MAX_SLOT_INDEX, "Slot index out of bounds.");
if (slotIndex == slotIndexSelectionPoint && numberIndex == numberIndexSelectionPoint) {
return 999_999;
}
uint256 position = numberIndex * 6; // Calculate the position for slicing the entropy value
require(position <= 66, "Position calculation error");
uint256 slotValue = entropySlots[slotIndex]; // Slice the required part of the entropy value
uint256 entropy = (slotValue / (10 ** (66 - position))) % 1_000_000; // Adjust the entropy value based on the
// number of digits
uint256 paddedEntropy = entropy * (10 ** (6 - numberOfDigits(entropy)));
return paddedEntropy; // Return the calculated entropy value
}
// Utility function to calculate the number of digits in a number
function numberOfDigits(uint256 number) private pure returns (uint256) {
uint256 digits = 0;
while (number != 0) {
number /= 10;
digits++;
}
return digits;
}
// Functions to initialize entropy values in batches to spread gas cost over multiple transactions
function _writeEntropyBatch() private {
uint256 endIndex = MAX_SLOT_INDEX; // We want to initialize all 770 slots
for (uint256 i = lastInitializedIndex; i < endIndex; i++) {
uint256 pseudoRandomValue =
uint256(keccak256(abi.encodePacked(block.number, block.timestamp, i))) % uint256(10) ** 77; // generate a
// pseudo-random value using block number and index
entropySlots[i] = pseudoRandomValue; // store the value in the slots array
}
lastInitializedIndex = endIndex; // Update the index to indicate initialization is complete
}
function _initializeAlphaIndices() private {
uint256 hashValue = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)));
uint256 slotIndexSelection = (hashValue % 258) + 612;
uint256 numberIndexSelection = hashValue % MAX_NUMBER_INDEX;
slotIndexSelectionPoint = slotIndexSelection;
numberIndexSelectionPoint = numberIndexSelection;
}
}