LCOV - code coverage report
Current view: top level - flamenco/genesis - fd_genesis_create.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 228 236 96.6 %
Date: 2026-01-23 05:02:40 Functions: 2 2 100.0 %

          Line data    Source code
       1             : #define FD_SCRATCH_USE_HANDHOLDING 1
       2             : #include "fd_genesis_create.h"
       3             : 
       4             : #include "../runtime/fd_system_ids.h"
       5             : #include "../runtime/program/fd_stake_program.h"
       6             : #include "../runtime/program/fd_vote_program.h"
       7             : #include "../runtime/sysvar/fd_sysvar_rent.h"
       8             : #include "../types/fd_types.h"
       9             : 
      10             : #define SORT_NAME sort_acct
      11         744 : #define SORT_KEY_T fd_pubkey_account_pair_t
      12         597 : #define SORT_BEFORE(a,b) (0>memcmp( (a).key.ul, (b).key.ul, sizeof(fd_pubkey_t) ))
      13             : #include "../../util/tmpl/fd_sort.c"
      14             : 
      15             : static ulong
      16             : genesis_create( void *                       buf,
      17             :                 ulong                        bufsz,
      18          12 :                 fd_genesis_options_t const * options ) {
      19             : 
      20          12 : # define REQUIRE(c)                         \
      21          72 :   do {                                      \
      22          72 :     if( FD_UNLIKELY( !(c) ) ) {             \
      23           0 :       FD_LOG_WARNING(( "FAIL: %s", #c ));   \
      24           0 :       return 0UL;                           \
      25           0 :     }                                       \
      26          72 :   } while(0);
      27             : 
      28          12 :   fd_genesis_solana_t genesis[1];
      29          12 :   fd_genesis_solana_new( genesis );
      30             : 
      31          12 :   genesis->cluster_type = 3;  /* development */
      32             : 
      33          12 :   genesis->creation_time  = options->creation_time;
      34          12 :   genesis->ticks_per_slot = options->ticks_per_slot;
      35          12 :   REQUIRE( genesis->ticks_per_slot );
      36             : 
      37          12 :   genesis->unused = 1024UL; /* match Anza genesis byte-for-byte */
      38             : 
      39          12 :   genesis->poh_config.has_hashes_per_tick = !!options->hashes_per_tick;
      40          12 :   genesis->poh_config.hashes_per_tick     =   options->hashes_per_tick;
      41             : 
      42          12 :   ulong target_tick_micros = options->target_tick_duration_micros;
      43          12 :   REQUIRE( target_tick_micros );
      44          12 :   genesis->poh_config.target_tick_duration = (fd_rust_duration_t) {
      45          12 :     .seconds     =         target_tick_micros / 1000000UL,
      46          12 :     .nanoseconds = (uint)( target_tick_micros % 1000000UL * 1000UL ),
      47          12 :   };
      48             : 
      49             :   /* Create fee rate governor */
      50             : 
      51          12 :   genesis->fee_rate_governor = (fd_fee_rate_governor_t) {
      52          12 :     .target_lamports_per_signature  =  10000UL,
      53          12 :     .target_signatures_per_slot     =  20000UL,
      54          12 :     .min_lamports_per_signature     =   5000UL,
      55          12 :     .max_lamports_per_signature     = 100000UL,
      56          12 :     .burn_percent                   =     50,
      57          12 :   };
      58             : 
      59             :   /* Create rent configuration */
      60             : 
      61          12 :   genesis->rent = (fd_rent_t) {
      62          12 :     .lamports_per_uint8_year = 3480,
      63          12 :     .exemption_threshold     = 2.0,
      64          12 :     .burn_percent            = 50,
      65          12 :   };
      66             : 
      67             :   /* Create inflation configuration */
      68             : 
      69          12 :   genesis->inflation = (fd_inflation_t) {
      70          12 :     .initial         = 0.08,
      71          12 :     .terminal        = 0.015,
      72          12 :     .taper           = 0.15,
      73          12 :     .foundation      = 0.05,
      74          12 :     .foundation_term = 7.0,
      75          12 :   };
      76             : 
      77             :   /* Create epoch schedule */
      78             :   /* TODO The epoch schedule should be configurable! */
      79             : 
      80             :   /* If warmup is enabled:
      81             :      MINIMUM_SLOTS_PER_EPOCH = 32
      82             :      first_normal_epoch = log2( slots_per_epoch ) - log2( MINIMUM_SLOTS_PER_EPOCH  )
      83             :      first_normal_slot  = MINIMUM_SLOTS_PER_EPOCH * ( 2^( first_normal_epoch ) - 1 )
      84             :   */
      85             : 
      86          12 :   genesis->epoch_schedule = (fd_epoch_schedule_t) {
      87          12 :     .slots_per_epoch             = 8192UL,
      88          12 :     .leader_schedule_slot_offset = 8192UL,
      89          12 :     .warmup                      = fd_uchar_if( options->warmup_epochs,    1,   0   ),
      90          12 :     .first_normal_epoch          = fd_ulong_if( options->warmup_epochs,    8UL, 0UL ),
      91          12 :     .first_normal_slot           = fd_ulong_if( options->warmup_epochs, 8160UL, 0UL ),
      92          12 :   };
      93             : 
      94             :   /* Create faucet account */
      95             : 
      96          12 :   fd_pubkey_account_pair_t const faucet_account = {
      97          12 :     .key = options->faucet_pubkey,
      98          12 :     .account = {
      99          12 :       .lamports   = options->faucet_balance,
     100          12 :       .owner      = fd_solana_system_program_id
     101          12 :     }
     102          12 :   };
     103          12 :   ulong const faucet_account_index = genesis->accounts_len++;
     104             : 
     105             :   /* Create identity account (vote authority, withdraw authority) */
     106             : 
     107          12 :   fd_pubkey_account_pair_t const identity_account = {
     108          12 :     .key = options->identity_pubkey,
     109          12 :     .account = {
     110          12 :       .lamports   = 500000000000UL /* 500 SOL */,
     111          12 :       .owner      = fd_solana_system_program_id
     112          12 :     }
     113          12 :   };
     114          12 :   ulong const identity_account_index = genesis->accounts_len++;
     115             : 
     116             :   /* Create vote account */
     117             : 
     118          12 :   ulong const vote_account_index = genesis->accounts_len++;
     119             : 
     120          12 :   uchar vote_state_data[ FD_VOTE_STATE_V3_SZ ] = {0};
     121             : 
     122          12 :   FD_SCRATCH_SCOPE_BEGIN {
     123          12 :     fd_vote_state_versioned_t vsv[1];
     124          12 :     fd_vote_state_versioned_new_disc( vsv, fd_vote_state_versioned_enum_v3 );
     125             : 
     126          12 :     fd_vote_state_v3_t * vs = &vsv->inner.v3;
     127          12 :     vs->node_pubkey             = options->identity_pubkey;
     128          12 :     vs->authorized_withdrawer   = options->identity_pubkey;
     129          12 :     vs->commission              = 100;
     130          12 :     uchar * pool_mem = fd_scratch_alloc( fd_vote_authorized_voters_pool_align(), fd_vote_authorized_voters_pool_footprint( 1UL ) );
     131          12 :     vs->authorized_voters.pool  = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, 1UL ) );
     132          12 :     uchar * mem = fd_scratch_alloc( fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( 1UL ) );
     133          12 :     vs->authorized_voters.treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( mem, 1UL ) );
     134             : 
     135          12 :     fd_vote_authorized_voter_t * ele =
     136          12 :       fd_vote_authorized_voters_pool_ele_acquire( vs->authorized_voters.pool );
     137          12 :     *ele = (fd_vote_authorized_voter_t) {
     138          12 :       .epoch  = 0UL,
     139          12 :       .pubkey = options->identity_pubkey,
     140          12 :       .prio   = options->identity_pubkey.ul[0],  /* treap prio */
     141          12 :     };
     142          12 :     fd_vote_authorized_voters_treap_ele_insert( vs->authorized_voters.treap, ele, vs->authorized_voters.pool );
     143             : 
     144          12 :     fd_bincode_encode_ctx_t encode =
     145          12 :       { .data    = vote_state_data,
     146          12 :         .dataend = vote_state_data + sizeof(vote_state_data) };
     147          12 :     REQUIRE( fd_vote_state_versioned_encode( vsv, &encode ) == FD_BINCODE_SUCCESS );
     148          12 :   }
     149          12 :   FD_SCRATCH_SCOPE_END;
     150             : 
     151             :   /* Create stake account */
     152             : 
     153          12 :   ulong const stake_account_index = genesis->accounts_len++;
     154             : 
     155          12 :   uchar stake_data[ FD_STAKE_STATE_V2_SZ ] = {0};
     156             : 
     157          12 :   ulong stake_state_min_bal = fd_rent_exempt_minimum_balance( &genesis->rent, FD_STAKE_STATE_V2_SZ );
     158          12 :   ulong vote_min_bal        = fd_rent_exempt_minimum_balance( &genesis->rent, FD_VOTE_STATE_V3_SZ  );
     159             : 
     160          12 :   do {
     161          12 :     fd_stake_state_v2_t state[1];
     162          12 :     fd_stake_state_v2_new_disc( state, fd_stake_state_v2_enum_stake );
     163             : 
     164          12 :     fd_stake_state_v2_stake_t * stake = &state->inner.stake;
     165          12 :     stake->meta = (fd_stake_meta_t) {
     166          12 :       .rent_exempt_reserve = stake_state_min_bal,
     167          12 :       .authorized = {
     168          12 :         .staker     = options->identity_pubkey,
     169          12 :         .withdrawer = options->identity_pubkey,
     170          12 :       }
     171          12 :     };
     172          12 :     stake->stake = (fd_stake_t) {
     173          12 :       .delegation = (fd_delegation_t) {
     174          12 :         .voter_pubkey       = options->vote_pubkey,
     175          12 :         .stake              = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
     176          12 :         .activation_epoch   = ULONG_MAX, /*  bootstrap stake denoted with ULONG_MAX */
     177          12 :         .deactivation_epoch = ULONG_MAX
     178          12 :       },
     179          12 :       .credits_observed = 0UL
     180          12 :     };
     181             : 
     182          12 :     fd_bincode_encode_ctx_t encode =
     183          12 :       { .data    = stake_data,
     184          12 :         .dataend = stake_data + sizeof(stake_data) };
     185          12 :     REQUIRE( fd_stake_state_v2_encode( state, &encode ) == FD_BINCODE_SUCCESS );
     186          12 :   } while(0);
     187             : 
     188             :   /* Create stake config account */
     189             : 
     190          12 :   ulong const stake_cfg_account_index = genesis->accounts_len++;
     191             : 
     192          12 :   uchar stake_cfg_data[10];
     193          12 :   do {
     194          12 :     fd_stake_config_t config[1] = {{
     195          12 :       .config_keys_len      =  0,
     196          12 :       .warmup_cooldown_rate =  0.25,
     197          12 :       .slash_penalty        = 12
     198          12 :     }};
     199             : 
     200          12 :     fd_bincode_encode_ctx_t encode =
     201          12 :       { .data    = stake_cfg_data,
     202          12 :         .dataend = stake_cfg_data + sizeof(stake_cfg_data) };
     203          12 :     REQUIRE( fd_stake_config_encode( config, &encode ) == FD_BINCODE_SUCCESS );
     204          12 :     REQUIRE( encode.data == encode.dataend );
     205          12 :   } while(0);
     206             : 
     207             :   /* Read enabled features */
     208             : 
     209          12 :   ulong         feature_cnt = 0UL;
     210          12 :   fd_pubkey_t * features =
     211          12 :       fd_scratch_alloc( alignof(fd_pubkey_t), FD_FEATURE_ID_CNT * sizeof(fd_pubkey_t) );
     212             : 
     213          12 :   if( options->features ) {
     214           3 :     for( fd_feature_id_t const * id = fd_feature_iter_init();
     215         777 :                                      !fd_feature_iter_done( id );
     216         774 :                                  id = fd_feature_iter_next( id ) ) {
     217         774 :       if( fd_features_get( options->features, id ) == 0UL )
     218           3 :         features[ feature_cnt++ ] = id->id;
     219         774 :     }
     220           3 :   }
     221             : 
     222             :   /* Allocate the account table */
     223             : 
     224          12 :   ulong default_funded_cnt = options->fund_initial_accounts;
     225             : 
     226          12 :   ulong default_funded_idx = genesis->accounts_len;      genesis->accounts_len += default_funded_cnt;
     227          12 :   ulong feature_gate_idx   = genesis->accounts_len;      genesis->accounts_len += feature_cnt;
     228             : 
     229          12 :   genesis->accounts = fd_scratch_alloc( alignof(fd_pubkey_account_pair_t),
     230          12 :                                         genesis->accounts_len * sizeof(fd_pubkey_account_pair_t) );
     231          12 :   fd_memset( genesis->accounts, 0,      genesis->accounts_len * sizeof(fd_pubkey_account_pair_t) );
     232             : 
     233          12 :   genesis->accounts[ faucet_account_index ] = faucet_account;
     234          12 :   genesis->accounts[ identity_account_index ] = identity_account;
     235          12 :   genesis->accounts[ stake_account_index ] = (fd_pubkey_account_pair_t) {
     236          12 :     .key     = options->stake_pubkey,
     237          12 :     .account = (fd_solana_account_t) {
     238          12 :       .lamports   = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
     239          12 :       .data_len   = FD_STAKE_STATE_V2_SZ,
     240          12 :       .data       = stake_data,
     241          12 :       .owner      = fd_solana_stake_program_id
     242          12 :     }
     243          12 :   };
     244          12 :   genesis->accounts[ stake_cfg_account_index ] = (fd_pubkey_account_pair_t) {
     245          12 :     .key     = fd_solana_stake_program_config_id,
     246          12 :     .account = (fd_solana_account_t) {
     247          12 :       .lamports   = fd_rent_exempt_minimum_balance( &genesis->rent, sizeof(stake_cfg_data) ),
     248          12 :       .data_len   = sizeof(stake_cfg_data),
     249          12 :       .data       = stake_cfg_data,
     250          12 :       .owner      = fd_solana_config_program_id
     251          12 :     }
     252          12 :   };
     253          12 :   genesis->accounts[ vote_account_index ] = (fd_pubkey_account_pair_t) {
     254          12 :     .key     = options->vote_pubkey,
     255          12 :     .account = (fd_solana_account_t) {
     256          12 :       .lamports   = vote_min_bal,
     257          12 :       .data_len   = FD_VOTE_STATE_V3_SZ,
     258          12 :       .data       = vote_state_data,
     259          12 :       .owner      = fd_solana_vote_program_id
     260          12 :     }
     261          12 :   };
     262             : 
     263             :   /* Set up primordial accounts */
     264             : 
     265          12 :   ulong default_funded_balance = options->fund_initial_amount_lamports;
     266         108 :   for( ulong j=0UL; j<default_funded_cnt; j++ ) {
     267          96 :     fd_pubkey_account_pair_t * pair = &genesis->accounts[ default_funded_idx+j ];
     268             : 
     269          96 :     uchar privkey[ 32 ] = {0};
     270          96 :     FD_STORE( ulong, privkey, j );
     271          96 :     fd_sha512_t sha[1];
     272          96 :     fd_ed25519_public_from_private( pair->key.key, privkey, sha );
     273             : 
     274          96 :     pair->account = (fd_solana_account_t) {
     275          96 :       .lamports   = default_funded_balance,
     276          96 :       .data_len   = 0UL,
     277          96 :       .owner      = fd_solana_system_program_id
     278          96 :     };
     279          96 :   }
     280             : 
     281          15 : #define FEATURE_ENABLED_SZ 9UL
     282          12 :   static const uchar feature_enabled_data[ FEATURE_ENABLED_SZ ] = { 1, 0, 0, 0, 0, 0, 0, 0, 0 };
     283          12 :   ulong default_feature_enabled_balance = fd_rent_exempt_minimum_balance( &genesis->rent, FEATURE_ENABLED_SZ );
     284             : 
     285             :   /* Set up feature gate accounts */
     286          15 :   for( ulong j=0UL; j<feature_cnt; j++ ) {
     287           3 :     fd_pubkey_account_pair_t * pair = &genesis->accounts[ feature_gate_idx+j ];
     288             : 
     289           3 :     pair->key     = features[ j ];
     290           3 :     pair->account = (fd_solana_account_t) {
     291           3 :       .lamports   = default_feature_enabled_balance,
     292           3 :       .data_len   = FEATURE_ENABLED_SZ,
     293           3 :       .data       = (uchar *)feature_enabled_data,
     294           3 :       .owner      = fd_solana_feature_program_id
     295           3 :     };
     296           3 :   }
     297          12 : #undef FEATURE_ENABLED_SZ
     298             : 
     299             :   /* Sort and check for duplicates */
     300             : 
     301          12 :   sort_acct_inplace( genesis->accounts, genesis->accounts_len );
     302             : 
     303         159 :   for( ulong j=1UL; j < genesis->accounts_len; j++ ) {
     304         147 :     if( 0==memcmp( genesis->accounts[j-1].key.ul, genesis->accounts[j].key.ul, sizeof(fd_pubkey_t) ) ) {
     305           0 :       char dup_cstr[ FD_BASE58_ENCODED_32_SZ ];
     306           0 :       fd_base58_encode_32( genesis->accounts[j].key.uc, NULL, dup_cstr );
     307           0 :       FD_LOG_WARNING(( "Account %s is duplicate", dup_cstr ));
     308           0 :       return 0UL;
     309           0 :     }
     310         147 :   }
     311             : 
     312             :   /* Serialize bincode blob */
     313             : 
     314          12 :   fd_bincode_encode_ctx_t encode =
     315          12 :     { .data    = buf,
     316          12 :       .dataend = (uchar *)buf + bufsz };
     317          12 :   int encode_err = fd_genesis_solana_encode( genesis, &encode );
     318          12 :   if( FD_UNLIKELY( encode_err ) ) {
     319           3 :     FD_LOG_WARNING(( "Failed to encode genesis blob (bufsz=%lu)", bufsz ));
     320           3 :     return 0UL;
     321           3 :   }
     322           9 :   return (ulong)encode.data - (ulong)buf;
     323             : 
     324          12 : # undef REQUIRE
     325          12 : }
     326             : 
     327             : ulong
     328             : fd_genesis_create( void *                       buf,
     329             :                    ulong                        bufsz,
     330          12 :                    fd_genesis_options_t const * options ) {
     331          12 :   fd_scratch_push();
     332          12 :   ulong ret = genesis_create( buf, bufsz, options );
     333          12 :   fd_scratch_pop();
     334          12 :   return ret;
     335          12 : }

Generated by: LCOV version 1.14