@@ -144,6 +144,170 @@ async function getSigningKeys(contract, nodeOperatorId, startIndex, keysCount) {
144
144
}
145
145
}
146
146
147
+ async function getDepositQueue ( contract ) {
148
+ try {
149
+ if ( ! contract ) {
150
+ throw new Error ( "Contract is not initialized." ) ;
151
+ }
152
+ const queueInfo = await contract . methods . depositQueue ( ) . call ( ) ;
153
+
154
+ const head = Number ( queueInfo [ 0 ] ) ;
155
+ const tail = Number ( queueInfo [ 1 ] ) ;
156
+
157
+ return { head, tail } ;
158
+ } catch ( error ) {
159
+ log . error ( "Error calling getDepositQueue:" , error ) ;
160
+ return null ;
161
+ }
162
+ }
163
+
164
+ async function getDepositQueueItem ( contract , index ) {
165
+ try {
166
+ if ( ! contract ) {
167
+ throw new Error ( "Contract is not initialized." ) ;
168
+ }
169
+ // Fetch the batch item from the contract
170
+ const batchItem = await contract . methods . depositQueueItem ( index ) . call ( ) ;
171
+
172
+ // Function to extract batch information
173
+ const extractBatchInfo = ( batch ) => ( {
174
+ nodeId : ( batch >> 192n ) & ( ( 1n << 64n ) - 1n ) , // Extract the 64 bits for nodeId
175
+ keysCount : ( batch >> 128n ) & ( ( 1n << 64n ) - 1n ) , // Extract the next 64 bits for keysCount
176
+ nextBatch : batch & ( ( 1n << 128n ) - 1n ) , // Extract the lower 128 bits for nextBatch
177
+ } ) ;
178
+
179
+ // Extract details from the batch item
180
+ const { nodeId, keysCount, nextBatch } = extractBatchInfo ( BigInt ( batchItem ) ) ;
181
+
182
+ // Return extracted information
183
+ return { nodeId, keysCount, nextBatch } ;
184
+ } catch ( error ) {
185
+ log . error ( "Error calling getDepositQueueItem:" , error ) ;
186
+ return null ;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Retrieves signing keys with deposit queue information for a given Node Operator.
192
+ *
193
+ * @async
194
+ * @function getSigningKeysWithQueueInfo
195
+ * @param {Object } monitoring - Monitoring Object
196
+ * @returns {Promise<Array<{key: string, queuePosition: bigint}>> | Promise<null> }
197
+ * Returns an array of signing keys with queue positions or null on failure.
198
+ * @throws {Error } Logs and returns `null` if:
199
+ * - The RPC tunnel could not be opened.
200
+ * - The contract or other necessary data could not be retrieved.
201
+ * - Any other unexpected error occurs.
202
+ */
203
+ async function getSigningKeysWithQueueInfo ( monitoring ) {
204
+ try {
205
+ // Open RPC tunnel
206
+ log . info ( "Opening RPC tunnel..." ) ;
207
+ await monitoring . openRpcTunnel ( ) ;
208
+
209
+ // look up Node Operator ID
210
+ log . info ( "Get Node Operator ID from LCOM" ) ;
211
+ const lcomServices = await monitoring . getServiceInfos ( "LCOMService" ) ;
212
+ if ( lcomServices . length < 1 ) {
213
+ throw new Error ( "LCOM service not found" ) ;
214
+ }
215
+ const nodeOperatorId = lcomServices . find ( ( s ) => s . config . env . NO_ID ) . config . env . NO_ID ;
216
+ if ( ! nodeOperatorId ) {
217
+ throw new Error ( "Node Operator ID not found in LCOM Config" ) ;
218
+ }
219
+ log . info ( "Node Operator ID:" , nodeOperatorId ) ;
220
+
221
+ // Initialize the contract
222
+ const contract = await getContract ( ) ;
223
+ if ( ! contract ) {
224
+ log . error ( "Failed to initialize contract." ) ;
225
+ return null ;
226
+ }
227
+
228
+ // Check if the node is in sync
229
+ const isSynced = await getSyncStatus ( ) ;
230
+ if ( ! isSynced ) {
231
+ log . info ( "Node is currently syncing..." ) ;
232
+ return null ;
233
+ }
234
+
235
+ // Check if the Node Operator is active
236
+ const isActive = await isNodeOperatorActive ( contract , nodeOperatorId ) ;
237
+ if ( ! isActive ) {
238
+ log . info ( "Node Operator is not active." ) ;
239
+ return null ;
240
+ }
241
+
242
+ // Retrieve enqueued count
243
+ const enqueuedCount = await getNodeOperatorInfo ( contract , nodeOperatorId ) ;
244
+ if ( enqueuedCount === null || enqueuedCount <= 0 ) {
245
+ log . info ( "No enqueued validators for this Node Operator." ) ;
246
+ }
247
+
248
+ // Retrieve the number of non-withdrawn keys
249
+ const numberOfNoneWithdrawnKeys = await getNoneWithdrawnKeys ( contract , nodeOperatorId ) ;
250
+ if ( numberOfNoneWithdrawnKeys === null || numberOfNoneWithdrawnKeys <= 0 ) {
251
+ log . info ( "No non-withdrawn keys available." ) ;
252
+ return null ;
253
+ }
254
+
255
+ // Retrieve the signing keys
256
+ const signingKeys = await getSigningKeys ( contract , nodeOperatorId , 0 , numberOfNoneWithdrawnKeys ) ;
257
+ if ( ! signingKeys ) {
258
+ log . info ( "Failed to retrieve signing keys." ) ;
259
+ return null ;
260
+ }
261
+
262
+ // Initialize queue data
263
+ const queueData = await getDepositQueue ( contract ) ;
264
+ if ( ! queueData ) {
265
+ log . error ( "Failed to retrieve deposit queue data." ) ;
266
+ return null ;
267
+ }
268
+ const { head, tail } = queueData ;
269
+
270
+ // Prepare results
271
+ const signingKeysWithQueueInfo = signingKeys . map ( ( key ) => ( { key, queuePosition : 0 } ) ) ; // Default queuePosition to 0
272
+
273
+ if ( enqueuedCount > 0 ) {
274
+ let remainingKeysToMark = Number ( enqueuedCount ) ;
275
+
276
+ // Traverse the deposit queue from tail to head
277
+ for ( let index = tail ; index >= head && remainingKeysToMark > 0 ; index -- ) {
278
+ const queueItem = await getDepositQueueItem ( contract , index ) ;
279
+
280
+ if ( queueItem . nodeId === BigInt ( nodeOperatorId ) ) {
281
+ const keysCountInQueue = Number ( queueItem . keysCount ) ;
282
+
283
+ // Calculate queue position
284
+ const queuePosition = queueItem . nextBatch - BigInt ( head ) ;
285
+
286
+ // Determine range of keys to mark in reverse order
287
+ const startIndex = signingKeysWithQueueInfo . length - remainingKeysToMark ;
288
+ const endIndex = startIndex + keysCountInQueue ;
289
+
290
+ for ( let i = endIndex - 1 ; i >= startIndex ; i -- ) {
291
+ signingKeysWithQueueInfo [ i ] . queuePosition = queuePosition ;
292
+ }
293
+
294
+ // Reduce the remaining keys to mark
295
+ remainingKeysToMark -= keysCountInQueue ;
296
+ }
297
+ }
298
+ }
299
+
300
+ // Return signing keys with queue information
301
+ return signingKeysWithQueueInfo ;
302
+ } catch ( error ) {
303
+ log . error ( "Error in getSigningKeysWithQueueInfo:" , error ) ;
304
+ return null ;
305
+ } finally {
306
+ log . info ( "Closing RPC tunnel..." ) ;
307
+ await monitoring . closeRpcTunnel ( ) ;
308
+ }
309
+ }
310
+
147
311
/**
148
312
* Checks for matching signing keys of a specified Node Operator.
149
313
*
@@ -154,7 +318,7 @@ async function getSigningKeys(contract, nodeOperatorId, startIndex, keysCount) {
154
318
*
155
319
* @async
156
320
* @function checkSigningKeys
157
- * @param {number } nodeOperatorId - The ID of the Node Operator whose signing keys are to be checked .
321
+ * @param {Object } monitoring - Monitoring Object .
158
322
* @param {string[] } keysArray - An array of signing keys in hexadecimal format to be matched against the Node Operator's keys.
159
323
* @returns {Promise<string[]|boolean|null> } A promise that resolves to:
160
324
* - An array of matching signing keys (in hexadecimal format) if any matches are found.
@@ -254,4 +418,7 @@ async function checkSigningKeys(keysArray, monitoring) {
254
418
}
255
419
}
256
420
257
- export default checkSigningKeys ;
421
+ export default {
422
+ checkSigningKeys,
423
+ getSigningKeysWithQueueInfo,
424
+ } ;
0 commit comments