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

Generated by: LCOV version 1.14