-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathBoltValidatorsV2.sol
295 lines (249 loc) · 12.8 KB
/
BoltValidatorsV2.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {BLS12381} from "../lib/bls/BLS12381.sol";
import {BLSSignatureVerifier} from "../lib/bls/BLSSignatureVerifier.sol";
import {ValidatorsLib} from "../lib/ValidatorsLib.sol";
import {IBoltValidatorsV2} from "../interfaces/IBoltValidatorsV2.sol";
import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol";
/// @title Bolt Validators
/// @notice This contract is responsible for registering validators and managing their configuration
/// @dev This contract is upgradeable using the UUPSProxy pattern. Storage layout remains fixed across upgrades
/// with the use of storage gaps.
/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit.
/// You can also validate manually with forge: forge inspect <contract> storage-layout --pretty
contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpgradeable, UUPSUpgradeable {
using BLS12381 for BLS12381.G1Point;
using ValidatorsLib for ValidatorsLib.ValidatorSet;
// ========= STORAGE =========
/// @notice Bolt Parameters contract.
IBoltParametersV1 public parameters;
/// @notice Validators (aka Blockspace providers)
/// @dev This struct occupies 6 storage slots.
ValidatorsLib.ValidatorSet internal VALIDATORS;
// --> Storage layout marker: 7 slots
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
* This can be validated with the Openzeppelin Foundry Upgrades toolkit.
*
* Total storage slots: 50
*/
uint256[43] private __gap;
// ========= EVENTS =========
/// @notice Emitted when a validator is registered
/// @param pubkeyHash BLS public key hash of the validator
event ValidatorRegistered(bytes32 indexed pubkeyHash);
// ========= INITIALIZER =========
/// @notice Initializer
/// @param _owner Address of the owner of the contract
/// @param _parameters Address of the Bolt Parameters contract
function initialize(address _owner, address _parameters) public initializer {
__Ownable_init(_owner);
parameters = IBoltParametersV1(_parameters);
}
function initializeV2(address _owner, address _parameters) public reinitializer(2) {
__Ownable_init(_owner);
parameters = IBoltParametersV1(_parameters);
}
function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}
// ========= VIEW FUNCTIONS =========
/// @notice Get all validators in the system
/// @dev This function should be used with caution as it can return a large amount of data.
/// @return ValidatorInfo[] Array of validator info structs
function getAllValidators() public view returns (ValidatorInfo[] memory) {
ValidatorsLib._Validator[] memory _vals = VALIDATORS.getAll();
ValidatorInfo[] memory vals = new ValidatorInfo[](_vals.length);
for (uint256 i = 0; i < _vals.length; i++) {
vals[i] = _getValidatorInfo(_vals[i]);
}
return vals;
}
/// @notice Get a validator by its BLS public key
/// @param pubkey BLS public key of the validator
/// @return ValidatorInfo struct
function getValidatorByPubkey(
BLS12381.G1Point calldata pubkey
) public view returns (ValidatorInfo memory) {
return getValidatorByPubkeyHash(hashPubkey(pubkey));
}
/// @notice Get a validator by its BLS public key hash
/// @param pubkeyHash BLS public key hash of the validator
/// @return ValidatorInfo struct
function getValidatorByPubkeyHash(
bytes20 pubkeyHash
) public view returns (ValidatorInfo memory) {
ValidatorsLib._Validator memory _val = VALIDATORS.get(pubkeyHash);
return _getValidatorInfo(_val);
}
// ========= REGISTRATION LOGIC =========
/// @notice Register a single Validator and authorize a Collateral Provider and Operator for it
/// @dev This function allows anyone to register a single Validator. We do not perform any checks.
/// @param pubkeyHash BLS public key hash for the Validator to be registered
/// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations
/// @param authorizedOperator The address of the authorized operator
function registerValidatorUnsafe(
bytes20 pubkeyHash,
uint32 maxCommittedGasLimit,
address authorizedOperator
) public {
if (!parameters.ALLOW_UNSAFE_REGISTRATION()) {
revert UnsafeRegistrationNotAllowed();
}
_registerValidator(pubkeyHash, authorizedOperator, maxCommittedGasLimit);
}
/// @notice Register a single Validator and authorize an Operator for it.
/// @dev This function allows anyone to register a single Validator. We perform an important check:
/// The owner of the Validator (controller) must have signed the message with its BLS private key.
///
/// Message format: `chainId || controller || sequenceNumber`
/// @param pubkey BLS public key for the Validator to be registered
/// @param signature BLS signature of the registration message for the Validator
/// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations
/// @param authorizedOperator The address of the authorized operator
function registerValidator(
BLS12381.G1Point calldata pubkey,
BLS12381.G2Point calldata signature,
uint32 maxCommittedGasLimit,
address authorizedOperator
) public {
uint32 sequenceNumber = uint32(VALIDATORS.length() + 1);
bytes memory message = abi.encodePacked(block.chainid, msg.sender, sequenceNumber);
if (!_verifySignature(message, signature, pubkey)) {
revert InvalidBLSSignature();
}
_registerValidator(hashPubkey(pubkey), authorizedOperator, maxCommittedGasLimit);
}
/// @notice Register a batch of Validators and authorize a Collateral Provider and Operator for them
/// @dev This function allows anyone to register a list of Validators.
/// @param pubkeys List of BLS public keys for the Validators to be registered
/// @param signature BLS aggregated signature of the registration message for this batch of Validators
/// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations
/// @param authorizedOperator The address of the authorized operator
function batchRegisterValidators(
BLS12381.G1Point[] calldata pubkeys,
BLS12381.G2Point calldata signature,
uint32 maxCommittedGasLimit,
address authorizedOperator
) public {
uint32[] memory expectedValidatorSequenceNumbers = new uint32[](pubkeys.length);
uint32 nextValidatorSequenceNumber = uint32(VALIDATORS.length() + 1);
for (uint32 i = 0; i < pubkeys.length; i++) {
expectedValidatorSequenceNumbers[i] = nextValidatorSequenceNumber + i;
}
// Reconstruct the unique message for which we expect an aggregated signature.
// We need the msg.sender to prevent a front-running attack by an EOA that may
// try to register the same validators
bytes memory message = abi.encodePacked(block.chainid, msg.sender, expectedValidatorSequenceNumbers);
// Aggregate the pubkeys into a single pubkey to verify the aggregated signature once
BLS12381.G1Point memory aggPubkey = _aggregatePubkeys(pubkeys);
if (!_verifySignature(message, signature, aggPubkey)) {
revert InvalidBLSSignature();
}
bytes20[] memory pubkeyHashes = new bytes20[](pubkeys.length);
for (uint256 i = 0; i < pubkeys.length; i++) {
pubkeyHashes[i] = hashPubkey(pubkeys[i]);
}
_batchRegisterValidators(pubkeyHashes, authorizedOperator, maxCommittedGasLimit);
}
/// @notice Register a batch of Validators and authorize a Collateral Provider and Operator for them
/// @dev This function allows anyone to register a list of Validators.
/// @param pubkeyHashes List of BLS public key hashes for the Validators to be registered
/// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations
/// @param authorizedOperator The address of the authorized operator
function batchRegisterValidatorsUnsafe(
bytes20[] calldata pubkeyHashes,
uint32 maxCommittedGasLimit,
address authorizedOperator
) public {
if (!parameters.ALLOW_UNSAFE_REGISTRATION()) {
revert UnsafeRegistrationNotAllowed();
}
_batchRegisterValidators(pubkeyHashes, authorizedOperator, maxCommittedGasLimit);
}
// ========= UPDATE FUNCTIONS =========
/// @notice Update the maximum gas limit that a validator can commit for preconfirmations
/// @dev Only the `controller` of the validator can update this value.
/// @param pubkeyHash The hash of the BLS public key of the validator
/// @param maxCommittedGasLimit The new maximum gas limit
function updateMaxCommittedGasLimit(bytes20 pubkeyHash, uint32 maxCommittedGasLimit) public {
address controller = VALIDATORS.getController(pubkeyHash);
if (msg.sender != controller) {
revert UnauthorizedCaller();
}
VALIDATORS.updateMaxCommittedGasLimit(pubkeyHash, maxCommittedGasLimit);
}
// ========= HELPERS =========
/// @notice Internal helper to register a single validator
/// @param pubkeyHash BLS public key hash of the validator
/// @param authorizedOperator Address of the authorized operator
/// @param maxCommittedGasLimit Maximum gas limit that the validator can commit for preconfirmations
function _registerValidator(bytes20 pubkeyHash, address authorizedOperator, uint32 maxCommittedGasLimit) internal {
if (authorizedOperator == address(0)) {
revert InvalidAuthorizedOperator();
}
if (pubkeyHash == bytes20(0)) {
revert InvalidPubkey();
}
VALIDATORS.insert(
pubkeyHash,
maxCommittedGasLimit,
VALIDATORS.getOrInsertController(msg.sender),
VALIDATORS.getOrInsertAuthorizedOperator(authorizedOperator)
);
emit ValidatorRegistered(pubkeyHash);
}
/// @notice Internal helper to register a batch of validators
/// @param pubkeyHashes List of BLS public key hashes of the validators
/// @param authorizedOperator Address of the authorized operator
/// @param maxCommittedGasLimit Maximum gas limit that the validators can commit for preconfirmations
function _batchRegisterValidators(
bytes20[] memory pubkeyHashes,
address authorizedOperator,
uint32 maxCommittedGasLimit
) internal {
if (authorizedOperator == address(0)) {
revert InvalidAuthorizedOperator();
}
uint32 authorizedOperatorIndex = VALIDATORS.getOrInsertAuthorizedOperator(authorizedOperator);
uint32 controllerIndex = VALIDATORS.getOrInsertController(msg.sender);
uint256 pubkeysLength = pubkeyHashes.length;
for (uint32 i; i < pubkeysLength; i++) {
bytes20 pubkeyHash = pubkeyHashes[i];
if (pubkeyHash == bytes20(0)) {
revert InvalidPubkey();
}
VALIDATORS.insert(pubkeyHash, maxCommittedGasLimit, controllerIndex, authorizedOperatorIndex);
emit ValidatorRegistered(pubkeyHash);
}
}
/// @notice Internal helper to get the ValidatorInfo struct from a _Validator struct
/// @param _val Validator struct
/// @return ValidatorInfo struct
function _getValidatorInfo(
ValidatorsLib._Validator memory _val
) internal view returns (ValidatorInfo memory) {
return ValidatorInfo({
pubkeyHash: _val.pubkeyHash,
maxCommittedGasLimit: _val.maxCommittedGasLimit,
authorizedOperator: VALIDATORS.getAuthorizedOperator(_val.pubkeyHash),
controller: VALIDATORS.getController(_val.pubkeyHash)
});
}
/// @notice Helper to compute the hash of a BLS public key
/// @param pubkey Decompressed BLS public key
/// @return Hash of the public key in compressed form
function hashPubkey(
BLS12381.G1Point memory pubkey
) public pure returns (bytes20) {
uint256[2] memory compressedPubKey = pubkey.compress();
bytes32 fullHash = keccak256(abi.encodePacked(compressedPubKey));
// take the leftmost 20 bytes of the keccak256 hash
return bytes20(uint160(uint256(fullHash)));
}
}