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

Generated by: LCOV version 1.14