-
Notifications
You must be signed in to change notification settings - Fork 79
/
OperatorStateRetriever.sol
181 lines (159 loc) · 10 KB
/
OperatorStateRetriever.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: BUSL-1.1
pragma solidity =0.8.12;
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
/**
* @title OperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system.
* @author Layr Labs Inc.
*/
contract OperatorStateRetriever {
struct Operator {
address operator;
bytes32 operatorId;
uint96 stake;
}
struct CheckSignaturesIndices {
uint32[] nonSignerQuorumBitmapIndices;
uint32[] quorumApkIndices;
uint32[] totalStakeIndices;
uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex]
}
/**
* @notice This function is intended to to be called by AVS operators every time a new task is created (i.e.)
* the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain,
* operators don't need to run indexers to fetch the data.
* @param registryCoordinator is the registry coordinator to fetch the AVS registry information from
* @param operatorId the id of the operator to fetch the quorums lists
* @param blockNumber is the block number to get the operator state for
* @return 1) the quorumBitmap of the operator at the given blockNumber
* 2) 2d array of Operator structs. For each quorum the provided operator
* was a part of at `blockNumber`, an ordered list of operators.
*/
function getOperatorState(
IRegistryCoordinator registryCoordinator,
bytes32 operatorId,
uint32 blockNumber
) external view returns (uint256, Operator[][] memory) {
bytes32[] memory operatorIds = new bytes32[](1);
operatorIds[0] = operatorId;
uint256 index = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds)[0];
uint256 quorumBitmap = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index);
bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap);
return (quorumBitmap, getOperatorState(registryCoordinator, quorumNumbers, blockNumber));
}
/**
* @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator
* may call this function directly to get the operator state for a given block number
* @param registryCoordinator is the registry coordinator to fetch the AVS registry information from
* @param quorumNumbers are the ids of the quorums to get the operator state for
* @param blockNumber is the block number to get the operator state for
* @return 2d array of Operators. For each quorum, an ordered list of Operators
*/
function getOperatorState(
IRegistryCoordinator registryCoordinator,
bytes memory quorumNumbers,
uint32 blockNumber
) public view returns(Operator[][] memory) {
IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry();
IIndexRegistry indexRegistry = registryCoordinator.indexRegistry();
IBLSApkRegistry blsApkRegistry = registryCoordinator.blsApkRegistry();
Operator[][] memory operators = new Operator[][](quorumNumbers.length);
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
bytes32[] memory operatorIds = indexRegistry.getOperatorListAtBlockNumber(quorumNumber, blockNumber);
operators[i] = new Operator[](operatorIds.length);
for (uint256 j = 0; j < operatorIds.length; j++) {
operators[i][j] = Operator({
operator: blsApkRegistry.getOperatorFromPubkeyHash(operatorIds[j]),
operatorId: bytes32(operatorIds[j]),
stake: stakeRegistry.getStakeAtBlockNumber(bytes32(operatorIds[j]), quorumNumber, blockNumber)
});
}
}
return operators;
}
/**
* @notice this is called by the AVS operator to get the relevant indices for the checkSignatures function
* if they are not running an indexer
* @param registryCoordinator is the registry coordinator to fetch the AVS registry information from
* @param referenceBlockNumber is the block number to get the indices for
* @param quorumNumbers are the ids of the quorums to get the operator state for
* @param nonSignerOperatorIds are the ids of the nonsigning operators
* @return 1) the indices of the quorumBitmaps for each of the operators in the @param nonSignerOperatorIds array at the given blocknumber
* 2) the indices of the total stakes entries for the given quorums at the given blocknumber
* 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a
* part of (for each nonsigner, an array of length the number of quorums they were a part of
* that are also part of the provided quorumNumbers) at the given blocknumber
* 4) the indices of the quorum apks for each of the provided quorums at the given blocknumber
*/
function getCheckSignaturesIndices(
IRegistryCoordinator registryCoordinator,
uint32 referenceBlockNumber,
bytes calldata quorumNumbers,
bytes32[] calldata nonSignerOperatorIds
) external view returns (CheckSignaturesIndices memory) {
IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry();
CheckSignaturesIndices memory checkSignaturesIndices;
// get the indices of the quorumBitmap updates for each of the operators in the nonSignerOperatorIds array
checkSignaturesIndices.nonSignerQuorumBitmapIndices = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(referenceBlockNumber, nonSignerOperatorIds);
// get the indices of the totalStake updates for each of the quorums in the quorumNumbers array
checkSignaturesIndices.totalStakeIndices = stakeRegistry.getTotalStakeIndicesAtBlockNumber(referenceBlockNumber, quorumNumbers);
checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](quorumNumbers.length);
for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length; quorumNumberIndex++) {
uint256 numNonSignersForQuorum = 0;
// this array's length will be at most the number of nonSignerOperatorIds, this will be trimmed after it is filled
checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = new uint32[](nonSignerOperatorIds.length);
for (uint i = 0; i < nonSignerOperatorIds.length; i++) {
// get the quorumBitmap for the operator at the given blocknumber and index
uint192 nonSignerQuorumBitmap =
registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(
nonSignerOperatorIds[i],
referenceBlockNumber,
checkSignaturesIndices.nonSignerQuorumBitmapIndices[i]
);
require(nonSignerQuorumBitmap != 0, "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber");
// if the operator was a part of the quorum and the quorum is a part of the provided quorumNumbers
if ((nonSignerQuorumBitmap >> uint8(quorumNumbers[quorumNumberIndex])) & 1 == 1) {
// get the index of the stake update for the operator at the given blocknumber and quorum number
checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][numNonSignersForQuorum] = stakeRegistry.getStakeUpdateIndexAtBlockNumber(
nonSignerOperatorIds[i],
uint8(quorumNumbers[quorumNumberIndex]),
referenceBlockNumber
);
numNonSignersForQuorum++;
}
}
// resize the array to the number of nonSigners for this quorum
uint32[] memory nonSignerStakeIndicesForQuorum = new uint32[](numNonSignersForQuorum);
for (uint i = 0; i < numNonSignersForQuorum; i++) {
nonSignerStakeIndicesForQuorum[i] = checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][i];
}
checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = nonSignerStakeIndicesForQuorum;
}
IBLSApkRegistry blsApkRegistry = registryCoordinator.blsApkRegistry();
// get the indices of the quorum apks for each of the provided quorums at the given blocknumber
checkSignaturesIndices.quorumApkIndices = blsApkRegistry.getApkIndicesAtBlockNumber(quorumNumbers, referenceBlockNumber);
return checkSignaturesIndices;
}
/**
* @notice this function returns the quorumBitmaps for each of the operators in the operatorIds array at the given blocknumber
* @param registryCoordinator is the AVS registry coordinator to fetch the operator information from
* @param operatorIds are the ids of the operators to get the quorumBitmaps for
* @param blockNumber is the block number to get the quorumBitmaps for
*/
function getQuorumBitmapsAtBlockNumber(
IRegistryCoordinator registryCoordinator,
bytes32[] memory operatorIds,
uint32 blockNumber
) external view returns (uint256[] memory) {
uint32[] memory quorumBitmapIndices = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds);
uint256[] memory quorumBitmaps = new uint256[](operatorIds.length);
for (uint256 i = 0; i < operatorIds.length; i++) {
quorumBitmaps[i] = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorIds[i], blockNumber, quorumBitmapIndices[i]);
}
return quorumBitmaps;
}
}