LCOV - code coverage report
Current view: top level - flamenco/genesis - fd_genesis_create.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 226 234 96.6 %
Date: 2024-11-13 11:58:15 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 :     vs->authorized_voters.pool  = fd_vote_authorized_voters_pool_alloc ( fd_scratch_virtual(), 1UL );
     132          12 :     vs->authorized_voters.treap = fd_vote_authorized_voters_treap_alloc( fd_scratch_virtual(), 1UL );
     133             : 
     134          12 :     fd_vote_authorized_voter_t * ele =
     135          12 :       fd_vote_authorized_voters_pool_ele_acquire( vs->authorized_voters.pool );
     136          12 :     *ele = (fd_vote_authorized_voter_t) {
     137          12 :       .epoch  = 0UL,
     138          12 :       .pubkey = options->identity_pubkey,
     139          12 :       .prio   = options->identity_pubkey.ul[0],  /* treap prio */
     140          12 :     };
     141          12 :     fd_vote_authorized_voters_treap_ele_insert( vs->authorized_voters.treap, ele, vs->authorized_voters.pool );
     142             : 
     143          12 :     fd_bincode_encode_ctx_t encode =
     144          12 :       { .data    = vote_state_data,
     145          12 :         .dataend = vote_state_data + sizeof(vote_state_data) };
     146          12 :     REQUIRE( fd_vote_state_versioned_encode( vsv, &encode ) == FD_BINCODE_SUCCESS );
     147          12 :   }
     148          12 :   FD_SCRATCH_SCOPE_END;
     149             : 
     150             :   /* Create stake account */
     151             : 
     152          12 :   ulong const stake_account_index = genesis->accounts_len++;
     153             : 
     154          12 :   uchar stake_data[ FD_STAKE_STATE_V2_SZ ];
     155             : 
     156          12 :   ulong stake_state_min_bal = fd_rent_exempt_minimum_balance( &genesis->rent, FD_STAKE_STATE_V2_SZ );
     157          12 :   ulong vote_min_bal        = fd_rent_exempt_minimum_balance( &genesis->rent, FD_VOTE_STATE_V3_SZ  );
     158             : 
     159          12 :   do {
     160          12 :     fd_stake_state_v2_t state[1];
     161          12 :     fd_stake_state_v2_new_disc( state, fd_stake_state_v2_enum_stake );
     162             : 
     163          12 :     fd_stake_state_v2_stake_t * stake = &state->inner.stake;
     164          12 :     stake->meta = (fd_stake_meta_t) {
     165          12 :       .rent_exempt_reserve = stake_state_min_bal,
     166          12 :       .authorized = {
     167          12 :         .staker     = options->identity_pubkey,
     168          12 :         .withdrawer = options->identity_pubkey,
     169          12 :       }
     170          12 :     };
     171          12 :     stake->stake = (fd_stake_t) {
     172          12 :       .delegation = (fd_delegation_t) {
     173          12 :         .voter_pubkey       = options->vote_pubkey,
     174          12 :         .stake              = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
     175          12 :         .activation_epoch   = ULONG_MAX, /*  bootstrap stake denoted with ULONG_MAX */
     176          12 :         .deactivation_epoch = ULONG_MAX
     177          12 :       },
     178          12 :       .credits_observed = 0UL
     179          12 :     };
     180             : 
     181          12 :     fd_bincode_encode_ctx_t encode =
     182          12 :       { .data    = stake_data,
     183          12 :         .dataend = stake_data + sizeof(stake_data) };
     184          12 :     REQUIRE( fd_stake_state_v2_encode( state, &encode ) == FD_BINCODE_SUCCESS );
     185          12 :   } while(0);
     186             : 
     187             :   /* Create stake config account */
     188             : 
     189          12 :   ulong const stake_cfg_account_index = genesis->accounts_len++;
     190             : 
     191          12 :   uchar stake_cfg_data[10];
     192          12 :   do {
     193          12 :     fd_stake_config_t config[1] = {{
     194          12 :       .config_keys_len      =  0,
     195          12 :       .warmup_cooldown_rate =  0.25,
     196          12 :       .slash_penalty        = 12
     197          12 :     }};
     198             : 
     199          12 :     fd_bincode_encode_ctx_t encode =
     200          12 :       { .data    = stake_cfg_data,
     201          12 :         .dataend = stake_cfg_data + sizeof(stake_cfg_data) };
     202          12 :     REQUIRE( fd_stake_config_encode( config, &encode ) == FD_BINCODE_SUCCESS );
     203          12 :     REQUIRE( encode.data == encode.dataend );
     204          12 :   } while(0);
     205             : 
     206             :   /* Read enabled features */
     207             : 
     208          12 :   ulong         feature_cnt = 0UL;
     209          12 :   fd_pubkey_t * features =
     210          12 :       fd_scratch_alloc( alignof(fd_pubkey_t), FD_FEATURE_ID_CNT * sizeof(fd_pubkey_t) );
     211             : 
     212          12 :   if( options->features ) {
     213           3 :     for( fd_feature_id_t const * id = fd_feature_iter_init();
     214         612 :                                      !fd_feature_iter_done( id );
     215         609 :                                  id = fd_feature_iter_next( id ) ) {
     216         609 :       if( fd_features_get( options->features, id ) == 0UL )
     217           3 :         features[ feature_cnt++ ] = id->id;
     218         609 :     }
     219           3 :   }
     220             : 
     221             :   /* Allocate the account table */
     222             : 
     223          12 :   ulong default_funded_cnt = options->fund_initial_accounts;
     224             : 
     225          12 :   ulong default_funded_idx = genesis->accounts_len;      genesis->accounts_len += default_funded_cnt;
     226          12 :   ulong feature_gate_idx   = genesis->accounts_len;      genesis->accounts_len += feature_cnt;
     227             : 
     228          12 :   genesis->accounts = fd_scratch_alloc( alignof(fd_pubkey_account_pair_t),
     229          12 :                                         genesis->accounts_len * sizeof(fd_pubkey_account_pair_t) );
     230          12 :   fd_memset( genesis->accounts, 0,      genesis->accounts_len * sizeof(fd_pubkey_account_pair_t) );
     231             : 
     232          12 :   genesis->accounts[ faucet_account_index ] = faucet_account;
     233          12 :   genesis->accounts[ identity_account_index ] = identity_account;
     234          12 :   genesis->accounts[ stake_account_index ] = (fd_pubkey_account_pair_t) {
     235          12 :     .key     = options->stake_pubkey,
     236          12 :     .account = (fd_solana_account_t) {
     237          12 :       .lamports   = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
     238          12 :       .data_len   = FD_STAKE_STATE_V2_SZ,
     239          12 :       .data       = stake_data,
     240          12 :       .owner      = fd_solana_stake_program_id
     241          12 :     }
     242          12 :   };
     243          12 :   genesis->accounts[ stake_cfg_account_index ] = (fd_pubkey_account_pair_t) {
     244          12 :     .key     = fd_solana_stake_program_config_id,
     245          12 :     .account = (fd_solana_account_t) {
     246          12 :       .lamports   = fd_rent_exempt_minimum_balance( &genesis->rent, sizeof(stake_cfg_data) ),
     247          12 :       .data_len   = sizeof(stake_cfg_data),
     248          12 :       .data       = stake_cfg_data,
     249          12 :       .owner      = fd_solana_config_program_id
     250          12 :     }
     251          12 :   };
     252          12 :   genesis->accounts[ vote_account_index ] = (fd_pubkey_account_pair_t) {
     253          12 :     .key     = options->vote_pubkey,
     254          12 :     .account = (fd_solana_account_t) {
     255          12 :       .lamports   = vote_min_bal,
     256          12 :       .data_len   = FD_VOTE_STATE_V3_SZ,
     257          12 :       .data       = vote_state_data,
     258          12 :       .owner      = fd_solana_vote_program_id
     259          12 :     }
     260          12 :   };
     261             : 
     262             :   /* Set up primordial accounts */
     263             : 
     264          12 :   ulong default_funded_balance = options->fund_initial_amount_lamports;
     265         108 :   for( ulong j=0UL; j<default_funded_cnt; j++ ) {
     266          96 :     fd_pubkey_account_pair_t * pair = &genesis->accounts[ default_funded_idx+j ];
     267             : 
     268          96 :     uchar privkey[ 32 ] = {0};
     269          96 :     FD_STORE( ulong, privkey, j );
     270          96 :     fd_sha512_t sha[1];
     271          96 :     fd_ed25519_public_from_private( pair->key.key, privkey, sha );
     272             : 
     273          96 :     pair->account = (fd_solana_account_t) {
     274          96 :       .lamports   = default_funded_balance,
     275          96 :       .data_len   = 0UL,
     276          96 :       .owner      = fd_solana_system_program_id
     277          96 :     };
     278          96 :   }
     279             : 
     280          15 : #define FEATURE_ENABLED_SZ 9UL
     281          12 :   static const uchar feature_enabled_data[ FEATURE_ENABLED_SZ ] = { 1, 0, 0, 0, 0, 0, 0, 0, 0 };
     282          12 :   ulong default_feature_enabled_balance = fd_rent_exempt_minimum_balance( &genesis->rent, FEATURE_ENABLED_SZ );
     283             : 
     284             :   /* Set up feature gate accounts */
     285          15 :   for( ulong j=0UL; j<feature_cnt; j++ ) {
     286           3 :     fd_pubkey_account_pair_t * pair = &genesis->accounts[ feature_gate_idx+j ];
     287             : 
     288           3 :     pair->key     = features[ j ];
     289           3 :     pair->account = (fd_solana_account_t) {
     290           3 :       .lamports   = default_feature_enabled_balance,
     291           3 :       .data_len   = FEATURE_ENABLED_SZ,
     292           3 :       .data       = (uchar *)feature_enabled_data,
     293           3 :       .owner      = fd_solana_feature_program_id
     294           3 :     };
     295           3 :   }
     296          12 : #undef FEATURE_ENABLED_SZ
     297             : 
     298             :   /* Sort and check for duplicates */
     299             : 
     300          12 :   sort_acct_inplace( genesis->accounts, genesis->accounts_len );
     301             : 
     302         159 :   for( ulong j=1UL; j < genesis->accounts_len; j++ ) {
     303         147 :     if( 0==memcmp( genesis->accounts[j-1].key.ul, genesis->accounts[j].key.ul, sizeof(fd_pubkey_t) ) ) {
     304           0 :       char dup_cstr[ FD_BASE58_ENCODED_32_SZ ];
     305           0 :       fd_base58_encode_32( genesis->accounts[j].key.uc, NULL, dup_cstr );
     306           0 :       FD_LOG_WARNING(( "Account %s is duplicate", dup_cstr ));
     307           0 :       return 0UL;
     308           0 :     }
     309         147 :   }
     310             : 
     311             :   /* Serialize bincode blob */
     312             : 
     313          12 :   fd_bincode_encode_ctx_t encode =
     314          12 :     { .data    = buf,
     315          12 :       .dataend = (uchar *)buf + bufsz };
     316          12 :   int encode_err = fd_genesis_solana_encode( genesis, &encode );
     317          12 :   if( FD_UNLIKELY( encode_err ) ) {
     318           3 :     FD_LOG_WARNING(( "Failed to encode genesis blob (bufsz=%lu)", bufsz ));
     319           3 :     return 0UL;
     320           3 :   }
     321           9 :   return (ulong)encode.data - (ulong)buf;
     322             : 
     323          12 : # undef REQUIRE
     324          12 : }
     325             : 
     326             : ulong
     327             : fd_genesis_create( void *                       buf,
     328             :                    ulong                        bufsz,
     329          12 :                    fd_genesis_options_t const * options ) {
     330          12 :   fd_scratch_push();
     331          12 :   ulong ret = genesis_create( buf, bufsz, options );
     332          12 :   fd_scratch_pop();
     333          12 :   return ret;
     334          12 : }

Generated by: LCOV version 1.14