-
Notifications
You must be signed in to change notification settings - Fork 94
/
Copy pathStakeRegistry.sol
540 lines (499 loc) · 25.5 KB
/
StakeRegistry.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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
import "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
import "src/interfaces/IServiceManager.sol";
import "src/interfaces/IStakeRegistry.sol";
import "src/interfaces/IRegistryCoordinator.sol";
import "src/StakeRegistryStorage.sol";
import {VoteWeigherBase} from "src/VoteWeigherBase.sol";
/**
* @title A `Registry` that keeps track of stakes of operators for up to 256 quorums.
* Specifically, it keeps track of
* 1) The stake of each operator in all the quorums they are a part of for block ranges
* 2) The total stake of all operators in each quorum for block ranges
* 3) The minimum stake required to register for each quorum
* It allows an additional functionality (in addition to registering and deregistering) to update the stake of an operator.
* @author Layr Labs, Inc.
*/
contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage {
/// @notice requires that the caller is the RegistryCoordinator
modifier onlyRegistryCoordinator() {
require(
msg.sender == address(registryCoordinator),
"StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"
);
_;
}
constructor(
IRegistryCoordinator _registryCoordinator,
IStrategyManager _strategyManager,
IServiceManager _serviceManager
) VoteWeigherBase(_strategyManager, _serviceManager) StakeRegistryStorage(_registryCoordinator) {}
/**
* @notice Sets the minimum stake for each quorum and adds `_quorumStrategiesConsideredAndMultipliers` for each
* quorum the Registry is being initialized with
*/
function initialize(
uint96[] memory _minimumStakeForQuorum,
StrategyAndWeightingMultiplier[][] memory _quorumStrategiesConsideredAndMultipliers
) external virtual initializer {
_initialize(_minimumStakeForQuorum, _quorumStrategiesConsideredAndMultipliers);
}
function _initialize(
uint96[] memory _minimumStakeForQuorum,
StrategyAndWeightingMultiplier[][] memory _quorumStrategiesConsideredAndMultipliers
) internal virtual onlyInitializing {
// sanity check lengths
require(
_minimumStakeForQuorum.length == _quorumStrategiesConsideredAndMultipliers.length,
"Registry._initialize: minimumStakeForQuorum length mismatch"
);
// add the strategies considered and multipliers for each quorum
for (uint8 quorumNumber = 0; quorumNumber < _quorumStrategiesConsideredAndMultipliers.length; ) {
_setMinimumStakeForQuorum(quorumNumber, _minimumStakeForQuorum[quorumNumber]);
_createQuorum(_quorumStrategiesConsideredAndMultipliers[quorumNumber]);
unchecked {
++quorumNumber;
}
}
}
/*******************************************************************************
EXTERNAL FUNCTIONS
*******************************************************************************/
/**
* @notice Used for updating information on deposits of nodes.
* @param operators are the addresses of the operators whose stake information is getting updated
* @dev reverts if there are no operators registered with index out of bounds
*/
function updateStakes(address[] calldata operators) external {
// for each quorum, loop through operators and see if they are a part of the quorum
// if they are, get their new weight and update their individual stake history and the
// quorum's total stake history accordingly
for (uint8 quorumNumber = 0; quorumNumber < quorumCount; ) {
OperatorStakeUpdate memory totalStakeUpdate;
// for each operator
for (uint i = 0; i < operators.length; ) {
bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]);
uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmapByOperatorId(operatorId);
// if the operator is a part of the quorum
if (BitmapUtils.numberIsInBitmap(quorumBitmap, quorumNumber)) {
// if the total stake has not been loaded yet, load it
if (totalStakeUpdate.updateBlockNumber == 0) {
totalStakeUpdate = _totalStakeHistory[quorumNumber][
_totalStakeHistory[quorumNumber].length - 1
];
}
// update the operator's stake based on current state
(uint96 stakeBeforeUpdate, uint96 stakeAfterUpdate) = _updateOperatorStake({
operator: operators[i],
operatorId: operatorId,
quorumNumber: quorumNumber
});
// calculate the new total stake for the quorum
totalStakeUpdate.stake = totalStakeUpdate.stake - stakeBeforeUpdate + stakeAfterUpdate;
}
unchecked {
++i;
}
}
// if the total stake for this quorum was updated, record it in storage
if (totalStakeUpdate.updateBlockNumber != 0) {
// update the total stake history for the quorum
_recordTotalStakeUpdate(quorumNumber, totalStakeUpdate);
}
unchecked {
++quorumNumber;
}
}
// TODO after slashing enabled: record stake updates in the EigenLayer Slasher
// for (uint i = 0; i < operators.length;) {
// serviceManager.recordStakeUpdate(operators[i], uint32(block.number), serviceManager.latestServeUntilBlock(), prevElements[i]);
// unchecked {
// ++i;
// }
// }
}
/*******************************************************************************
EXTERNAL FUNCTIONS - REGISTRY COORDINATOR
*******************************************************************************/
/**
* @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`.
* @param operator The address of the operator to register.
* @param operatorId The id of the operator to register.
* @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber.
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already registered
*/
function registerOperator(
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers
) public virtual onlyRegistryCoordinator {
// check the operator is registering for only valid quorums
require(
uint8(quorumNumbers[quorumNumbers.length - 1]) < quorumCount,
"StakeRegistry._registerOperator: greatest quorumNumber must be less than quorumCount"
);
OperatorStakeUpdate memory _newTotalStakeUpdate;
// add the `updateBlockNumber` info
_newTotalStakeUpdate.updateBlockNumber = uint32(block.number);
// for each quorum, evaluate stake and add to total stake
for (uint8 quorumNumbersIndex = 0; quorumNumbersIndex < quorumNumbers.length; ) {
// get the next quorumNumber
uint8 quorumNumber = uint8(quorumNumbers[quorumNumbersIndex]);
// evaluate the stake for the operator
// since we don't use the first output, this will use 1 extra sload when deregistered operator's register again
(, uint96 stake) = _updateOperatorStake({
operator: operator,
operatorId: operatorId,
quorumNumber: quorumNumber
});
// check if minimum requirement has been met, will be 0 if not
require(
stake != 0,
"StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum"
);
// add operator stakes to total stake before update (in memory)
uint256 _totalStakeHistoryLength = _totalStakeHistory[quorumNumber].length;
// add calculate the total stake for the quorum
uint96 totalStakeAfterUpdate = stake;
if (_totalStakeHistoryLength != 0) {
// only add the stake if there is a previous total stake
// overwrite `stake` variable
totalStakeAfterUpdate += _totalStakeHistory[quorumNumber][_totalStakeHistoryLength - 1].stake;
}
_newTotalStakeUpdate.stake = totalStakeAfterUpdate;
// update storage of total stake
_recordTotalStakeUpdate(quorumNumber, _newTotalStakeUpdate);
unchecked {
++quorumNumbersIndex;
}
}
}
/**
* @notice Deregisters the operator with `operatorId` for the specified `quorumNumbers`.
* @param operatorId The id of the operator to deregister.
* @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber.
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already deregistered
* 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for
*/
function deregisterOperator(
bytes32 operatorId,
bytes calldata quorumNumbers
) public virtual onlyRegistryCoordinator {
OperatorStakeUpdate memory _operatorStakeUpdate;
// add the `updateBlockNumber` info
_operatorStakeUpdate.updateBlockNumber = uint32(block.number);
OperatorStakeUpdate memory _newTotalStakeUpdate;
// add the `updateBlockNumber` info
_newTotalStakeUpdate.updateBlockNumber = uint32(block.number);
// loop through the operator's quorums and remove the operator's stake for each quorum
for (uint8 quorumNumbersIndex = 0; quorumNumbersIndex < quorumNumbers.length; ) {
uint8 quorumNumber = uint8(quorumNumbers[quorumNumbersIndex]);
// update the operator's stake
uint96 stakeBeforeUpdate = _recordOperatorStakeUpdate({
operatorId: operatorId,
quorumNumber: quorumNumber,
operatorStakeUpdate: _operatorStakeUpdate
});
// subtract the amounts staked by the operator that is getting deregistered from the total stake before deregistration
// copy latest totalStakes to memory
_newTotalStakeUpdate.stake =
_totalStakeHistory[quorumNumber][_totalStakeHistory[quorumNumber].length - 1].stake -
stakeBeforeUpdate;
// update storage of total stake
_recordTotalStakeUpdate(quorumNumber, _newTotalStakeUpdate);
emit StakeUpdate(
operatorId,
quorumNumber,
// new stakes are zero
0
);
unchecked {
++quorumNumbersIndex;
}
}
}
/*******************************************************************************
EXTERNAL FUNCTIONS - SERVICE MANAGER OWNER
*******************************************************************************/
/// @notice Adjusts the `minimumStakeFirstQuorum` -- i.e. the node stake (weight) requirement for inclusion in the 1st quorum.
function setMinimumStakeForQuorum(uint8 quorumNumber, uint96 minimumStake) external onlyServiceManagerOwner {
_setMinimumStakeForQuorum(quorumNumber, minimumStake);
}
/*******************************************************************************
INTERNAL FUNCTIONS
*******************************************************************************/
function _getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) internal view returns (uint32) {
uint32 length = uint32(operatorIdToStakeHistory[operatorId][quorumNumber].length);
for (uint32 i = 0; i < length; i++) {
if (operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].updateBlockNumber <= blockNumber) {
require(
operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber == 0 ||
operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber >
blockNumber,
"StakeRegistry._getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber: operatorId has no stake update at blockNumber"
);
return length - i - 1;
}
}
revert(
"StakeRegistry._getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber: no stake update found for operatorId and quorumNumber at block number"
);
}
function _setMinimumStakeForQuorum(uint8 quorumNumber, uint96 minimumStake) internal {
minimumStakeForQuorum[quorumNumber] = minimumStake;
emit MinimumStakeForQuorumUpdated(quorumNumber, minimumStake);
}
/**
* @notice Finds the updated stake for `operator` for `quorumNumber`, stores it, records the update,
* and returns both the previous stake then updated stake.
* @dev **DOES NOT UPDATE `totalStake` IN ANY WAY** -- `totalStake` updates must be done elsewhere.
*/
function _updateOperatorStake(
address operator,
bytes32 operatorId,
uint8 quorumNumber
) internal returns (uint96, uint96) {
// determine new stakes
OperatorStakeUpdate memory operatorStakeUpdate;
operatorStakeUpdate.updateBlockNumber = uint32(block.number);
operatorStakeUpdate.stake = weightOfOperatorForQuorum(quorumNumber, operator);
// check if minimum requirements have been met
if (operatorStakeUpdate.stake < minimumStakeForQuorum[quorumNumber]) {
// set staker to 0
operatorStakeUpdate.stake = uint96(0);
}
// get stakeBeforeUpdate and update with new stake
uint96 stakeBeforeUpdate = _recordOperatorStakeUpdate({
operatorId: operatorId,
quorumNumber: quorumNumber,
operatorStakeUpdate: operatorStakeUpdate
});
emit StakeUpdate(operatorId, quorumNumber, operatorStakeUpdate.stake);
return (stakeBeforeUpdate, operatorStakeUpdate.stake);
}
/**
* @notice Records that `operatorId`'s current stake for `quorumNumber` is now param @operatorStakeUpdate
* and returns the previous stake.
*/
function _recordOperatorStakeUpdate(
bytes32 operatorId,
uint8 quorumNumber,
OperatorStakeUpdate memory operatorStakeUpdate
) internal returns (uint96) {
// initialize stakeBeforeUpdate to 0
uint96 stakeBeforeUpdate;
uint256 operatorStakeHistoryLength = operatorIdToStakeHistory[operatorId][quorumNumber].length;
if (operatorStakeHistoryLength != 0) {
// set nextUpdateBlockNumber in prev stakes
operatorIdToStakeHistory[operatorId][quorumNumber][operatorStakeHistoryLength - 1]
.nextUpdateBlockNumber = uint32(block.number);
// load stake before update into memory if it exists
stakeBeforeUpdate = operatorIdToStakeHistory[operatorId][quorumNumber][operatorStakeHistoryLength - 1]
.stake;
}
// push new stake to storage
operatorIdToStakeHistory[operatorId][quorumNumber].push(operatorStakeUpdate);
return stakeBeforeUpdate;
}
/// @notice Records that the `totalStake` for `quorumNumber` is now equal to the input param @_totalStake
function _recordTotalStakeUpdate(uint8 quorumNumber, OperatorStakeUpdate memory _totalStake) internal {
uint256 _totalStakeHistoryLength = _totalStakeHistory[quorumNumber].length;
if (_totalStakeHistoryLength != 0) {
_totalStakeHistory[quorumNumber][_totalStakeHistoryLength - 1].nextUpdateBlockNumber = uint32(block.number);
}
_totalStake.updateBlockNumber = uint32(block.number);
_totalStakeHistory[quorumNumber].push(_totalStake);
}
/// @notice Validates that the `operatorStake` was accurate at the given `blockNumber`
function _validateOperatorStakeUpdateAtBlockNumber(
OperatorStakeUpdate memory operatorStakeUpdate,
uint32 blockNumber
) internal pure {
require(
operatorStakeUpdate.updateBlockNumber <= blockNumber,
"StakeRegistry._validateOperatorStakeAtBlockNumber: operatorStakeUpdate is from after blockNumber"
);
require(
operatorStakeUpdate.nextUpdateBlockNumber == 0 || operatorStakeUpdate.nextUpdateBlockNumber > blockNumber,
"StakeRegistry._validateOperatorStakeAtBlockNumber: there is a newer operatorStakeUpdate available before blockNumber"
);
}
/*******************************************************************************
VIEW FUNCTIONS
*******************************************************************************/
/**
* @notice Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array.
* @param operatorId The id of the operator of interest.
* @param quorumNumber The quorum number to get the stake for.
*/
function getOperatorIdToStakeHistory(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (OperatorStakeUpdate[] memory) {
return operatorIdToStakeHistory[operatorId][quorumNumber];
}
/**
* @notice Returns the `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array.
* @param quorumNumber The quorum number to get the stake for.
* @param operatorId The id of the operator of interest.
* @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`.
* @dev Function will revert if `index` is out-of-bounds.
*/
function getStakeUpdateForQuorumFromOperatorIdAndIndex(
uint8 quorumNumber,
bytes32 operatorId,
uint256 index
) external view returns (OperatorStakeUpdate memory) {
return operatorIdToStakeHistory[operatorId][quorumNumber][index];
}
/**
* @notice Returns the `index`-th entry in the dynamic array of total stake, `_totalStakeHistory` for quorum `quorumNumber`.
* @param quorumNumber The quorum number to get the stake for.
* @param index Array index for lookup, within the dynamic array `_totalStakeHistory[quorumNumber]`.
*/
function getTotalStakeUpdateForQuorumFromIndex(
uint8 quorumNumber,
uint256 index
) external view returns (OperatorStakeUpdate memory) {
return _totalStakeHistory[quorumNumber][index];
}
/// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber`
function getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) external view returns (uint32) {
return _getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(operatorId, quorumNumber, blockNumber);
}
/**
* @notice Returns the indices of the total stakes for the provided `quorumNumbers` at the given `blockNumber`
* @param blockNumber Block number to retrieve the stake indices from.
* @param quorumNumbers The quorum numbers to get the stake indices for.
* @dev Function will revert if there are no indices for the given `blockNumber`
*/
function getTotalStakeIndicesByQuorumNumbersAtBlockNumber(
uint32 blockNumber,
bytes calldata quorumNumbers
) external view returns (uint32[] memory) {
uint32[] memory indices = new uint32[](quorumNumbers.length);
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
require(
_totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber,
"StakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber: quorum has no stake history at blockNumber"
);
uint32 length = uint32(_totalStakeHistory[quorumNumber].length);
for (uint32 j = 0; j < length; j++) {
if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) {
indices[i] = length - j - 1;
break;
}
}
}
return indices;
}
/**
* @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the
* `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if it was the operator's
* stake at `blockNumber`. Reverts otherwise.
* @param quorumNumber The quorum number to get the stake for.
* @param operatorId The id of the operator of interest.
* @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`.
* @param blockNumber Block number to make sure the stake is from.
* @dev Function will revert if `index` is out-of-bounds.
*/
function getStakeForQuorumAtBlockNumberFromOperatorIdAndIndex(
uint8 quorumNumber,
uint32 blockNumber,
bytes32 operatorId,
uint256 index
) external view returns (uint96) {
OperatorStakeUpdate memory operatorStakeUpdate = operatorIdToStakeHistory[operatorId][quorumNumber][index];
_validateOperatorStakeUpdateAtBlockNumber(operatorStakeUpdate, blockNumber);
return operatorStakeUpdate.stake;
}
/**
* @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the
* `_totalStakeHistory[quorumNumber]` array if it was the stake at `blockNumber`. Reverts otherwise.
* @param quorumNumber The quorum number to get the stake for.
* @param index Array index for lookup, within the dynamic array `_totalStakeHistory[quorumNumber]`.
* @param blockNumber Block number to make sure the stake is from.
* @dev Function will revert if `index` is out-of-bounds.
*/
function getTotalStakeAtBlockNumberFromIndex(
uint8 quorumNumber,
uint32 blockNumber,
uint256 index
) external view returns (uint96) {
OperatorStakeUpdate memory totalStakeUpdate = _totalStakeHistory[quorumNumber][index];
_validateOperatorStakeUpdateAtBlockNumber(totalStakeUpdate, blockNumber);
return totalStakeUpdate.stake;
}
/**
* @notice Returns the most recent stake weight for the `operatorId` for a certain quorum
* @dev Function returns an OperatorStakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history
*/
function getMostRecentStakeUpdateByOperatorId(
bytes32 operatorId,
uint8 quorumNumber
) public view returns (OperatorStakeUpdate memory) {
uint256 historyLength = operatorIdToStakeHistory[operatorId][quorumNumber].length;
OperatorStakeUpdate memory operatorStakeUpdate;
if (historyLength == 0) {
return operatorStakeUpdate;
} else {
operatorStakeUpdate = operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1];
return operatorStakeUpdate;
}
}
/**
* @notice Returns the most recent stake weight for the `operatorId` for quorum `quorumNumber`
* @dev Function returns weight of **0** in the event that the operator has no stake history
*/
function getCurrentOperatorStakeForQuorum(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96) {
OperatorStakeUpdate memory operatorStakeUpdate = getMostRecentStakeUpdateByOperatorId(operatorId, quorumNumber);
return operatorStakeUpdate.stake;
}
/// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber`
function getStakeForOperatorIdForQuorumAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) external view returns (uint96) {
return
operatorIdToStakeHistory[operatorId][quorumNumber][
_getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(operatorId, quorumNumber, blockNumber)
].stake;
}
/**
* @notice Returns the stake weight from the latest entry in `_totalStakeHistory` for quorum `quorumNumber`.
* @dev Will revert if `_totalStakeHistory[quorumNumber]` is empty.
*/
function getCurrentTotalStakeForQuorum(uint8 quorumNumber) external view returns (uint96) {
return _totalStakeHistory[quorumNumber][_totalStakeHistory[quorumNumber].length - 1].stake;
}
function getLengthOfOperatorIdStakeHistoryForQuorum(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (uint256) {
return operatorIdToStakeHistory[operatorId][quorumNumber].length;
}
function getLengthOfTotalStakeHistoryForQuorum(uint8 quorumNumber) external view returns (uint256) {
return _totalStakeHistory[quorumNumber].length;
}
}