@@ -222,43 +222,63 @@ pub fn process_feature_subcommand(
222
222
}
223
223
}
224
224
225
- fn active_stake_by_feature_set ( rpc_client : & RpcClient ) -> Result < HashMap < u32 , f64 > , ClientError > {
225
+ fn feature_set_stats ( rpc_client : & RpcClient ) -> Result < HashMap < u32 , ( f64 , f32 ) > , ClientError > {
226
226
// Validator identity -> feature set
227
- let feature_set_map = rpc_client
227
+ let feature_sets = rpc_client
228
228
. get_cluster_nodes ( ) ?
229
229
. into_iter ( )
230
- . map ( |contact_info| ( contact_info. pubkey , contact_info. feature_set ) )
231
- . collect :: < HashMap < _ , _ > > ( ) ;
230
+ . map ( |contact_info| {
231
+ (
232
+ contact_info. pubkey ,
233
+ contact_info. feature_set ,
234
+ contact_info. rpc . is_some ( ) ,
235
+ )
236
+ } )
237
+ . collect :: < Vec < _ > > ( ) ;
232
238
233
239
let vote_accounts = rpc_client. get_vote_accounts ( ) ?;
234
240
235
- let total_active_stake: u64 = vote_accounts
236
- . current
241
+ let mut total_active_stake: u64 = vote_accounts
242
+ . delinquent
237
243
. iter ( )
238
- . chain ( vote_accounts. delinquent . iter ( ) )
239
244
. map ( |vote_account| vote_account. activated_stake )
240
245
. sum ( ) ;
241
246
242
- // Sum all active stake by feature set
243
- let mut active_stake_by_feature_set: HashMap < u32 , u64 > = HashMap :: new ( ) ;
244
- for vote_account in vote_accounts. current {
245
- if let Some ( Some ( feature_set) ) = feature_set_map. get ( & vote_account. node_pubkey ) {
246
- * active_stake_by_feature_set. entry ( * feature_set) . or_default ( ) +=
247
- vote_account. activated_stake ;
248
- } else {
249
- * active_stake_by_feature_set
250
- . entry ( 0 /* "unknown" */ )
251
- . or_default ( ) += vote_account. activated_stake ;
247
+ let vote_stakes = vote_accounts
248
+ . current
249
+ . into_iter ( )
250
+ . map ( |vote_account| {
251
+ total_active_stake += vote_account. activated_stake ;
252
+ ( vote_account. node_pubkey , vote_account. activated_stake )
253
+ } )
254
+ . collect :: < HashMap < _ , _ > > ( ) ;
255
+
256
+ let mut feature_set_stats: HashMap < u32 , ( u64 , u32 ) > = HashMap :: new ( ) ;
257
+ let mut total_rpc_nodes = 0 ;
258
+ for ( node_id, feature_set, is_rpc) in feature_sets {
259
+ let feature_set = feature_set. unwrap_or ( 0 ) ;
260
+ let feature_set_entry = feature_set_stats. entry ( feature_set) . or_default ( ) ;
261
+
262
+ if let Some ( vote_stake) = vote_stakes. get ( & node_id) {
263
+ feature_set_entry. 0 += * vote_stake;
264
+ }
265
+
266
+ if is_rpc {
267
+ feature_set_entry. 1 += 1 ;
268
+ total_rpc_nodes += 1 ;
252
269
}
253
270
}
254
271
255
- Ok ( active_stake_by_feature_set
272
+ Ok ( feature_set_stats
256
273
. into_iter ( )
257
- . map ( |( feature_set, active_stake) | {
258
- (
259
- feature_set,
260
- active_stake as f64 * 100. / total_active_stake as f64 ,
261
- )
274
+ . filter_map ( |( feature_set, ( active_stake, is_rpc) ) | {
275
+ let active_stake = active_stake as f64 * 100. / total_active_stake as f64 ;
276
+ let is_rpc = is_rpc as f32 * 100. / total_rpc_nodes as f32 ;
277
+ if active_stake >= 0.001 || is_rpc >= 0.001 {
278
+ Some ( ( feature_set, ( active_stake, is_rpc) ) )
279
+ } else {
280
+ None
281
+ }
262
282
} )
263
283
. collect ( ) )
264
284
}
@@ -267,50 +287,93 @@ fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, f6
267
287
fn feature_activation_allowed ( rpc_client : & RpcClient , quiet : bool ) -> Result < bool , ClientError > {
268
288
let my_feature_set = solana_version:: Version :: default ( ) . feature_set ;
269
289
270
- let active_stake_by_feature_set = active_stake_by_feature_set ( rpc_client) ?;
290
+ let feature_set_stats = feature_set_stats ( rpc_client) ?;
271
291
272
- let feature_activation_allowed = active_stake_by_feature_set
292
+ let ( stake_allowed , rpc_allowed ) = feature_set_stats
273
293
. get ( & my_feature_set)
274
- . map ( |percentage| * percentage >= 95. )
275
- . unwrap_or ( false ) ;
294
+ . map ( |( stake_percent , rpc_percent ) | ( * stake_percent >= 95. , * rpc_percent >= 95. ) )
295
+ . unwrap_or ( ( false , false ) ) ;
276
296
277
- if !feature_activation_allowed && !quiet {
278
- if active_stake_by_feature_set . get ( & my_feature_set) . is_none ( ) {
297
+ if !stake_allowed && !rpc_allowed && !quiet {
298
+ if feature_set_stats . get ( & my_feature_set) . is_none ( ) {
279
299
println ! (
280
300
"{}" ,
281
301
style( "To activate features the tool and cluster feature sets must match, select a tool version that matches the cluster" )
282
302
. bold( ) ) ;
283
303
} else {
284
- println ! (
285
- "{}" ,
286
- style( "To activate features the stake must be >= 95%" ) . bold( )
287
- ) ;
304
+ if !stake_allowed {
305
+ print ! (
306
+ "\n {}" ,
307
+ style( "To activate features the stake must be >= 95%" )
308
+ . bold( )
309
+ . red( )
310
+ ) ;
311
+ }
312
+ if !rpc_allowed {
313
+ print ! (
314
+ "\n {}" ,
315
+ style( "To activate features the RPC nodes must be >= 95%" )
316
+ . bold( )
317
+ . red( )
318
+ ) ;
319
+ }
288
320
}
289
321
println ! (
290
- "{}" ,
322
+ "\n \n {}" ,
291
323
style( format!( "Tool Feature Set: {}" , my_feature_set) ) . bold( )
292
324
) ;
293
- println ! ( "{}" , style( "Cluster Feature Sets and Stakes:" ) . bold( ) ) ;
294
- for ( feature_set, percentage) in active_stake_by_feature_set. iter ( ) {
295
- if * feature_set == 0 {
296
- println ! ( " unknown - {:.2}%" , percentage) ;
325
+ let feature_set_title = "Feature Set" ;
326
+ let stake_percent_title = "Stake" ;
327
+ let rpc_percent_title = "RPC" ;
328
+ let mut stats_output = Vec :: new ( ) ;
329
+ let mut max_feature_set_len = feature_set_title. len ( ) ;
330
+ let mut max_stake_percent_len = stake_percent_title. len ( ) ;
331
+ let mut max_rpc_percent_len = rpc_percent_title. len ( ) ;
332
+ for ( feature_set, ( stake_percent, rpc_percent) ) in feature_set_stats. iter ( ) {
333
+ let me = * feature_set == my_feature_set;
334
+ let feature_set = if * feature_set == 0 {
335
+ "unknown" . to_string ( )
297
336
} else {
298
- println ! (
299
- " {:<10} - {:.2}% {}" ,
300
- feature_set,
301
- percentage,
302
- if * feature_set == my_feature_set {
303
- " <-- me"
304
- } else {
305
- ""
306
- }
307
- ) ;
308
- }
337
+ feature_set. to_string ( )
338
+ } ;
339
+ let stake_percent = format ! ( "{:.2}%" , stake_percent) ;
340
+ let rpc_percent = format ! ( "{:.2}%" , rpc_percent) ;
341
+
342
+ max_feature_set_len = max_feature_set_len. max ( feature_set. len ( ) ) ;
343
+ max_stake_percent_len = max_stake_percent_len. max ( stake_percent. len ( ) ) ;
344
+ max_rpc_percent_len = max_rpc_percent_len. max ( rpc_percent. len ( ) ) ;
345
+
346
+ stats_output. push ( ( feature_set, stake_percent, rpc_percent, me) ) ;
347
+ }
348
+ println ! (
349
+ "{}" ,
350
+ style( format!(
351
+ "{1:<0$} {3:<2$} {5:<4$}" ,
352
+ max_feature_set_len,
353
+ feature_set_title,
354
+ max_stake_percent_len,
355
+ stake_percent_title,
356
+ max_rpc_percent_len,
357
+ rpc_percent_title,
358
+ ) )
359
+ . bold( ) ,
360
+ ) ;
361
+ for ( feature_set, stake_percent, rpc_percent, me) in stats_output {
362
+ println ! (
363
+ "{1:>0$} {3:>2$} {5:>4$} {6}" ,
364
+ max_feature_set_len,
365
+ feature_set,
366
+ max_stake_percent_len,
367
+ stake_percent,
368
+ max_rpc_percent_len,
369
+ rpc_percent,
370
+ if me { "<-- me" } else { "" } ,
371
+ ) ;
309
372
}
310
373
println ! ( ) ;
311
374
}
312
375
313
- Ok ( feature_activation_allowed )
376
+ Ok ( stake_allowed && rpc_allowed )
314
377
}
315
378
316
379
fn status_from_account ( account : Account ) -> Option < CliFeatureStatus > {
0 commit comments