LCOV - code coverage report
Current view: top level - flamenco/genesis - fd_genesis_create.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 388 405 95.8 %
Date: 2026-09-17 04:28:31 Functions: 11 11 100.0 %

          Line data    Source code
       1             : #include "fd_genesis_create.h"
       2             : 
       3             : #include "../runtime/fd_system_ids.h"
       4             : #include "../runtime/fd_pubkey_utils.h"
       5             : #include "../stakes/fd_stakes.h"
       6             : #include "../runtime/program/fd_vote_program.h"
       7             : #include "../runtime/program/fd_bpf_loader_program.h"
       8             : #include "../runtime/program/vote/fd_vote_codec_tmpl.h"
       9             : #include "../runtime/sysvar/fd_sysvar_rent.h"
      10             : #include "../../ballet/sha256/fd_sha256.h"
      11             : 
      12             : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/genesis/src/main.rs#L69 */
      13          30 : #define FD_GENESIS_VAT_MINIMUM_LAMPORTS (1600000000UL*100UL)
      14             : 
      15             : /* TODO: Unify type with the one in fd_genesis_parse.c */
      16             : 
      17             : struct fd_rust_duration {
      18             :   ulong seconds;
      19             :   uint nanoseconds;
      20             : };
      21             : typedef struct fd_rust_duration fd_rust_duration_t;
      22             : 
      23             : struct fd_poh_config {
      24             :   fd_rust_duration_t target_tick_duration;
      25             :   ulong target_tick_count;
      26             :   uchar has_target_tick_count;
      27             :   ulong hashes_per_tick;
      28             :   uchar has_hashes_per_tick;
      29             : };
      30             : typedef struct fd_poh_config fd_poh_config_t;
      31             : 
      32             : struct fd_genesis_account {
      33             :   ulong       lamports;
      34             :   ulong       data_len;
      35             :   uchar *     data;
      36             :   fd_pubkey_t owner;
      37             :   uchar       executable;
      38             :   ulong       rent_epoch;
      39             : };
      40             : typedef struct fd_genesis_account fd_genesis_account_t;
      41             : 
      42             : struct fd_genesis_account_pair {
      43             :   fd_pubkey_t          key;
      44             :   fd_genesis_account_t account;
      45             : };
      46             : typedef struct fd_genesis_account_pair fd_genesis_account_pair_t;
      47             : 
      48             : #define SORT_NAME sort_acct
      49        2181 : #define SORT_KEY_T fd_genesis_account_pair_t
      50        2124 : #define SORT_BEFORE(a,b) (0>memcmp( (a).key.ul, (b).key.ul, sizeof(fd_pubkey_t) ))
      51             : #include "../../util/tmpl/fd_sort.c"
      52             : 
      53             : /* SPL Token state layout sizes.  See
      54             :    https://github.com/solana-program/token/blob/program%40v8.0.0/interface/src/state.rs */
      55             : 
      56          12 : #define SPL_TOKEN_MINT_SZ    ( 82UL)
      57         195 : #define SPL_TOKEN_ACCOUNT_SZ (165UL)
      58             : 
      59             : /* token_addr derives the address of one of the token accounts, or of
      60             :    the mint.  The addresses are domain separated SHA-256 hashes, so they
      61             :    cannot collide with the ed25519 keys of the primordial accounts. */
      62             : 
      63           6 : #define TOKEN_ADDR_KIND_MINT ((uchar)0xFF)
      64             : 
      65             : FD_STATIC_ASSERT( FD_GENESIS_TOKEN_ACCOUNTS_PER_ACCOUNT<TOKEN_ADDR_KIND_MINT, token_addr_kind );
      66             : 
      67             : static void
      68             : token_addr( fd_pubkey_t * out,
      69             :             ulong         acct_idx,
      70         198 :             uchar         kind ) {
      71         198 :   fd_sha256_t sha[1];
      72         198 :   fd_sha256_init( sha );
      73         198 :   fd_sha256_append( sha, "fd_genesis_token", 16UL          );
      74         198 :   fd_sha256_append( sha, &kind,              sizeof(uchar) );
      75         198 :   fd_sha256_append( sha, &acct_idx,          sizeof(ulong) );
      76         198 :   fd_sha256_fini( sha, out->uc );
      77         198 : }
      78             : 
      79             : void
      80           6 : fd_genesis_token_mint_address( fd_pubkey_t * out ) {
      81           6 :   token_addr( out, 0UL, TOKEN_ADDR_KIND_MINT );
      82           6 : }
      83             : 
      84             : void
      85             : fd_genesis_token_account_address( fd_pubkey_t * out,
      86             :                                   ulong         acct_idx,
      87         192 :                                   ulong         token_idx ) {
      88         192 :   token_addr( out, acct_idx, (uchar)token_idx );
      89         192 : }
      90             : 
      91             : static inline uchar *
      92         432 : emit_u8( uchar * p, uchar * end, uchar v ) {
      93         432 :   if( FD_UNLIKELY( (ulong)end-(ulong)p<1UL ) ) return NULL;
      94         432 :   *p = v;
      95         432 :   return p+1;
      96         432 : }
      97             : 
      98             : static inline uchar *
      99          30 : emit_u32( uchar * p, uchar * end, uint v ) {
     100          30 :   if( FD_UNLIKELY( (ulong)end-(ulong)p<4UL ) ) return NULL;
     101          30 :   FD_STORE( uint, p, v );
     102          30 :   return p+4;
     103          30 : }
     104             : 
     105             : static inline uchar *
     106        1329 : emit_u64( uchar * p, uchar * end, ulong v ) {
     107        1329 :   if( FD_UNLIKELY( (ulong)end-(ulong)p<8UL ) ) return NULL;
     108        1326 :   FD_STORE( ulong, p, v );
     109        1326 :   return p+8;
     110        1329 : }
     111             : 
     112             : static inline uchar *
     113         105 : emit_f64( uchar * p, uchar * end, double v ) {
     114         105 :   if( FD_UNLIKELY( (ulong)end-(ulong)p<8UL ) ) return NULL;
     115         105 :   FD_STORE( double, p, v );
     116         105 :   return p+8;
     117         105 : }
     118             : 
     119             : static inline uchar *
     120         849 : emit_bytes( uchar * p, uchar * end, void const * src, ulong n ) {
     121         849 :   if( FD_UNLIKELY( (ulong)end-(ulong)p<n ) ) return NULL;
     122         849 :   if( FD_LIKELY( n ) ) fd_memcpy( p, src, n );
     123         849 :   return p+n;
     124         849 : }
     125             : 
     126             : /* Private struct mirroring the Solana GenesisConfig bincode layout.
     127             :    Only used locally for building up state before serialization. */
     128             : 
     129             : struct genesis_solana {
     130             :   ulong                      creation_time;
     131             :   ulong                      accounts_len;
     132             :   fd_genesis_account_pair_t * accounts;
     133             :   ulong                      native_instruction_processors_len;
     134             :   ulong                      rewards_pools_len;
     135             :   ulong                      ticks_per_slot;
     136             :   ulong                      unused;
     137             :   fd_poh_config_t            poh_config;
     138             :   ulong                      __backwards_compat_with_v0_23;
     139             :   fd_fee_rate_governor_t     fee_rate_governor;
     140             :   fd_rent_t                  rent;
     141             :   fd_inflation_t             inflation;
     142             :   fd_epoch_schedule_t        epoch_schedule;
     143             :   uint                       cluster_type;
     144             : };
     145             : typedef struct genesis_solana genesis_solana_t;
     146             : 
     147             : /* genesis_encode serializes a genesis_solana_t into a bincode blob
     148             :    byte-for-byte compatible with Anza's genesis.bin format.  Returns the
     149             :    number of bytes written, or 0 on failure (buffer too small). */
     150             : 
     151             : static ulong
     152             : genesis_encode( genesis_solana_t const * g,
     153             :                 uchar *                  buf,
     154          21 :                 ulong                    bufsz ) {
     155          21 :   if( FD_UNLIKELY( !buf ) ) return 0UL;
     156             : 
     157          18 :   uchar * p   = buf;
     158          18 :   uchar * end = buf + bufsz;
     159             : 
     160        2745 : # define EMIT(expr) do { p = (expr); if( FD_UNLIKELY( !p ) ) return 0UL; } while(0)
     161             : 
     162          18 :   EMIT( emit_u64( p, end, g->creation_time ) );
     163             : 
     164             :   /* accounts vector */
     165          15 :   EMIT( emit_u64( p, end, g->accounts_len ) );
     166         372 :   for( ulong i=0; i<g->accounts_len; i++ ) {
     167         357 :     fd_genesis_account_pair_t const * a = &g->accounts[i];
     168         357 :     EMIT( emit_bytes( p, end, a->key.key, 32 ) );
     169         357 :     EMIT( emit_u64(   p, end, a->account.lamports ) );
     170         357 :     EMIT( emit_u64(   p, end, a->account.data_len ) );
     171         357 :     if( a->account.data_len )
     172         135 :       EMIT( emit_bytes( p, end, a->account.data, a->account.data_len ) );
     173         357 :     EMIT( emit_bytes( p, end, a->account.owner.key, 32 ) );
     174         357 :     EMIT( emit_u8(    p, end, !!a->account.executable ) );
     175         357 :     EMIT( emit_u64(   p, end, a->account.rent_epoch ) );
     176         357 :   }
     177             : 
     178             :   /* native_instruction_processors vector
     179             :      TODO: currently always empty */
     180          15 :   EMIT( emit_u64( p, end, g->native_instruction_processors_len ) );
     181             : 
     182             :   /* rewards_pools vector
     183             :      TODO: currently always empty */
     184          15 :   EMIT( emit_u64( p, end, g->rewards_pools_len ) );
     185             : 
     186          15 :   EMIT( emit_u64( p, end, g->ticks_per_slot ) );
     187          15 :   EMIT( emit_u64( p, end, g->unused ) );
     188             : 
     189             :   /* poh_config
     190             :      TODO: has target tick count always == 0 */
     191          15 :   EMIT( emit_u64( p, end, g->poh_config.target_tick_duration.seconds ) );
     192          15 :   EMIT( emit_u32( p, end, g->poh_config.target_tick_duration.nanoseconds ) );
     193          15 :   EMIT( emit_u8(  p, end, !!g->poh_config.has_target_tick_count ) );
     194          15 :   if( g->poh_config.has_target_tick_count )
     195           0 :     EMIT( emit_u64( p, end, g->poh_config.target_tick_count ) );
     196          15 :   EMIT( emit_u8(  p, end, !!g->poh_config.has_hashes_per_tick ) );
     197          15 :   if( g->poh_config.has_hashes_per_tick )
     198           0 :     EMIT( emit_u64( p, end, g->poh_config.hashes_per_tick ) );
     199             : 
     200             :   /* TODO: always set to 0 */
     201          15 :   EMIT( emit_u64( p, end, g->__backwards_compat_with_v0_23 ) );
     202             : 
     203             :   /* fee_rate_governor */
     204          15 :   EMIT( emit_u64( p, end, g->fee_rate_governor.target_lamports_per_signature ) );
     205          15 :   EMIT( emit_u64( p, end, g->fee_rate_governor.target_signatures_per_slot ) );
     206          15 :   EMIT( emit_u64( p, end, g->fee_rate_governor.min_lamports_per_signature ) );
     207          15 :   EMIT( emit_u64( p, end, g->fee_rate_governor.max_lamports_per_signature ) );
     208          15 :   EMIT( emit_u8(  p, end, g->fee_rate_governor.burn_percent ) );
     209             : 
     210             :   /* rent */
     211          15 :   EMIT( emit_u64( p, end, g->rent.lamports_per_uint8_year ) );
     212          15 :   EMIT( emit_f64( p, end, g->rent.exemption_threshold ) );
     213          15 :   EMIT( emit_u8(  p, end, g->rent.burn_percent ) );
     214             : 
     215             :   /* inflation */
     216          15 :   EMIT( emit_f64( p, end, g->inflation.initial ) );
     217          15 :   EMIT( emit_f64( p, end, g->inflation.terminal ) );
     218          15 :   EMIT( emit_f64( p, end, g->inflation.taper ) );
     219          15 :   EMIT( emit_f64( p, end, g->inflation.foundation ) );
     220          15 :   EMIT( emit_f64( p, end, g->inflation.foundation_term ) );
     221          15 :   EMIT( emit_f64( p, end, g->inflation.unused ) );
     222             : 
     223             :   /* epoch_schedule */
     224          15 :   EMIT( emit_u64( p, end, g->epoch_schedule.slots_per_epoch ) );
     225          15 :   EMIT( emit_u64( p, end, g->epoch_schedule.leader_schedule_slot_offset ) );
     226          15 :   EMIT( emit_u8(  p, end, !!g->epoch_schedule.warmup ) );
     227          15 :   EMIT( emit_u64( p, end, g->epoch_schedule.first_normal_epoch ) );
     228          15 :   EMIT( emit_u64( p, end, g->epoch_schedule.first_normal_slot ) );
     229             : 
     230          15 :   EMIT( emit_u32( p, end, g->cluster_type ) );
     231             : 
     232          15 : # undef EMIT
     233          15 :   return (ulong)(p - buf);
     234          15 : }
     235             : 
     236             : static ulong
     237             : genesis_create( void *                       buf,
     238             :                 ulong                        bufsz,
     239          30 :                 fd_genesis_options_t const * options ) {
     240             : 
     241          30 : # define REQUIRE(c)                         \
     242         291 :   do {                                      \
     243         291 :     if( FD_UNLIKELY( !(c) ) ) {             \
     244           9 :       FD_LOG_WARNING(( "FAIL: %s", #c ));   \
     245           9 :       return 0UL;                           \
     246           9 :     }                                       \
     247         291 :   } while(0);
     248             : 
     249          30 :   genesis_solana_t genesis[1] = {0};
     250             : 
     251          30 :   genesis->cluster_type = 3;  /* development */
     252             : 
     253          30 :   genesis->creation_time  = options->creation_time;
     254          30 :   genesis->ticks_per_slot = options->ticks_per_slot;
     255          30 :   REQUIRE( genesis->ticks_per_slot );
     256             : 
     257          30 :   genesis->unused = 1024UL; /* match Anza genesis byte-for-byte */
     258             : 
     259          30 :   genesis->poh_config.has_hashes_per_tick = !!options->hashes_per_tick;
     260          30 :   genesis->poh_config.hashes_per_tick     =   options->hashes_per_tick;
     261             : 
     262          30 :   ulong target_tick_micros = options->target_tick_duration_micros;
     263          30 :   REQUIRE( target_tick_micros );
     264          30 :   genesis->poh_config.target_tick_duration = (fd_rust_duration_t) {
     265          30 :     .seconds     =         target_tick_micros / 1000000UL,
     266          30 :     .nanoseconds = (uint)( target_tick_micros % 1000000UL * 1000UL ),
     267          30 :   };
     268             : 
     269             :   /* Create fee rate governor */
     270             : 
     271          30 :   genesis->fee_rate_governor = (fd_fee_rate_governor_t) {
     272          30 :     .target_lamports_per_signature  =  10000UL,
     273          30 :     .target_signatures_per_slot     =  20000UL,
     274          30 :     .min_lamports_per_signature     =   5000UL,
     275          30 :     .max_lamports_per_signature     = 100000UL,
     276          30 :     .burn_percent                   =     50,
     277          30 :   };
     278             : 
     279             :   /* Create rent configuration */
     280             : 
     281          30 :   genesis->rent = (fd_rent_t) {
     282          30 :     .lamports_per_uint8_year = 3480,
     283          30 :     .exemption_threshold     = 2.0,
     284          30 :     .burn_percent            = 50,
     285          30 :   };
     286             : 
     287             :   /* Create inflation configuration */
     288             : 
     289          30 :   genesis->inflation = (fd_inflation_t) {
     290          30 :     .initial         = 0.08,
     291          30 :     .terminal        = 0.015,
     292          30 :     .taper           = 0.15,
     293          30 :     .foundation      = 0.05,
     294          30 :     .foundation_term = 7.0,
     295          30 :   };
     296             : 
     297             :   /* Create epoch schedule */
     298             :   /* TODO The epoch schedule should be configurable! */
     299             : 
     300             :   /* If warmup is enabled:
     301             :      MINIMUM_SLOTS_PER_EPOCH = 32
     302             :      first_normal_epoch = log2( slots_per_epoch ) - log2( MINIMUM_SLOTS_PER_EPOCH  )
     303             :      first_normal_slot  = MINIMUM_SLOTS_PER_EPOCH * ( 2^( first_normal_epoch ) - 1 )
     304             :   */
     305             : 
     306          30 :   genesis->epoch_schedule = (fd_epoch_schedule_t) {
     307          30 :     .slots_per_epoch             = 8192UL,
     308          30 :     .leader_schedule_slot_offset = 8192UL,
     309          30 :     .warmup                      = fd_uchar_if( options->warmup_epochs,    1,   0   ),
     310          30 :     .first_normal_epoch          = fd_ulong_if( options->warmup_epochs,    8UL, 0UL ),
     311          30 :     .first_normal_slot           = fd_ulong_if( options->warmup_epochs, 8160UL, 0UL ),
     312          30 :   };
     313             : 
     314             :   /* Create faucet account */
     315             : 
     316          30 :   fd_genesis_account_pair_t const faucet_account = {
     317          30 :     .key = options->faucet_pubkey,
     318          30 :     .account = {
     319          30 :       .lamports   = options->faucet_balance,
     320          30 :       .owner      = fd_solana_system_program_id
     321          30 :     }
     322          30 :   };
     323          30 :   ulong const faucet_account_index = genesis->accounts_len++;
     324             : 
     325             :   /* Create identity account (vote authority, withdraw authority) */
     326             : 
     327          30 :   fd_genesis_account_pair_t const identity_account = {
     328          30 :     .key = options->identity_pubkey,
     329          30 :     .account = {
     330          30 :       .lamports   = 500000000000UL /* 500 SOL */,
     331          30 :       .owner      = fd_solana_system_program_id
     332          30 :     }
     333          30 :   };
     334          30 :   ulong const identity_account_index = genesis->accounts_len++;
     335             : 
     336             :   /* Create vote account */
     337             : 
     338          30 :   ulong const vote_account_index = genesis->accounts_len++;
     339             : 
     340          30 :   uchar vote_state_data[ FD_VOTE_STATE_V4_SZ ] = {0};
     341             : 
     342          30 :   FD_SCRATCH_SCOPE_BEGIN {
     343          30 :     fd_vote_state_versioned_t versioned[1];
     344          30 :     fd_vote_state_versioned_new( versioned, fd_vote_state_versioned_enum_v4 );
     345             : 
     346          30 :     fd_vote_state_v4_t * vote_state              = &versioned->v4;
     347          30 :     vote_state->node_pubkey                      = options->identity_pubkey;
     348          30 :     vote_state->authorized_withdrawer            = options->identity_pubkey;
     349          30 :     vote_state->inflation_rewards_collector      = options->vote_pubkey;
     350          30 :     vote_state->block_revenue_collector          = options->identity_pubkey;
     351          30 :     vote_state->inflation_rewards_commission_bps = 10000;
     352          30 :     vote_state->block_revenue_commission_bps     = 0;
     353          30 :     vote_state->has_bls_pubkey_compressed        = 1;
     354             : 
     355          30 :     fd_vote_authorized_voter_t * voter = fd_vote_authorized_voters_pool_ele_acquire( vote_state->authorized_voters.pool );
     356          30 :     *voter = (fd_vote_authorized_voter_t) {
     357          30 :       .epoch  = 0UL,
     358          30 :       .pubkey = options->identity_pubkey,
     359          30 :       .prio   = options->identity_pubkey.uc[0],
     360          30 :     };
     361          30 :     fd_vote_authorized_voters_treap_ele_insert( vote_state->authorized_voters.treap, voter, vote_state->authorized_voters.pool );
     362             : 
     363          30 :     REQUIRE( !fd_vote_state_versioned_serialize( versioned, vote_state_data, sizeof(vote_state_data) ) );
     364          30 :   }
     365          30 :   FD_SCRATCH_SCOPE_END;
     366             : 
     367             :   /* Create stake account */
     368             : 
     369          30 :   ulong const stake_account_index = genesis->accounts_len++;
     370             : 
     371          30 :   uchar stake_data[ FD_STAKE_STATE_SZ ] = {0};
     372             : 
     373          30 :   ulong stake_state_min_bal = fd_rent_exempt_minimum_balance( &genesis->rent, FD_STAKE_STATE_SZ   );
     374          30 :   ulong vote_min_bal        = fd_rent_exempt_minimum_balance( &genesis->rent, FD_VOTE_STATE_V4_SZ ) +
     375          30 :                               FD_GENESIS_VAT_MINIMUM_LAMPORTS;
     376             : 
     377          30 :   do {
     378          30 :     FD_STORE( fd_stake_state_t, stake_data, ((fd_stake_state_t) {
     379          30 :       .stake_type = FD_STAKE_STATE_STAKE,
     380          30 :       .stake = {
     381          30 :         .meta = {
     382          30 :           .rent_exempt_reserve = stake_state_min_bal,
     383          30 :           .staker              = options->identity_pubkey,
     384          30 :           .withdrawer          = options->identity_pubkey,
     385          30 :         },
     386          30 :         .stake = (fd_stake_t) {
     387          30 :           .delegation = (fd_delegation_t) {
     388          30 :             .voter_pubkey         = options->vote_pubkey,
     389          30 :             .stake                = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
     390          30 :             .activation_epoch     = ULONG_MAX, /* bootstrap stake denoted with ULONG_MAX */
     391          30 :             .deactivation_epoch   = ULONG_MAX,
     392          30 :             .warmup_cooldown_rate = 0.25
     393          30 :           },
     394          30 :           .credits_observed = 0UL
     395          30 :         }
     396          30 :       }
     397          30 :     }) );
     398          30 :   } while(0);
     399             : 
     400             :   /* Read enabled features */
     401             : 
     402          30 :   ulong         feature_cnt = 0UL;
     403          30 :   ulong const   features_sz = FD_FEATURE_ID_CNT * sizeof(fd_pubkey_t);
     404          30 :   REQUIRE( fd_scratch_alloc_is_safe( alignof(fd_pubkey_t), features_sz ) );
     405          30 :   fd_pubkey_t * features =
     406          30 :       fd_scratch_alloc( alignof(fd_pubkey_t), features_sz );
     407             : 
     408          30 :   if( options->features ) {
     409          18 :     for( fd_feature_id_t const * id = fd_feature_iter_init();
     410        5346 :                                      !fd_feature_iter_done( id );
     411        5328 :                                  id = fd_feature_iter_next( id ) ) {
     412        5328 :       if( fd_features_get( options->features, id ) == 0UL )
     413           0 :         features[ feature_cnt++ ] = id->id;
     414        5328 :     }
     415          18 :   }
     416             : 
     417             :   /* Allocate the account table */
     418             : 
     419          30 :   ulong default_funded_cnt = options->fund_initial_accounts;
     420             : 
     421          30 :   ulong default_funded_idx = genesis->accounts_len;
     422          30 :   REQUIRE( !__builtin_add_overflow( genesis->accounts_len, default_funded_cnt, &genesis->accounts_len ) );
     423          27 :   ulong feature_gate_idx = genesis->accounts_len;
     424          27 :   REQUIRE( !__builtin_add_overflow( genesis->accounts_len, feature_cnt, &genesis->accounts_len ) );
     425             : 
     426             :   /* Program account, programdata account, mint, and a fixed number of
     427             :      token accounts per primordial account. */
     428          27 :   int   const token_enabled = !!options->token_program_elf;
     429          27 :   ulong       token_cnt     = 0UL;
     430          27 :   if( token_enabled ) {
     431           3 :     REQUIRE( !__builtin_mul_overflow( default_funded_cnt, FD_GENESIS_TOKEN_ACCOUNTS_PER_ACCOUNT, &token_cnt ) );
     432           3 :     REQUIRE( !__builtin_add_overflow( token_cnt, 3UL, &token_cnt ) );
     433           3 :   }
     434          27 :   ulong token_idx = genesis->accounts_len;
     435          27 :   REQUIRE( !__builtin_add_overflow( genesis->accounts_len, token_cnt, &genesis->accounts_len ) );
     436             : 
     437          27 :   ulong accounts_sz;
     438          27 :   REQUIRE( !__builtin_mul_overflow( genesis->accounts_len, sizeof(fd_genesis_account_pair_t), &accounts_sz ) );
     439          24 :   REQUIRE( fd_scratch_alloc_is_safe( alignof(fd_genesis_account_pair_t), accounts_sz ) );
     440             : 
     441          21 :   genesis->accounts = fd_scratch_alloc( alignof(fd_genesis_account_pair_t),
     442          21 :                                         accounts_sz );
     443          21 :   fd_memset( genesis->accounts, 0,      accounts_sz );
     444             : 
     445          21 :   genesis->accounts[ faucet_account_index ] = faucet_account;
     446          21 :   genesis->accounts[ identity_account_index ] = identity_account;
     447          21 :   genesis->accounts[ stake_account_index ] = (fd_genesis_account_pair_t) {
     448          21 :     .key     = options->stake_pubkey,
     449          21 :     .account = (fd_genesis_account_t) {
     450          21 :       .lamports   = fd_ulong_max( stake_state_min_bal, options->vote_account_stake ),
     451          21 :       .data_len   = FD_STAKE_STATE_SZ,
     452          21 :       .data       = stake_data,
     453          21 :       .owner      = fd_solana_stake_program_id
     454          21 :     }
     455          21 :   };
     456          21 :   genesis->accounts[ vote_account_index ] = (fd_genesis_account_pair_t) {
     457          21 :     .key     = options->vote_pubkey,
     458          21 :     .account = (fd_genesis_account_t) {
     459          21 :       .lamports   = vote_min_bal,
     460          21 :       .data_len   = FD_VOTE_STATE_V4_SZ,
     461          21 :       .data       = vote_state_data,
     462          21 :       .owner      = fd_solana_vote_program_id
     463          21 :     }
     464          21 :   };
     465             : 
     466             :   /* Set up primordial accounts */
     467             : 
     468          21 :   ulong default_funded_balance = options->fund_initial_amount_lamports;
     469         213 :   for( ulong j=0UL; j<default_funded_cnt; j++ ) {
     470         192 :     fd_genesis_account_pair_t * pair = &genesis->accounts[ default_funded_idx+j ];
     471             : 
     472         192 :     uchar privkey[ 32 ] = {0};
     473         192 :     FD_STORE( ulong, privkey, j );
     474         192 :     fd_sha512_t sha[1];
     475         192 :     fd_ed25519_public_from_private( pair->key.key, privkey, sha );
     476             : 
     477         192 :     pair->account = (fd_genesis_account_t) {
     478         192 :       .lamports   = default_funded_balance,
     479         192 :       .data_len   = 0UL,
     480         192 :       .owner      = fd_solana_system_program_id
     481         192 :     };
     482         192 :   }
     483             : 
     484          21 : #define FEATURE_ENABLED_SZ 9UL
     485          21 :   static const uchar feature_enabled_data[ FEATURE_ENABLED_SZ ] = { 1, 0, 0, 0, 0, 0, 0, 0, 0 };
     486          21 :   ulong default_feature_enabled_balance = fd_rent_exempt_minimum_balance( &genesis->rent, FEATURE_ENABLED_SZ );
     487             : 
     488             :   /* Set up feature gate accounts */
     489          21 :   for( ulong j=0UL; j<feature_cnt; j++ ) {
     490           0 :     fd_genesis_account_pair_t * pair = &genesis->accounts[ feature_gate_idx+j ];
     491             : 
     492           0 :     pair->key     = features[ j ];
     493           0 :     pair->account = (fd_genesis_account_t) {
     494           0 :       .lamports   = default_feature_enabled_balance,
     495           0 :       .data_len   = FEATURE_ENABLED_SZ,
     496           0 :       .data       = (uchar *)feature_enabled_data,
     497           0 :       .owner      = fd_solana_feature_program_id
     498           0 :     };
     499           0 :   }
     500          21 : #undef FEATURE_ENABLED_SZ
     501             : 
     502             :   /* Deploy an SPL Token compatible program and set up the token
     503             :      accounts */
     504             : 
     505          21 :   if( token_enabled ) {
     506           3 :     ulong const elf_sz             = options->token_program_elf_sz;
     507           3 :     ulong const token_cnt_per_owner = FD_GENESIS_TOKEN_ACCOUNTS_PER_ACCOUNT;
     508             : 
     509             :     /* Program account: UpgradeableLoaderState::Program { programdata_address } */
     510             : 
     511           3 :     fd_pubkey_t programdata_addr[1];
     512           3 :     uchar const * seed  = fd_solana_spl_token_id.uc;
     513           3 :     ulong const   seedsz = sizeof(fd_pubkey_t);
     514           3 :     uchar         bump;
     515           3 :     uint          custom_err;
     516           3 :     REQUIRE( FD_PUBKEY_SUCCESS==fd_pubkey_find_program_address( &fd_solana_bpf_loader_upgradeable_program_id,
     517           3 :                                                                 1UL, &seed, &seedsz,
     518           3 :                                                                 programdata_addr, &bump, &custom_err ) );
     519             : 
     520           3 :     REQUIRE( fd_scratch_alloc_is_safe( 1UL, SIZE_OF_PROGRAM ) );
     521           3 :     uchar * program_data = fd_scratch_alloc( 1UL, SIZE_OF_PROGRAM );
     522           3 :     fd_memset( program_data, 0, SIZE_OF_PROGRAM );
     523           3 :     FD_STORE( uint, program_data, 2U ); /* UpgradeableLoaderState::Program */
     524           3 :     fd_memcpy( program_data+4UL, programdata_addr->uc, sizeof(fd_pubkey_t) );
     525             : 
     526           3 :     genesis->accounts[ token_idx++ ] = (fd_genesis_account_pair_t) {
     527           3 :       .key     = fd_solana_spl_token_id,
     528           3 :       .account = (fd_genesis_account_t) {
     529           3 :         .lamports   = fd_rent_exempt_minimum_balance( &genesis->rent, SIZE_OF_PROGRAM ),
     530           3 :         .data_len   = SIZE_OF_PROGRAM,
     531           3 :         .data       = program_data,
     532           3 :         .owner      = fd_solana_bpf_loader_upgradeable_program_id,
     533           3 :         .executable = 1
     534           3 :       }
     535           3 :     };
     536             : 
     537             :     /* ProgramData account: UpgradeableLoaderState::ProgramData {
     538             :        slot, upgrade_authority_address } followed by the ELF.  The
     539             :        metadata is always padded out to the size of a Some(pubkey)
     540             :        authority, matching Agave. */
     541             : 
     542           3 :     ulong programdata_sz;
     543           3 :     REQUIRE( !__builtin_add_overflow( elf_sz, PROGRAMDATA_METADATA_SIZE, &programdata_sz ) );
     544           3 :     REQUIRE( fd_scratch_alloc_is_safe( 1UL, programdata_sz ) );
     545           3 :     uchar * programdata = fd_scratch_alloc( 1UL, programdata_sz );
     546           3 :     fd_memset( programdata, 0, PROGRAMDATA_METADATA_SIZE );
     547           3 :     FD_STORE( uint, programdata, 3U ); /* UpgradeableLoaderState::ProgramData */
     548           3 :     fd_memcpy( programdata+PROGRAMDATA_METADATA_SIZE, options->token_program_elf, elf_sz );
     549             : 
     550           3 :     genesis->accounts[ token_idx++ ] = (fd_genesis_account_pair_t) {
     551           3 :       .key     = *programdata_addr,
     552           3 :       .account = (fd_genesis_account_t) {
     553           3 :         .lamports   = fd_rent_exempt_minimum_balance( &genesis->rent, programdata_sz ),
     554           3 :         .data_len   = programdata_sz,
     555           3 :         .data       = programdata,
     556           3 :         .owner      = fd_solana_bpf_loader_upgradeable_program_id
     557           3 :       }
     558           3 :     };
     559             : 
     560             :     /* Mint with no mint or freeze authority.  Supply does not need to
     561             :        be consistent with the token accounts for transfers to work, but
     562             :        keep it honest anyway. */
     563             : 
     564           3 :     fd_pubkey_t mint_addr[1];
     565           3 :     fd_genesis_token_mint_address( mint_addr );
     566             : 
     567           3 :     REQUIRE( fd_scratch_alloc_is_safe( 1UL, SPL_TOKEN_MINT_SZ ) );
     568           3 :     uchar * mint_data = fd_scratch_alloc( 1UL, SPL_TOKEN_MINT_SZ );
     569           3 :     fd_memset( mint_data, 0, SPL_TOKEN_MINT_SZ );
     570           3 :     ulong token_account_cnt;
     571           3 :     ulong token_supply;
     572           3 :     REQUIRE( !__builtin_mul_overflow( token_cnt_per_owner, default_funded_cnt, &token_account_cnt ) );
     573           3 :     REQUIRE( !__builtin_mul_overflow( token_account_cnt, FD_GENESIS_TOKEN_AMOUNT, &token_supply ) );
     574           3 :     FD_STORE( ulong, mint_data+36UL, token_supply ); /* supply */
     575           3 :     mint_data[ 45 ] = 1; /* is_initialized */
     576             : 
     577           3 :     genesis->accounts[ token_idx++ ] = (fd_genesis_account_pair_t) {
     578           3 :       .key     = *mint_addr,
     579           3 :       .account = (fd_genesis_account_t) {
     580           3 :         .lamports   = fd_rent_exempt_minimum_balance( &genesis->rent, SPL_TOKEN_MINT_SZ ),
     581           3 :         .data_len   = SPL_TOKEN_MINT_SZ,
     582           3 :         .data       = mint_data,
     583           3 :         .owner      = fd_solana_spl_token_id
     584           3 :       }
     585           3 :     };
     586             : 
     587             :     /* Initialized token accounts owned by each primordial account, all
     588             :        holding the same balance of the mint above. */
     589             : 
     590           3 :     ulong token_accounts_sz;
     591           3 :     REQUIRE( !__builtin_mul_overflow( token_account_cnt, SPL_TOKEN_ACCOUNT_SZ, &token_accounts_sz ) );
     592           3 :     REQUIRE( fd_scratch_alloc_is_safe( 1UL, token_accounts_sz ) );
     593           3 :     uchar * token_accounts = fd_scratch_alloc( 1UL, token_accounts_sz );
     594           3 :     fd_memset( token_accounts, 0, token_accounts_sz );
     595             : 
     596           3 :     ulong const token_min_bal = fd_rent_exempt_minimum_balance( &genesis->rent, SPL_TOKEN_ACCOUNT_SZ );
     597          51 :     for( ulong j=0UL; j<default_funded_cnt; j++ ) {
     598         144 :       for( ulong k=0UL; k<token_cnt_per_owner; k++ ) {
     599          96 :         uchar * data = token_accounts + (token_cnt_per_owner*j+k)*SPL_TOKEN_ACCOUNT_SZ;
     600          96 :         fd_memcpy( data,      mint_addr->uc,                                    sizeof(fd_pubkey_t) );
     601          96 :         fd_memcpy( data+32UL, genesis->accounts[ default_funded_idx+j ].key.uc, sizeof(fd_pubkey_t) );
     602          96 :         FD_STORE( ulong, data+64UL, FD_GENESIS_TOKEN_AMOUNT ); /* amount */
     603          96 :         data[ 108 ] = 1; /* state = Initialized */
     604             : 
     605          96 :         fd_pubkey_t key[1];
     606          96 :         fd_genesis_token_account_address( key, j, k );
     607          96 :         genesis->accounts[ token_idx++ ] = (fd_genesis_account_pair_t) {
     608          96 :           .key     = *key,
     609          96 :           .account = (fd_genesis_account_t) {
     610          96 :             .lamports   = token_min_bal,
     611          96 :             .data_len   = SPL_TOKEN_ACCOUNT_SZ,
     612          96 :             .data       = data,
     613          96 :             .owner      = fd_solana_spl_token_id
     614          96 :           }
     615          96 :         };
     616          96 :       }
     617          48 :     }
     618             : 
     619           3 :     REQUIRE( token_idx==genesis->accounts_len );
     620           3 :   }
     621             : 
     622             :   /* Sort and check for duplicates */
     623             : 
     624          21 :   sort_acct_inplace( genesis->accounts, genesis->accounts_len );
     625             : 
     626         381 :   for( ulong j=1UL; j < genesis->accounts_len; j++ ) {
     627         360 :     if( 0==memcmp( genesis->accounts[j-1].key.ul, genesis->accounts[j].key.ul, sizeof(fd_pubkey_t) ) ) {
     628           0 :       char dup_cstr[ FD_BASE58_ENCODED_32_SZ ];
     629           0 :       fd_base58_encode_32( genesis->accounts[j].key.uc, NULL, dup_cstr );
     630           0 :       FD_LOG_WARNING(( "Account %s is duplicate", dup_cstr ));
     631           0 :       return 0UL;
     632           0 :     }
     633         360 :   }
     634             : 
     635             :   /* Serialize bincode blob */
     636             : 
     637          21 :   ulong encoded_sz = genesis_encode( genesis, (uchar *)buf, bufsz );
     638          21 :   if( FD_UNLIKELY( !encoded_sz ) ) {
     639           6 :     FD_LOG_WARNING(( "Failed to encode genesis blob (bufsz=%lu)", bufsz ));
     640           6 :     return 0UL;
     641           6 :   }
     642          15 :   return encoded_sz;
     643             : 
     644          21 : # undef REQUIRE
     645          21 : }
     646             : 
     647             : ulong
     648             : fd_genesis_create( void *                       buf,
     649             :                    ulong                        bufsz,
     650          30 :                    fd_genesis_options_t const * options ) {
     651          30 :   fd_scratch_push();
     652          30 :   ulong ret = genesis_create( buf, bufsz, options );
     653          30 :   fd_scratch_pop();
     654          30 :   return ret;
     655          30 : }

Generated by: LCOV version 1.14