-
Notifications
You must be signed in to change notification settings - Fork 76
/
Copy pathFuelChainState.sol
181 lines (146 loc) · 6.46 KB
/
FuelChainState.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-License-Identifier: UNLICENSED
// solhint-disable not-rely-on-time
pragma solidity 0.8.24;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import {CryptographyLib} from "../lib/Cryptography.sol";
/// @notice Structure for commits
struct Commit {
bytes32 blockHash;
uint32 timestamp;
address reserved1;
uint16 reserved2;
}
/// @notice The Fuel v2 chain state
contract FuelChainState is Initializable, PausableUpgradeable, AccessControlUpgradeable, UUPSUpgradeable {
///////////////
// Constants //
///////////////
/// @dev The commit proccess parameters
// NUM_COMMIT_SLOTS an arbitrary number of commits to store before starting to overwrite
uint256 public constant NUM_COMMIT_SLOTS = 240;
// Number of blocks per commit interval
// BLOCKS_PER_COMMIT_INTERVAL = (num of blocks per minute (=60) * target interval in minutes)
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
uint256 public immutable BLOCKS_PER_COMMIT_INTERVAL;
// Time after which a commit becomes finalized
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
uint256 public immutable TIME_TO_FINALIZE;
/// Time before a slot can be overwritten
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
uint32 public immutable COMMIT_COOLDOWN;
/// @dev The admin related contract roles
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant COMMITTER_ROLE = keccak256("COMMITTER_ROLE");
////////////
// Events //
////////////
/// @dev Emitted when a commit is first submitted
event CommitSubmitted(uint256 indexed commitHeight, bytes32 blockHash);
////////////
// Errors //
////////////
error CannotRecommit();
error CommitCooldownTooLarge();
error FinalizationIsGtCooldown();
error InvalidTimeToFinalize();
error TimeToFinalizeTooLarge();
error UnknownBlock();
/////////////
// Storage //
/////////////
/// @dev The commits buffer
Commit[NUM_COMMIT_SLOTS] private _commitSlots;
/////////////////////////////
// Constructor/Initializer //
/////////////////////////////
/// @notice Constructor disables initialization for the implementation contract
/// @dev assumes 1 block per second in the L2 chain
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(uint256 timeToFinalize, uint256 blocksPerCommitInterval, uint32 commitCooldown) {
if (timeToFinalize == 0) {
revert InvalidTimeToFinalize();
}
if (timeToFinalize > commitCooldown) {
revert FinalizationIsGtCooldown();
}
uint256 circularBufferSizeInSeconds = NUM_COMMIT_SLOTS * blocksPerCommitInterval;
if (timeToFinalize > circularBufferSizeInSeconds) {
revert TimeToFinalizeTooLarge();
}
if (commitCooldown > circularBufferSizeInSeconds) {
revert CommitCooldownTooLarge();
}
TIME_TO_FINALIZE = timeToFinalize;
COMMIT_COOLDOWN = commitCooldown;
BLOCKS_PER_COMMIT_INTERVAL = blocksPerCommitInterval;
_disableInitializers();
}
/// @notice Contract initializer to setup starting values
function initialize() public initializer {
__Pausable_init();
__AccessControl_init();
__UUPSUpgradeable_init();
//grant initial roles
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(PAUSER_ROLE, msg.sender);
_grantRole(COMMITTER_ROLE, msg.sender);
}
/////////////////////
// Admin Functions //
/////////////////////
/// @notice Pause block commitments
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}
/// @notice Unpause block commitments
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_unpause();
}
/// @notice Commits a block header.
/// @param blockHash The hash of a block
/// @param commitHeight The height of the commit
function commit(bytes32 blockHash, uint256 commitHeight) external whenNotPaused onlyRole(COMMITTER_ROLE) {
uint256 slot = commitHeight % NUM_COMMIT_SLOTS;
Commit storage commitSlot = _commitSlots[slot];
unchecked {
if (commitSlot.timestamp + COMMIT_COOLDOWN > uint32(block.timestamp)) {
revert CannotRecommit();
}
}
commitSlot.blockHash = blockHash;
commitSlot.timestamp = uint32(block.timestamp);
emit CommitSubmitted(commitHeight, blockHash);
}
//////////////////////
// Public Functions //
//////////////////////
/// @notice Checks if a given block is finalized
/// @param blockHash The hash of the block to check
/// @param blockHeight The height of the block to check
/// @return true if the block is finalized
function finalized(bytes32 blockHash, uint256 blockHeight) external view whenNotPaused returns (bool) {
// TODO This division could be done offchain, or at least also could be assembly'ed to avoid non-zero division check
uint256 commitHeight = blockHeight / BLOCKS_PER_COMMIT_INTERVAL;
Commit storage commitSlot = _commitSlots[commitHeight % NUM_COMMIT_SLOTS];
if (commitSlot.blockHash != blockHash) revert UnknownBlock();
return block.timestamp >= uint256(commitSlot.timestamp) + TIME_TO_FINALIZE;
}
/// @notice Gets the block hash at the given commit height
/// @param commitHeight The height of the commit
/// @return hash of the block at the given commit height
function blockHashAtCommit(uint256 commitHeight) external view returns (bytes32) {
Commit storage commitSlot = _commitSlots[commitHeight % NUM_COMMIT_SLOTS];
return commitSlot.blockHash;
}
////////////////////////
// Internal Functions //
////////////////////////
/// @notice Executes a message in the given header
// solhint-disable-next-line no-empty-blocks
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {
//should revert if msg.sender is not authorized to upgrade the contract (currently only owner)
}
}