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

Generated by: LCOV version 1.14