LCOV - code coverage report
Current view: top level - flamenco/features - fd_features.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 49 100 49.0 %
Date: 2026-11-11 12:40:43 Functions: 5 8 62.5 %

          Line data    Source code
       1             : #include "fd_features.h"
       2             : #include "../runtime/fd_system_ids.h"
       3             : #include "../runtime/sysvar/fd_sysvar_epoch_schedule.h"
       4             : 
       5             : FD_STATIC_ASSERT( sizeof  ( fd_feature_t                  )==9UL, layout );
       6             : FD_STATIC_ASSERT( offsetof( fd_feature_t, is_active       )==0UL, layout );
       7             : FD_STATIC_ASSERT( offsetof( fd_feature_t, activation_slot )==1UL, layout );
       8             : 
       9             : fd_feature_t *
      10             : fd_feature_decode( fd_feature_t * feature,
      11             :                    uchar const *  data,
      12        7841 :                    ulong          data_sz ) {
      13        7841 :   if( FD_UNLIKELY( data_sz < sizeof(fd_feature_t) ) ) return NULL;
      14        7841 :   *feature = FD_LOAD( fd_feature_t, data );
      15        7841 :   if( FD_UNLIKELY( feature->is_active>1 ) ) return NULL;
      16        7841 :   return feature;
      17        7841 : }
      18             : 
      19             : void
      20           0 : fd_features_enable_all( fd_features_t * f ) {
      21           0 :   for( fd_feature_id_t const * id = fd_feature_iter_init();
      22           0 :        !fd_feature_iter_done( id );
      23           0 :        id = fd_feature_iter_next( id ) ) {
      24           0 :     fd_features_set( f, id, 0UL );
      25           0 :   }
      26           0 : }
      27             : 
      28             : void
      29       66906 : fd_features_disable_all( fd_features_t * f ) {
      30       66906 :   for( fd_feature_id_t const * id = fd_feature_iter_init();
      31    20212296 :        !fd_feature_iter_done( id );
      32    20145390 :        id = fd_feature_iter_next( id ) ) {
      33    20145390 :     fd_features_set( f, id, FD_FEATURE_DISABLED );
      34    20145390 :   }
      35       66906 : }
      36             : 
      37             : void
      38           0 : fd_features_enable_cleaned_up( fd_features_t * f ) {
      39           0 :   for( fd_feature_id_t const * id = fd_feature_iter_init();
      40           0 :        !fd_feature_iter_done( id );
      41           0 :        id = fd_feature_iter_next( id ) ) {
      42           0 :     if( FD_LIKELY( id->cleaned_up ) ) {
      43           0 :       fd_features_set( f, id, 0UL );
      44           0 :     } else {
      45           0 :       fd_features_set( f, id, FD_FEATURE_DISABLED );
      46           0 :     }
      47           0 :   }
      48           0 : }
      49             : 
      50             : static uchar one_off_forced[ FD_FEATURE_ID_CNT ];
      51             : 
      52             : void
      53           0 : fd_features_enable_one_offs( fd_features_t * f, char const * * one_offs, uint one_offs_cnt, ulong slot ) {
      54           0 :   uchar pubkey[32];
      55           0 :   for( uint i=0U; i<one_offs_cnt; i++ ) {
      56           0 :     if( FD_UNLIKELY( !fd_base58_decode_32( one_offs[i], pubkey ) ) )
      57           0 :       FD_LOG_ERR(( "Invalid feature key \"%s\" in [tiles.replay].enable_features", one_offs[i] ));
      58           0 :     fd_feature_id_t const * id = fd_feature_iter_init();
      59           0 :     for( ; !fd_feature_iter_done( id );
      60           0 :          id = fd_feature_iter_next( id ) ) {
      61           0 :       if( !memcmp( &id->id, pubkey, sizeof(fd_pubkey_t) ) ) {
      62           0 :         fd_features_set( f, id, slot );
      63           0 :         one_off_forced[ id->index ] = (uchar)( slot!=FD_FEATURE_DISABLED );
      64           0 :         break;
      65           0 :       }
      66           0 :     }
      67           0 :     if( FD_UNLIKELY( fd_feature_iter_done( id ) ) )
      68           0 :       FD_LOG_ERR(( "Unknown feature key \"%s\" in [tiles.replay].enable_features", one_offs[i] ));
      69           0 :   }
      70           0 : }
      71             : 
      72             : static void
      73             : fd_feature_restore( fd_features_t *             features,
      74             :                     fd_accdb_t *                accdb,
      75             :                     fd_accdb_fork_id_t          fork_id,
      76             :                     ulong                       slot,
      77             :                     fd_epoch_schedule_t const * epoch_schedule,
      78        9300 :                     fd_feature_id_t const *     id ) {
      79             : 
      80        9300 :   if( FD_UNLIKELY( id->cleaned_up ) ) {
      81        6630 :     fd_features_set( features, id, 0UL );
      82        6630 :     return;
      83        6630 :   }
      84             : 
      85        2670 :   fd_features_set( features, id, FD_FEATURE_DISABLED );
      86             : 
      87             :   /* Skip reverted features */
      88        2670 :   if( FD_UNLIKELY( id->reverted ) ) return;
      89             : 
      90        2130 :   fd_acc_t acc = fd_accdb_read_one( accdb, fork_id, id->id.uc );
      91        2130 :   if( FD_UNLIKELY( !acc.lamports ) ) {
      92        1266 :     fd_accdb_unread_one( accdb, &acc );
      93        1266 :     return;
      94        1266 :   }
      95             : 
      96             :   /* Skip accounts that are not owned by the feature program
      97             :      https://github.com/anza-xyz/solana-sdk/blob/6512aca61167088ce10f2b545c35c9bcb1400e70/feature-gate-interface/src/lib.rs#L42-L44 */
      98         864 :   if( FD_UNLIKELY( memcmp( acc.owner, fd_solana_feature_program_id.uc, 32UL ) ) ) {
      99           0 :     fd_accdb_unread_one( accdb, &acc );
     100           0 :     return;
     101           0 :   }
     102             : 
     103             :   /* Account data size must be >= FD_FEATURE_SIZEOF (9 bytes)
     104             :      https://github.com/anza-xyz/solana-sdk/blob/6512aca61167088ce10f2b545c35c9bcb1400e70/feature-gate-interface/src/lib.rs#L45-L47 */
     105         864 :   if( FD_UNLIKELY( acc.data_len<sizeof(fd_feature_t) ) ) {
     106           0 :     fd_accdb_unread_one( accdb, &acc );
     107           0 :     return;
     108           0 :   }
     109             : 
     110             :   /* Deserialize the feature account data
     111             :      https://github.com/anza-xyz/solana-sdk/blob/6512aca61167088ce10f2b545c35c9bcb1400e70/feature-gate-interface/src/lib.rs#L48-L50 */
     112         864 :   fd_feature_t feature[1];
     113         864 :   if( FD_UNLIKELY( !fd_feature_decode( feature, acc.data, acc.data_len ) ) ) {
     114           0 :     fd_accdb_unread_one( accdb, &acc );
     115           0 :     return;
     116           0 :   }
     117         864 :   fd_accdb_unread_one( accdb, &acc );
     118             : 
     119         864 :   FD_BASE58_ENCODE_32_BYTES( id->id.uc, addr_b58 );
     120         864 :   if( feature->is_active ) {
     121         864 :     FD_LOG_DEBUG(( "feature %s activated at slot %lu", addr_b58, feature->activation_slot ));
     122         864 :     fd_features_set( features, id, feature->activation_slot );
     123         864 :   } else if( fd_slot_to_epoch( epoch_schedule, slot, NULL )!=fd_slot_to_epoch( epoch_schedule, slot+1UL, NULL ) ) {
     124           0 :     ulong activation_slot = slot+1UL;
     125           0 :     FD_LOG_DEBUG(( "feature %s pending, pre-populating activation at slot %lu", addr_b58, activation_slot ));
     126           0 :     fd_features_set( features, id, activation_slot );
     127           0 :   } else {
     128           0 :     FD_LOG_DEBUG(( "feature %s not activated at slot %lu", addr_b58, feature->activation_slot ));
     129           0 :   }
     130         864 : }
     131             : 
     132             : void
     133             : fd_features_restore_chunk( fd_features_t *             features,
     134             :                            fd_accdb_t *                accdb,
     135             :                            fd_accdb_fork_id_t          fork_id,
     136             :                            ulong                       slot,
     137             :                            fd_epoch_schedule_t const * epoch_schedule,
     138             :                            ulong                       chunk_idx,
     139          30 :                            ulong                       chunk_cnt ) {
     140          30 :   FD_TEST( chunk_cnt>0UL );
     141          30 :   FD_TEST( chunk_idx<chunk_cnt );
     142             : 
     143          30 :   ulong begin = chunk_idx     * FD_FEATURE_ID_CNT / chunk_cnt;
     144          30 :   ulong end   = (chunk_idx+1) * FD_FEATURE_ID_CNT / chunk_cnt;
     145             : 
     146        9330 :   for( fd_feature_id_t const * id=ids+begin; id<ids+end; id++ ) {
     147        9300 :     fd_feature_restore( features, accdb, fork_id, slot, epoch_schedule, id );
     148        9300 :     if( FD_UNLIKELY( one_off_forced[ id->index ] ) ) fd_features_set( features, id, 0UL );
     149        9300 :   }
     150          30 : }
     151             : 
     152             : void
     153             : fd_features_restore( fd_features_t *             features,
     154             :                      fd_accdb_t *                accdb,
     155             :                      fd_accdb_fork_id_t          fork_id,
     156             :                      ulong                       slot,
     157          30 :                      fd_epoch_schedule_t const * epoch_schedule ) {
     158          30 :   fd_features_restore_chunk( features, accdb, fork_id, slot, epoch_schedule, 0UL, 1UL );
     159          30 : }

Generated by: LCOV version 1.14