LCOV - code coverage report
Current view: top level - flamenco/runtime/tests - fd_svm_mini.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 626 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 20 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_svm_mini.h"
       3             : #include "../fd_runtime_stack_tmpl.h"
       4             : #include "../../progcache/fd_progcache_admin.h"
       5             : #include "../../progcache/fd_progcache_user.h"
       6             : #include "../../runtime/fd_bank.h"
       7             : #include "../../runtime/program/fd_builtin_programs.h"
       8             : #include "../../runtime/fd_system_ids.h"
       9             : #include "../../runtime/fd_runtime_const.h"
      10             : #include "../../log_collector/fd_log_collector.h"
      11             : #include "../../runtime/fd_runtime.h"
      12             : #include "../../runtime/sysvar/fd_sysvar_cache.h"
      13             : #include "../../runtime/sysvar/fd_sysvar_rent.h"
      14             : #include "../../runtime/sysvar/fd_sysvar_epoch_schedule.h"
      15             : #include "../../runtime/sysvar/fd_sysvar_slot_history.h"
      16             : #include "../../runtime/program/fd_vote_program.h"
      17             : #include "../../runtime/program/vote/fd_vote_codec_tmpl.h"
      18             : #include "../../stakes/fd_stake_types.h"
      19             : #include "../../stakes/fd_stake_delegations.h"
      20             : #include "../../stakes/fd_vote_stakes.h"
      21             : #include "../../leaders/fd_leaders.h"
      22             : #include <errno.h>
      23             : #include <stdlib.h>
      24             : #include <sys/mman.h>
      25             : #include <unistd.h>
      26             : 
      27           0 : #define TEST_CACHE_MIN_RESERVED FD_ACCDB_CACHE_MIN_RESERVED_TXN
      28           0 : #define TEST_CACHE_FOOTPRINT    (4UL<<30UL)
      29           0 : #define TEST_PARTITION_CNT      (8192UL)
      30           0 : #define TEST_PARTITION_SZ       (1UL<<30UL)
      31           0 : #define TEST_WRITES_PER_SLOT    (8192UL)
      32             : 
      33           0 : #define SENTINEL ((fd_accdb_fork_id_t){ .val = USHORT_MAX })
      34             : 
      35             : 
      36             : static fd_wksp_t *
      37             : fd_wksp_new_lazy( ulong footprint,
      38           0 :                   ulong addl_part_cnt ) {
      39           0 :   footprint = fd_ulong_align_up( footprint, FD_SHMEM_NORMAL_PAGE_SZ );
      40           0 :   void * mem = mmap( NULL, footprint, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 );
      41           0 :   if( FD_UNLIKELY( mem==MAP_FAILED ) ) {
      42           0 :     FD_LOG_ERR(( "mmap(NULL,%lu KiB,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS) failed (%i-%s)",
      43           0 :                  footprint>>10, errno, fd_io_strerror( errno ) ));
      44           0 :   }
      45             : 
      46           0 :   ulong part_max = fd_wksp_part_max_est( footprint, 64UL<<10 );
      47           0 :   FD_TEST( part_max );
      48           0 :   part_max += addl_part_cnt;
      49           0 :   ulong data_max = fd_wksp_data_max_est( footprint, part_max );
      50           0 :   FD_TEST( data_max );
      51           0 :   fd_wksp_t * wksp = fd_wksp_join( fd_wksp_new( mem, "wksp", 1U, part_max, data_max ) );
      52           0 :   FD_TEST( wksp );
      53             : 
      54           0 :   FD_TEST( 0==fd_shmem_join_anonymous( "wksp", FD_SHMEM_JOIN_MODE_READ_WRITE, wksp, mem, FD_SHMEM_NORMAL_PAGE_SZ, footprint>>FD_SHMEM_NORMAL_LG_PAGE_SZ ) );
      55           0 :   return wksp;
      56           0 : }
      57             : 
      58             : fd_svm_mini_t *
      59             : fd_svm_test_boot( int *    pargc,
      60             :                   char *** pargv,
      61           0 :                   fd_svm_mini_limits_t const * limits ) {
      62             : 
      63           0 :   fd_boot( pargc, pargv );
      64             : 
      65           0 :   int spill_fd = memfd_create( "svm_mini_stakedel_spill", 0 );
      66           0 :   FD_TEST( spill_fd>=0 );
      67           0 :   if( spill_fd!=FD_STAKE_DELEGATIONS_FD ) {
      68           0 :     FD_TEST( dup2( spill_fd, FD_STAKE_DELEGATIONS_FD )==FD_STAKE_DELEGATIONS_FD );
      69           0 :     FD_TEST( !close( spill_fd ) );
      70           0 :   }
      71             : 
      72           0 :   int epoch_credits_fd = memfd_create( "svm_mini_epoch_credits_spill", 0 );
      73           0 :   FD_TEST( epoch_credits_fd>=0 );
      74           0 :   if( epoch_credits_fd!=FD_EPOCH_CREDITS_FD ) {
      75           0 :     FD_TEST( dup2( epoch_credits_fd, FD_EPOCH_CREDITS_FD )==FD_EPOCH_CREDITS_FD );
      76           0 :     FD_TEST( !close( epoch_credits_fd ) );
      77           0 :   }
      78             : 
      79           0 :   int cost_tracker_fd = memfd_create( "svm_mini_cost_tracker_spill", 0 );
      80           0 :   FD_TEST( cost_tracker_fd>=0 );
      81           0 :   if( cost_tracker_fd!=FD_COST_TRACKER_FD ) {
      82           0 :     FD_TEST( dup2( cost_tracker_fd, FD_COST_TRACKER_FD )==FD_COST_TRACKER_FD );
      83           0 :     FD_TEST( !close( cost_tracker_fd ) );
      84           0 :   }
      85             : 
      86           0 :   int vote_stakes_fd = memfd_create( "svm_mini_vote_stakes_spill", 0 );
      87           0 :   FD_TEST( vote_stakes_fd>=0 );
      88           0 :   if( vote_stakes_fd!=FD_VOTE_STAKES_FD ) {
      89           0 :     FD_TEST( dup2( vote_stakes_fd, FD_VOTE_STAKES_FD )==FD_VOTE_STAKES_FD );
      90           0 :     FD_TEST( !close( vote_stakes_fd ) );
      91           0 :   }
      92             : 
      93           0 :   char const * page_sz_cstr = fd_env_strip_cmdline_cstr ( pargc, pargv, "--page-sz",  NULL, NULL            );
      94           0 :   ulong        page_cnt     = fd_env_strip_cmdline_ulong( pargc, pargv, "--page-cnt", NULL, 0UL             );
      95           0 :   char const * wksp_name    = fd_env_strip_cmdline_cstr ( pargc, pargv, "--wksp",     NULL, NULL            );
      96           0 :   ulong        near_cpu     = fd_env_strip_cmdline_ulong( pargc, pargv, "--near-cpu", NULL, fd_log_cpu_id() );
      97           0 :   ulong        wksp_tag     = limits->wksp_tag ? limits->wksp_tag : 42UL;
      98             : 
      99           0 :   ulong data_max = fd_svm_mini_wksp_data_max( limits );
     100           0 :   ulong part_max = fd_wksp_part_max_est( data_max, 64UL<<10 );
     101           0 :   ulong wksp_sz  = fd_wksp_footprint( part_max, data_max );
     102           0 :   fd_wksp_t * wksp = NULL;
     103           0 :   if( wksp_name && !!wksp_name[0] ) {
     104           0 :     FD_LOG_NOTICE(( "Attaching to --wksp %s", wksp_name ));
     105           0 :     wksp = fd_wksp_attach( wksp_name );
     106           0 :   } else if( page_sz_cstr ) {
     107           0 :     ulong page_sz = fd_cstr_to_shmem_page_sz( page_sz_cstr );
     108           0 :     if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "Invalid --page-sz %s", page_sz_cstr ));
     109           0 :     if( FD_UNLIKELY( page_sz*page_cnt < wksp_sz ) ) {
     110           0 :       FD_LOG_WARNING(( "--page-sz %s * --page-cnt %lu is smaller than required wksp_sz %lu KiB; falling back to lazy anonymous memory",
     111           0 :                        page_sz_cstr, page_cnt, wksp_sz>>10 ));
     112           0 :       goto fallback;
     113           0 :     }
     114           0 :     FD_LOG_NOTICE(( "--wksp not specified, using anonymous pinned shmem" ));
     115           0 :     wksp = fd_wksp_new_anonymous( page_sz, page_cnt, near_cpu, "wksp", wksp_tag );
     116           0 :   } else {
     117           0 : fallback:
     118           0 :     FD_LOG_NOTICE(( "--page-sz not specified, using lazy paged memory" ));
     119           0 :     wksp = fd_wksp_new_lazy( wksp_sz, limits->wksp_addl_part_cnt );
     120           0 :   }
     121           0 :   if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "Unable to attach to wksp" ));
     122             : 
     123           0 :   return fd_svm_mini_create( wksp, limits );
     124           0 : }
     125             : 
     126             : void
     127           0 : fd_svm_test_halt( fd_svm_mini_t * mini ) {
     128           0 :   fd_svm_mini_destroy( mini );
     129           0 :   fd_halt();
     130           0 : }
     131             : 
     132             : ulong
     133           0 : fd_svm_mini_wksp_data_max( fd_svm_mini_limits_t const * limits ) {
     134           0 :   ulong txn_max     = limits->max_live_slots;
     135           0 :   ulong joiner_cnt  = fd_ulong_max( limits->accdb_joiner_cnt, 1UL );
     136             : 
     137           0 :   ulong pcache_sz         = fd_progcache_shmem_footprint( txn_max, fd_progcache_shmem_min_sz( txn_max ) );
     138           0 :   ulong txncache_shmem_sz = fd_txncache_shmem_footprint( txn_max, limits->max_txn_per_slot );
     139           0 :   ulong txncache_sz       = fd_txncache_footprint( txn_max );
     140           0 :   ulong banks_sz          = fd_banks_footprint( txn_max, 8UL, limits->max_stake_accounts, limits->max_vote_accounts );
     141           0 :   ulong runtime_stack_sz  = fd_runtime_stack_footprint( limits->max_vote_accounts, limits->max_vote_accounts, limits->max_stake_accounts );
     142             : 
     143           0 :   ulong accdb_shmem_sz = fd_accdb_shmem_footprint( limits->max_accounts, limits->max_live_slots,
     144           0 :                                                     TEST_WRITES_PER_SLOT, TEST_PARTITION_CNT,
     145           0 :                                                     TEST_CACHE_FOOTPRINT, TEST_CACHE_MIN_RESERVED, joiner_cnt, 0UL );
     146           0 :   ulong accdb_join_sz  = fd_accdb_footprint( limits->max_live_slots, 1 );
     147             : 
     148           0 : # define WKSP_ALLOC(a,s) fd_ulong_align_up( fd_ulong_max((s),1UL), fd_ulong_max((a),FD_WKSP_ALIGN_DEFAULT) )
     149           0 :   ulong sz = 0UL;
     150           0 :   sz += WKSP_ALLOC( alignof(fd_svm_mini_t),     sizeof(fd_svm_mini_t)            );
     151           0 :   sz += WKSP_ALLOC( fd_accdb_shmem_align(),     accdb_shmem_sz                   );
     152           0 :   sz += WKSP_ALLOC( fd_accdb_align(),           accdb_join_sz                    );
     153           0 :   sz += WKSP_ALLOC( fd_progcache_shmem_align(), pcache_sz                        );
     154           0 :   sz += WKSP_ALLOC( FD_PROGCACHE_SCRATCH_ALIGN, FD_PROGCACHE_SCRATCH_FOOTPRINT   );
     155           0 :   sz += WKSP_ALLOC( fd_txncache_shmem_align(),  txncache_shmem_sz                );
     156           0 :   sz += WKSP_ALLOC( fd_txncache_align(),        txncache_sz                      );
     157           0 :   sz += WKSP_ALLOC( fd_banks_align(),           banks_sz                         );
     158           0 :   sz += WKSP_ALLOC( alignof(fd_runtime_t),      sizeof(fd_runtime_t)             );
     159           0 :   sz += WKSP_ALLOC( fd_runtime_stack_align(),   runtime_stack_sz                 );
     160           0 :   sz += WKSP_ALLOC( fd_vm_align(),              fd_vm_footprint()                );
     161           0 :   sz += WKSP_ALLOC( 16UL,                       limits->wksp_addl_sz             );
     162           0 : # undef WKSP_ALLOC
     163             : 
     164           0 :   return sz;
     165           0 : }
     166             : 
     167             : fd_svm_mini_t *
     168             : fd_svm_mini_create( fd_wksp_t *                  wksp,
     169           0 :                     fd_svm_mini_limits_t const * limits ) {
     170             : 
     171           0 :   ulong const wksp_tag    = limits->wksp_tag ? limits->wksp_tag : 42UL;
     172           0 :   ulong const txn_max     = limits->max_live_slots;
     173           0 :   ulong const joiner_cnt  = fd_ulong_max( limits->accdb_joiner_cnt, 1UL );
     174             : 
     175           0 :   ulong progcache_sz      = fd_progcache_shmem_min_sz( txn_max );
     176           0 :   ulong pcache_sz         = fd_progcache_shmem_footprint( txn_max, progcache_sz );
     177           0 :   ulong txncache_shmem_sz = fd_txncache_shmem_footprint( txn_max, limits->max_txn_per_slot );
     178           0 :   ulong txncache_sz       = fd_txncache_footprint( txn_max );
     179           0 :   ulong banks_sz         = fd_banks_footprint( txn_max, 8UL, limits->max_stake_accounts, limits->max_vote_accounts );
     180           0 :   ulong runtime_stack_sz = fd_runtime_stack_footprint( limits->max_vote_accounts, limits->max_vote_accounts, limits->max_stake_accounts );
     181             : 
     182           0 :   ulong accdb_shmem_sz = fd_accdb_shmem_footprint( limits->max_accounts, limits->max_live_slots,
     183           0 :                                                     TEST_WRITES_PER_SLOT, TEST_PARTITION_CNT,
     184           0 :                                                     TEST_CACHE_FOOTPRINT, TEST_CACHE_MIN_RESERVED, joiner_cnt, 0UL );
     185           0 :   ulong accdb_join_sz  = fd_accdb_footprint( limits->max_live_slots, 1 );
     186             : 
     187             :   /* Allocate objects */
     188             : 
     189           0 :   fd_svm_mini_t * mini;          FD_TEST( (mini           = fd_wksp_alloc_laddr( wksp, alignof(fd_svm_mini_t),     sizeof(fd_svm_mini_t),          wksp_tag )) );
     190           0 :   void *          accdb_shmem;   FD_TEST( (accdb_shmem    = fd_wksp_alloc_laddr( wksp, fd_accdb_shmem_align(),     accdb_shmem_sz,                 wksp_tag )) );
     191           0 :   void *          accdb_join;    FD_TEST( (accdb_join     = fd_wksp_alloc_laddr( wksp, fd_accdb_align(),           accdb_join_sz,                  wksp_tag )) );
     192           0 :   void *          pcache_mem;    FD_TEST( (pcache_mem     = fd_wksp_alloc_laddr( wksp, fd_progcache_shmem_align(), pcache_sz,                      wksp_tag )) );
     193           0 :   uchar *         scratch;       FD_TEST( (scratch        = fd_wksp_alloc_laddr( wksp, FD_PROGCACHE_SCRATCH_ALIGN, FD_PROGCACHE_SCRATCH_FOOTPRINT, wksp_tag )) );
     194           0 :   void *          txncache_shmem; FD_TEST( (txncache_shmem = fd_wksp_alloc_laddr( wksp, fd_txncache_shmem_align(), txncache_shmem_sz,              wksp_tag )) );
     195           0 :   void *          txncache_mem;   FD_TEST( (txncache_mem   = fd_wksp_alloc_laddr( wksp, fd_txncache_align(),       txncache_sz,                    wksp_tag )) );
     196           0 :   void *          banks_mem;     FD_TEST( (banks_mem      = fd_wksp_alloc_laddr( wksp, fd_banks_align(),           banks_sz,                       wksp_tag )) );
     197           0 :   fd_runtime_t *  runtime;       FD_TEST( (runtime        = fd_wksp_alloc_laddr( wksp, alignof(fd_runtime_t),      sizeof(fd_runtime_t),           wksp_tag )) );
     198           0 :   void *          rstack_mem;    FD_TEST( (rstack_mem     = fd_wksp_alloc_laddr( wksp, fd_runtime_stack_align(),   runtime_stack_sz,               wksp_tag )) );
     199           0 :   void *          vm_mem;        FD_TEST( (vm_mem         = fd_wksp_alloc_laddr( wksp, fd_vm_align(),              fd_vm_footprint(),              wksp_tag )) );
     200             : 
     201             :   /* Initialize objects */
     202             : 
     203           0 :   fd_memset( mini, 0, sizeof(fd_svm_mini_t) );
     204           0 :   mini->wksp = wksp;
     205             : 
     206           0 :   fd_txncache_shmem_t * shtxncache = fd_txncache_shmem_join( fd_txncache_shmem_new( txncache_shmem, txn_max, limits->max_txn_per_slot, 0UL ) );
     207           0 :   if( FD_UNLIKELY( !shtxncache ) ) FD_LOG_ERR(( "fd_txncache_shmem_new failed" ));
     208             : 
     209             :   /* Create accdb backed by memfd */
     210           0 :   int accdb_fd = memfd_create( "accdb_test", 0 );
     211           0 :   if( FD_UNLIKELY( accdb_fd<0 ) ) FD_LOG_ERR(( "memfd_create failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     212             : 
     213           0 :   fd_accdb_shmem_t * shmem = fd_accdb_shmem_join(
     214           0 :       fd_accdb_shmem_new( accdb_shmem, limits->max_accounts, limits->max_live_slots,
     215           0 :                           TEST_WRITES_PER_SLOT, TEST_PARTITION_CNT,
     216           0 :                           TEST_PARTITION_SZ, TEST_CACHE_FOOTPRINT, TEST_CACHE_MIN_RESERVED, 0, 42UL, joiner_cnt, 0UL ) );
     217           0 :   FD_TEST( shmem );
     218           0 :   fd_accdb_t * accdb = fd_accdb_join( fd_accdb_new( accdb_join, shmem, accdb_fd, 0UL, NULL, NULL, 0UL, 1 ) );
     219           0 :   FD_TEST( accdb );
     220             : 
     221             :   /* Save accdb init params for reset */
     222           0 :   mini->accdb_fd             = accdb_fd;
     223           0 :   mini->accdb_shmem_mem      = accdb_shmem;
     224           0 :   mini->accdb_join_mem       = accdb_join;
     225           0 :   mini->accdb_max_accounts   = limits->max_accounts;
     226           0 :   mini->accdb_max_live_slots = limits->max_live_slots;
     227           0 :   mini->accdb_joiner_cnt     = joiner_cnt;
     228             : 
     229           0 :   void * shpcache = fd_progcache_shmem_new( pcache_mem, wksp_tag, 1UL, txn_max, progcache_sz );
     230           0 :   if( FD_UNLIKELY( !shpcache ) ) FD_LOG_ERR(( "fd_progcache_shmem_new failed" ));
     231             : 
     232           0 :   FD_TEST( fd_progcache_join( mini->progcache, pcache_mem, scratch, FD_PROGCACHE_SCRATCH_FOOTPRINT ) );
     233           0 :   mini->txncache_shmem = shtxncache;
     234           0 :   FD_TEST( (mini->txncache = fd_txncache_join( fd_txncache_new( txncache_mem, shtxncache ) )) );
     235             : 
     236           0 :   mini->banks = fd_banks_join( fd_banks_new( banks_mem, FD_STAKE_DELEGATIONS_FD, txn_max,
     237           0 :                                8UL,
     238           0 :                                limits->max_stake_accounts, limits->max_disk_records,
     239           0 :                                limits->max_vote_accounts, 0, 8888UL ) );
     240           0 :   FD_TEST( mini->banks );
     241             : 
     242           0 :   mini->runtime = runtime;
     243             : 
     244           0 :   runtime->accdb        = accdb;
     245           0 :   runtime->status_cache = mini->txncache;
     246           0 :   runtime->progcache    = mini->progcache;
     247             : 
     248           0 :   runtime->instr.stack_sz          = 0;
     249           0 :   runtime->instr.trace_length      = 0;
     250           0 :   runtime->instr.current_idx       = 0;
     251           0 :   runtime->accounts.executable_cnt = 0;
     252           0 :   runtime->accounts.account_cnt    = 0;
     253           0 :   fd_memset( &runtime->log,     0, sizeof(runtime->log)     );
     254           0 :   fd_memset( &runtime->metrics, 0, sizeof(runtime->metrics) );
     255           0 :   fd_memset( &runtime->fuzz,    0, sizeof(runtime->fuzz)    );
     256             : 
     257           0 :   mini->runtime_stack = fd_runtime_stack_join( fd_runtime_stack_new( rstack_mem,
     258           0 :       limits->max_vote_accounts, limits->max_vote_accounts, limits->max_stake_accounts, 42UL ) );
     259           0 :   FD_TEST( mini->runtime_stack );
     260             : 
     261           0 :   fd_log_collector_init( mini->log_collector, 1 );
     262           0 :   runtime->log.enable_log_collector = 0;
     263           0 :   runtime->log.log_collector        = mini->log_collector;
     264             : 
     265           0 :   fd_features_disable_all( mini->features );
     266           0 :   fd_features_enable_cleaned_up( mini->features );
     267             : 
     268           0 :   FD_TEST( fd_sha256_join( fd_sha256_new( mini->sha256 ) ) );
     269             : 
     270           0 :   fd_memset( vm_mem, 0, fd_vm_footprint() );
     271           0 :   mini->vm = fd_vm_join( fd_vm_new( vm_mem ) );
     272           0 :   FD_TEST( mini->vm );
     273             : 
     274           0 :   return mini;
     275           0 : }
     276             : 
     277             : void
     278           0 : fd_svm_mini_destroy( fd_svm_mini_t * mini ) {
     279           0 :   if( FD_UNLIKELY( !mini ) ) return;
     280             : 
     281           0 :   if( mini->vm ) fd_wksp_free_laddr( fd_vm_delete( fd_vm_leave( mini->vm ) ) );
     282           0 :   if( mini->runtime_stack ) fd_wksp_free_laddr( mini->runtime_stack );
     283           0 :   if( mini->runtime ) fd_wksp_free_laddr( mini->runtime );
     284           0 :   if( mini->banks ) fd_wksp_free_laddr( mini->banks );
     285             : 
     286           0 :   if( mini->txncache ) {
     287           0 :     fd_wksp_free_laddr( mini->txncache );
     288           0 :     if( mini->txncache_shmem ) fd_wksp_free_laddr( mini->txncache_shmem );
     289           0 :   }
     290             : 
     291           0 :   uchar * scratch = mini->progcache->scratch;
     292           0 :   fd_progcache_shmem_t * shpcache = NULL;
     293           0 :   fd_progcache_leave( mini->progcache, &shpcache );
     294           0 :   if( scratch  ) fd_wksp_free_laddr( scratch );
     295           0 :   if( shpcache ) fd_wksp_free_laddr( fd_progcache_shmem_delete( shpcache ) );
     296             : 
     297             :   /* accdb shmem and join are workspace allocations, freed with mini */
     298             : 
     299           0 :   fd_wksp_free_laddr( mini );
     300           0 : }
     301             : 
     302             : static void
     303           0 : drain_background( fd_accdb_t * accdb ) {
     304           0 :   int charge_busy = 0;
     305           0 :   fd_accdb_background( accdb, &charge_busy );
     306           0 : }
     307             : 
     308             : static void
     309             : fd_svm_mini_init_mock_validators( fd_svm_mini_t *              mini,
     310             :                                   fd_bank_t *                  bank,
     311           0 :                                   fd_svm_mini_params_t const * params ) {
     312             : 
     313           0 :   ulong const N             = params->mock_validator_cnt;
     314           0 :   ulong const uniform_stake = 1000000000UL; /* 1 SOL */
     315           0 :   ulong const vote_min_bal  = fd_rent_exempt_minimum_balance( &bank->f.rent, FD_VOTE_STATE_V4_SZ );
     316           0 :   ulong const stake_min_bal = fd_rent_exempt_minimum_balance( &bank->f.rent, FD_STAKE_STATE_SZ );
     317           0 :   ulong const vote_bal      = fd_ulong_max( vote_min_bal, uniform_stake );
     318             : 
     319           0 :   fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
     320           0 :   ulong              fork_id     = bank->vote_stakes_fork_id;
     321             : 
     322           0 :   fd_stake_delegations_t * stake_delegations = fd_banks_stake_delegations_root_query( mini->banks );
     323             : 
     324           0 :   fd_vote_stake_weight_t * stakes = calloc( N, sizeof(fd_vote_stake_weight_t) );
     325           0 :   FD_TEST( stakes );
     326             : 
     327           0 :   fd_rng_t rng[1];
     328           0 :   fd_rng_join( fd_rng_new( rng, (uint)params->hash_seed, 0UL ) );
     329             : 
     330           0 :   fd_accdb_t *       accdb   = mini->runtime->accdb;
     331           0 :   fd_accdb_fork_id_t root_fk = fd_banks_root( mini->banks )->accdb_fork_id;
     332             : 
     333           0 :   fd_epoch_credits_view_t epoch_credits_view[1];
     334           0 :   FD_TEST( fd_epoch_credits_view_init( epoch_credits_view, fd_bank_epoch_credits( bank ), bank->epoch_credits_fork_id ) );
     335             : 
     336           0 :   for( ulong i=0UL; i<N; i++ ) {
     337             : 
     338             :     /* Generate deterministic pubkeys */
     339             : 
     340           0 :     fd_pubkey_t identity_key, vote_key, stake_key;
     341           0 :     for( ulong j=0UL; j<4UL; j++ ) identity_key.ul[j] = fd_rng_ulong( rng );
     342           0 :     for( ulong j=0UL; j<4UL; j++ ) vote_key.ul[j]     = fd_rng_ulong( rng );
     343           0 :     for( ulong j=0UL; j<4UL; j++ ) stake_key.ul[j]     = fd_rng_ulong( rng );
     344             : 
     345             :     /* Identity account */
     346             : 
     347           0 :     fd_svm_mini_add_lamports_rooted( mini, &identity_key, 500000000000UL /* 500 SOL */ );
     348             : 
     349             :     /* Vote account */
     350             : 
     351           0 :     {
     352           0 :       uchar vote_state_data[ FD_VOTE_STATE_V4_SZ ] = {0};
     353             : 
     354           0 :       fd_vote_state_versioned_t versioned[1];
     355           0 :       FD_TEST( fd_vote_state_versioned_new( versioned, fd_vote_state_versioned_enum_v4 ) );
     356             : 
     357           0 :       fd_vote_state_v4_t * vs               = &versioned->v4;
     358           0 :       vs->node_pubkey                        = identity_key;
     359           0 :       vs->authorized_withdrawer              = identity_key;
     360           0 :       vs->inflation_rewards_collector        = vote_key;
     361           0 :       vs->block_revenue_collector            = identity_key;
     362           0 :       vs->inflation_rewards_commission_bps   = 1234U;
     363           0 :       vs->block_revenue_commission_bps       = 10000U;
     364           0 :       vs->has_bls_pubkey_compressed          = 1U;
     365           0 :       fd_memset( vs->bls_pubkey_compressed, 0xBB, FD_BLS_PUBKEY_COMPRESSED_SZ );
     366             : 
     367           0 :       fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( vs->authorized_voters.pool );
     368           0 :       *ele = (fd_vote_authorized_voter_t){
     369           0 :         .epoch  = 0UL,
     370           0 :         .pubkey = identity_key,
     371           0 :         .prio   = identity_key.uc[0],
     372           0 :       };
     373           0 :       fd_vote_authorized_voters_treap_ele_insert( vs->authorized_voters.treap, ele, vs->authorized_voters.pool );
     374           0 :       FD_TEST( !fd_vote_state_versioned_serialize( versioned, vote_state_data, sizeof(vote_state_data) ) );
     375             : 
     376           0 :       fd_acc_t acc = fd_accdb_write_one( accdb, root_fk, vote_key.uc );
     377           0 :       acc.lamports = vote_bal;
     378           0 :       fd_memcpy( acc.owner, fd_solana_vote_program_id.uc, 32UL );
     379           0 :       fd_memcpy( acc.data, vote_state_data, FD_VOTE_STATE_V4_SZ );
     380           0 :       acc.data_len = FD_VOTE_STATE_V4_SZ;
     381           0 :       acc.commit = 1;
     382           0 :       fd_accdb_unwrite_one( accdb, &acc );
     383           0 :     }
     384             : 
     385             :     /* Stake account */
     386             : 
     387           0 :     {
     388           0 :       uchar stake_data[ FD_STAKE_STATE_SZ ] = {0};
     389           0 :       FD_STORE( fd_stake_state_t, stake_data, ((fd_stake_state_t) {
     390           0 :         .stake_type = FD_STAKE_STATE_STAKE,
     391           0 :         .stake = {
     392           0 :           .meta = {
     393           0 :             .rent_exempt_reserve = stake_min_bal,
     394           0 :             .staker              = identity_key,
     395           0 :             .withdrawer          = identity_key,
     396           0 :           },
     397           0 :           .stake = (fd_stake_t) {
     398           0 :             .delegation = (fd_delegation_t) {
     399           0 :               .voter_pubkey         = vote_key,
     400           0 :               .stake                = uniform_stake,
     401           0 :               .activation_epoch     = ULONG_MAX,
     402           0 :               .deactivation_epoch   = ULONG_MAX,
     403           0 :               .warmup_cooldown_rate = 0.25,
     404           0 :             },
     405           0 :             .credits_observed = 0UL,
     406           0 :           },
     407           0 :         },
     408           0 :       }) );
     409             : 
     410           0 :       fd_acc_t acc = fd_accdb_write_one( accdb, root_fk, stake_key.uc );
     411           0 :       acc.lamports = fd_ulong_max( stake_min_bal, uniform_stake );
     412           0 :       fd_memcpy( acc.owner, fd_solana_stake_program_id.uc, 32UL );
     413           0 :       fd_memcpy( acc.data, stake_data, FD_STAKE_STATE_SZ );
     414           0 :       acc.data_len = FD_STAKE_STATE_SZ;
     415           0 :       acc.commit = 1;
     416           0 :       fd_accdb_unwrite_one( accdb, &acc );
     417           0 :     }
     418             : 
     419             :     /* Populate bank structures */
     420             : 
     421           0 :     uchar const no_bls[ FD_BLS_PUBKEY_COMPRESSED_SZ ] = {0}; /* zeroed means no key is registered */
     422           0 :     fd_pubkey_t const * block_collector = params->mock_block_collector_self ? &vote_key : params->mock_block_collector;
     423           0 :     fd_vote_stakes_snap_insert_t_1( vote_stakes, fork_id, &vote_key, &identity_key, uniform_stake, 1234U, no_bls,
     424           0 :                                     params->mock_block_revenue_commission_bps, 0UL, NULL, block_collector );
     425           0 :     fd_vote_stakes_snap_insert_t_2( vote_stakes, fork_id, &vote_key, &identity_key, uniform_stake, 1234U, no_bls,
     426           0 :                                     params->mock_block_revenue_commission_bps, 0UL, NULL, block_collector );
     427           0 :     fd_vote_stakes_update_state( vote_stakes, fork_id, &vote_key, 0UL, 0L, 1 );
     428             : 
     429           0 :     fd_epoch_credits_t * epoch_credits = &epoch_credits_view->credits[ i ];
     430           0 :     fd_memcpy( epoch_credits->pubkey, &vote_key, sizeof(fd_pubkey_t) );
     431           0 :     epoch_credits->cnt                     = 0UL;
     432           0 :     epoch_credits->base_credits            = 0UL;
     433           0 :     epoch_credits->has_ag_migration_marker = 0;
     434             : 
     435           0 :     fd_stake_delegations_root_update( stake_delegations,
     436           0 :                                       &stake_key, &vote_key,
     437           0 :                                       uniform_stake,
     438           0 :                                       ULONG_MAX,  /* activation_epoch (bootstrap) */
     439           0 :                                       ULONG_MAX,  /* deactivation_epoch */
     440           0 :                                       0UL,        /* credits_observed */
     441           0 :                                       fd_ulong_max( stake_min_bal, uniform_stake ),
     442           0 :                                       (uint)FD_STAKE_STATE_SZ );
     443             : 
     444           0 :     stakes[i] = (fd_vote_stake_weight_t){
     445           0 :       .vote_key = vote_key,
     446           0 :       .id_key   = identity_key,
     447           0 :       .stake    = uniform_stake,
     448           0 :     };
     449           0 :   }
     450           0 :   epoch_credits_view->len = N;
     451           0 :   fd_epoch_credits_view_fini( epoch_credits_view );
     452             : 
     453             :   /* Create leader schedule */
     454             : 
     455           0 :   ulong epoch    = bank->f.epoch;
     456           0 :   ulong slot0    = fd_epoch_slot0( &bank->f.epoch_schedule, epoch );
     457           0 :   ulong slot_cnt = bank->f.epoch_schedule.slots_per_epoch;
     458             : 
     459           0 :   void * leaders_mem = fd_bank_epoch_leaders_modify( bank, epoch );
     460           0 :   FD_TEST( fd_epoch_leaders_join( fd_epoch_leaders_new(
     461           0 :       leaders_mem, epoch, slot0, slot_cnt, N, stakes ) ) );
     462             : 
     463           0 :   fd_rng_delete( fd_rng_leave( rng ) );
     464           0 :   free( stakes );
     465           0 : }
     466             : 
     467             : ulong
     468             : fd_svm_mini_reset( fd_svm_mini_t *        mini,
     469           0 :                    fd_svm_mini_params_t * params ) {
     470             : 
     471           0 :   ulong mock_validator_max = fd_ulong_min( mini->banks->max_vote_accounts, FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS );
     472           0 :   if( FD_UNLIKELY( params->mock_validator_cnt>mock_validator_max ) ) {
     473           0 :     FD_LOG_WARNING(( "mock_validator_cnt %lu exceeds capacity %lu",
     474           0 :                      params->mock_validator_cnt, mock_validator_max ));
     475           0 :     return ULONG_MAX;
     476           0 :   }
     477             : 
     478             :   /* Reset accdb: destroy and recreate shmem + join.
     479             :      The accdb shmem and join memory are wksp allocations that remain
     480             :      valid; we just re-initialize them in place. */
     481           0 :   int accdb_fd = mini->accdb_fd;
     482             : 
     483             :   /* Re-initialize shmem in place */
     484           0 :   fd_accdb_shmem_t * shmem = fd_accdb_shmem_join(
     485           0 :       fd_accdb_shmem_new( mini->accdb_shmem_mem, mini->accdb_max_accounts, mini->accdb_max_live_slots,
     486           0 :                           TEST_WRITES_PER_SLOT, TEST_PARTITION_CNT,
     487           0 :                           TEST_PARTITION_SZ, TEST_CACHE_FOOTPRINT, TEST_CACHE_MIN_RESERVED, 0, 42UL, mini->accdb_joiner_cnt, 0UL ) );
     488           0 :   FD_TEST( shmem );
     489             : 
     490             :   /* Re-truncate memfd */
     491           0 :   FD_TEST( 0==ftruncate( accdb_fd, 0 ) );
     492             : 
     493             :   /* Re-initialize accdb join in place */
     494           0 :   fd_accdb_t * accdb = fd_accdb_join( fd_accdb_new( mini->accdb_join_mem, shmem, accdb_fd, 0UL, NULL, NULL, 0UL, 1 ) );
     495           0 :   FD_TEST( accdb );
     496           0 :   mini->runtime->accdb = accdb;
     497             : 
     498           0 :   fd_progcache_reset( mini->progcache->join );
     499           0 :   fd_txncache_reset( mini->txncache );
     500           0 :   fd_banks_clear   ( mini->banks    );
     501             : 
     502           0 :   fd_bank_t * bank = fd_banks_init_bank( mini->banks );
     503           0 :   FD_TEST( bank );
     504           0 :   ulong bank_idx = bank->idx;
     505             : 
     506           0 :   bank->f.slot = params->root_slot;
     507             : 
     508           0 :   bank->progcache_fork_id = fd_progcache_fork_id_initial();
     509           0 :   bank->txncache_fork_id  = fd_txncache_attach_child( mini->txncache, (fd_txncache_fork_id_t){ USHORT_MAX } );
     510             : 
     511             :   /* Create the root fork in accdb */
     512           0 :   fd_accdb_fork_id_t root_fork_id = fd_accdb_attach_child( accdb, SENTINEL );
     513           0 :   bank->accdb_fork_id        = root_fork_id;
     514           0 :   bank->parent_accdb_fork_id = root_fork_id;
     515             : 
     516           0 :   if( params->clock ) {
     517           0 :     bank->f.slot  = params->clock->slot;
     518           0 :     bank->f.epoch = params->clock->epoch;
     519           0 :   }
     520             : 
     521           0 :   if( params->epoch_schedule ) {
     522           0 :     bank->f.epoch_schedule = *params->epoch_schedule;
     523           0 :   } else {
     524           0 :     bank->f.epoch_schedule = (fd_epoch_schedule_t) {
     525           0 :       .slots_per_epoch             = params->slots_per_epoch,
     526           0 :       .leader_schedule_slot_offset = params->slots_per_epoch,
     527           0 :       .warmup                      = 0,
     528           0 :       .first_normal_epoch          = 0UL,
     529           0 :       .first_normal_slot           = 0UL,
     530           0 :     };
     531           0 :   }
     532             : 
     533             :   /* Default slots_per_year matches Solana mainnet genesis defaults
     534             :      (target_tick_duration=6250000ns, ticks_per_slot=64). */
     535           0 :   bank->f.slot_params                = FD_SLOT_PARAMS_400MS;
     536           0 :   bank->f.slot_params.slots_per_year = SECONDS_PER_YEAR * (1000000000.0 / 6250000.0) / 64.0;
     537           0 :   bank->f.slot_params_default        = bank->f.slot_params;
     538             : 
     539           0 :   if( params->rent ) {
     540           0 :     bank->f.rent = *params->rent;
     541           0 :   } else {
     542           0 :     bank->f.rent = (fd_rent_t) {
     543           0 :       .lamports_per_uint8_year = 3480UL,
     544           0 :       .exemption_threshold     = 2.0,
     545           0 :       .burn_percent            = 50,
     546           0 :     };
     547           0 :   }
     548             : 
     549           0 :   fd_features_disable_all( &bank->f.features );
     550           0 :   fd_features_enable_cleaned_up( &bank->f.features );
     551             : 
     552           0 :   if( params->init_builtins ) {
     553           0 :     fd_builtin_program_t const * builtins = fd_builtins();
     554           0 :     for( ulong i=0UL; i<fd_num_builtins(); i++ ) {
     555           0 :       char const * data = builtins[i].data;
     556           0 :       ulong        sz   = strlen( data );
     557           0 :       fd_acc_t acc = fd_accdb_write_one( accdb, root_fork_id, builtins[i].pubkey->uc );
     558           0 :       acc.lamports = 1UL;
     559           0 :       fd_memcpy( acc.owner, fd_solana_native_loader_id.uc, 32UL );
     560           0 :       if( sz ) fd_memcpy( acc.data, data, sz );
     561           0 :       acc.data_len   = sz;
     562           0 :       acc.executable = 1;
     563           0 :       acc.commit     = 1;
     564           0 :       fd_accdb_unwrite_one( accdb, &acc );
     565           0 :       bank->f.capitalization += acc.lamports;
     566           0 :     }
     567             : 
     568           0 :     fd_pubkey_t const * precompiles[] = {
     569           0 :       &fd_solana_keccak_secp_256k_program_id,
     570           0 :       &fd_solana_ed25519_sig_verify_program_id,
     571           0 :       &fd_solana_secp256r1_program_id,
     572           0 :     };
     573           0 :     for( ulong i=0UL; i<3UL; i++ ) {
     574           0 :       fd_acc_t acc = fd_accdb_write_one( accdb, root_fork_id, precompiles[i]->uc );
     575           0 :       acc.lamports   = 1UL;
     576           0 :       fd_memcpy( acc.owner, fd_solana_native_loader_id.uc, 32UL );
     577           0 :       acc.data_len   = 0;
     578           0 :       acc.executable = 1;
     579           0 :       acc.commit     = 1;
     580           0 :       fd_accdb_unwrite_one( accdb, &acc );
     581           0 :       bank->f.capitalization += acc.lamports;
     582           0 :     }
     583           0 :   }
     584             : 
     585           0 :   if( params->init_feature_accounts ) {
     586           0 :     for( fd_feature_id_t const * id = fd_feature_iter_init();
     587           0 :          !fd_feature_iter_done( id );
     588           0 :          id = fd_feature_iter_next( id ) ) {
     589           0 :       ulong activation_slot = fd_features_get( &bank->f.features, id );
     590           0 :       if( activation_slot==FD_FEATURE_DISABLED ) continue;
     591             : 
     592           0 :       fd_feature_t feature = { .is_active = 1, .activation_slot = activation_slot };
     593           0 :       fd_acc_t acc = fd_accdb_write_one( accdb, root_fork_id, id->id.uc );
     594           0 :       acc.lamports = 1UL;
     595           0 :       fd_memcpy( acc.owner, fd_solana_feature_program_id.uc, 32UL );
     596           0 :       fd_memcpy( acc.data, &feature, sizeof(fd_feature_t) );
     597           0 :       acc.data_len = sizeof(fd_feature_t);
     598           0 :       acc.commit   = 1;
     599           0 :       fd_accdb_unwrite_one( accdb, &acc );
     600           0 :       bank->f.capitalization += acc.lamports;
     601           0 :     }
     602           0 :   }
     603             : 
     604           0 :   if( params->init_sysvars ) {
     605             : 
     606             :     /* Blockhash queue -- must be initialized before recent_hashes sysvar */
     607           0 :     fd_blockhashes_t * bhq = fd_blockhashes_init( &bank->f.block_hash_queue, params->hash_seed );
     608           0 :     fd_hash_t genesis_hash = {0};
     609           0 :     fd_memset( genesis_hash.uc, 0xAB, FD_HASH_FOOTPRINT );
     610           0 :     fd_blockhash_info_t * bh_info = fd_blockhashes_push_new( bhq, &genesis_hash );
     611           0 :     bh_info->lamports_per_signature = 0UL;
     612           0 :     bank->f.poh = genesis_hash;
     613             : 
     614             :     /* Clock */
     615           0 :     fd_sol_sysvar_clock_t clock = {
     616           0 :       .slot                  = bank->f.slot,
     617           0 :       .leader_schedule_epoch = 1,
     618           0 :     };
     619           0 :     if( params->clock ) clock = *params->clock;
     620             : 
     621             :     /* Last restart slot */
     622           0 :     uchar last_restart_enc[ FD_SYSVAR_LAST_RESTART_SLOT_BINCODE_SZ ] = {0};
     623             : 
     624             :     /* Recent hashes (encodes from blockhash queue -- initially 1 acc) */
     625           0 :     uchar recent_hashes_enc[ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] = {0};
     626           0 :     ulong rbh_cnt = 1UL;
     627           0 :     memcpy( recent_hashes_enc, &rbh_cnt, sizeof(ulong) );
     628           0 :     memcpy( recent_hashes_enc + sizeof(ulong), genesis_hash.uc, 32 );
     629             :     /* lamports_per_signature = 0 already zeroed */
     630             : 
     631             :     /* Slot hashes (empty) */
     632           0 :     uchar slot_hashes_enc[ FD_SYSVAR_SLOT_HASHES_BINCODE_SZ ] = {0};
     633             : 
     634             :     /* Slot history -- well-formed bincode (large, heap alloc) */
     635           0 :     uchar * slot_history_enc = calloc( 1, FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ );
     636           0 :     FD_TEST( slot_history_enc );
     637           0 :     ulong sh_blocks_len = FD_SLOT_HISTORY_MAX_ENTRIES / 64UL;
     638           0 :     slot_history_enc[0] = 1; /* has_bits */
     639           0 :     FD_STORE( ulong, slot_history_enc+1, sh_blocks_len );
     640           0 :     uchar * sh_footer = slot_history_enc + 9UL + sh_blocks_len * sizeof(ulong);
     641           0 :     FD_STORE( ulong, sh_footer,     FD_SLOT_HISTORY_MAX_ENTRIES );
     642           0 :     FD_STORE( ulong, sh_footer+8UL, bank->f.slot + 1UL );
     643             : 
     644             :     /* Stake history (empty) */
     645           0 :     uchar stake_history_enc[ FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ] = {0};
     646             : 
     647           0 :     struct { fd_pubkey_t const * addr; void const * data; ulong sz; } sysvars[] = {
     648           0 :       { &fd_sysvar_clock_id,               &clock,                  sizeof(clock)                     },
     649           0 :       { &fd_sysvar_epoch_schedule_id,      &bank->f.epoch_schedule, sizeof(bank->f.epoch_schedule)    },
     650           0 :       { &fd_sysvar_rent_id,                &bank->f.rent,           sizeof(bank->f.rent)              },
     651           0 :       { &fd_sysvar_last_restart_slot_id,   last_restart_enc,        sizeof(last_restart_enc)          },
     652           0 :       { &fd_sysvar_recent_block_hashes_id, recent_hashes_enc,       sizeof(recent_hashes_enc)         },
     653           0 :       { &fd_sysvar_slot_hashes_id,         slot_hashes_enc,         sizeof(slot_hashes_enc)           },
     654           0 :       { &fd_sysvar_slot_history_id,        slot_history_enc,        FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ },
     655           0 :       { &fd_sysvar_stake_history_id,       stake_history_enc,       sizeof(stake_history_enc)         },
     656           0 :     };
     657           0 :     for( ulong i=0UL; i<8UL; i++ ) {
     658           0 :       fd_acc_t acc = fd_accdb_write_one( accdb, root_fork_id, sysvars[i].addr->uc );
     659           0 :       acc.lamports = fd_rent_exempt_minimum_balance( &bank->f.rent, sysvars[i].sz );
     660           0 :       fd_memcpy( acc.owner, fd_sysvar_owner_id.uc, 32UL );
     661           0 :       if( sysvars[i].sz ) fd_memcpy( acc.data, sysvars[i].data, sysvars[i].sz );
     662           0 :       acc.data_len = sysvars[i].sz;
     663           0 :       acc.commit   = 1;
     664           0 :       fd_accdb_unwrite_one( accdb, &acc );
     665           0 :       bank->f.capitalization += acc.lamports;
     666           0 :     }
     667             : 
     668           0 :     free( slot_history_enc );
     669             : 
     670           0 :     FD_TEST( fd_sysvar_cache_restore( bank, accdb ) );
     671           0 :   }
     672             : 
     673           0 :   fd_epoch_credits_view_t epoch_credits_view[1];
     674           0 :   FD_TEST( fd_epoch_credits_view_init( epoch_credits_view, fd_bank_epoch_credits( bank ), bank->epoch_credits_fork_id ) );
     675           0 :   epoch_credits_view->len = 0UL;
     676           0 :   fd_epoch_credits_view_fini( epoch_credits_view );
     677             : 
     678           0 :   if( params->mock_validator_cnt ) {
     679           0 :     fd_svm_mini_init_mock_validators( mini, bank, params );
     680           0 :   }
     681             : 
     682           0 :   fd_hash_t const * root_block_hash = fd_blockhashes_peek_last_hash( &bank->f.block_hash_queue );
     683           0 :   if( FD_LIKELY( root_block_hash ) ) {
     684           0 :     fd_txncache_finalize_fork( mini->txncache, bank->txncache_fork_id, 0UL, root_block_hash->uc );
     685           0 :   }
     686             : 
     687           0 :   return bank_idx;
     688           0 : }
     689             : 
     690             : ulong
     691             : fd_svm_mini_attach_child( fd_svm_mini_t * mini,
     692             :                           ulong           parent_bank_idx,
     693           0 :                           ulong           child_slot ) {
     694             : 
     695           0 :   fd_bank_t * parent_bank = fd_banks_bank_query( mini->banks, parent_bank_idx );
     696           0 :   if( FD_UNLIKELY( !parent_bank ) ) FD_LOG_ERR(( "invalid parent_bank_idx" ));
     697           0 :   ulong parent_slot = parent_bank->f.slot;
     698           0 :   if( FD_UNLIKELY( child_slot<=parent_slot ) ) FD_LOG_ERR(( "child_slot (%lu) <= parent_slot (%lu)", child_slot, parent_slot ));
     699             : 
     700           0 :   fd_accdb_t * accdb = mini->runtime->accdb;
     701             : 
     702           0 :   fd_bank_t * bank = fd_banks_new_bank( mini->banks, parent_bank_idx, 0L, 0 );
     703           0 :   if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "fd_banks_new_bank failed" ));
     704           0 :   bank = fd_banks_clone_from_parent( mini->banks, bank->idx );
     705           0 :   if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "fd_banks_clone_from_parent failed" ));
     706           0 :   ulong bank_idx = bank->idx;
     707           0 :   bank->f.slot = child_slot;
     708             : 
     709           0 :   bank->progcache_fork_id    = fd_progcache_attach_child( mini->progcache->join, parent_bank->progcache_fork_id );
     710           0 :   bank->txncache_fork_id     = fd_txncache_attach_child ( mini->txncache,        parent_bank->txncache_fork_id  );
     711           0 :   bank->accdb_fork_id        = fd_accdb_attach_child    ( accdb,                 parent_bank->accdb_fork_id     );
     712           0 :   bank->parent_accdb_fork_id = parent_bank->accdb_fork_id;
     713             : 
     714           0 :   int is_epoch_boundary = 0;
     715           0 :   fd_runtime_block_execute_prepare( mini->banks, bank, accdb, mini->runtime_stack, NULL, &is_epoch_boundary );
     716             : 
     717           0 :   return bank_idx;
     718           0 : }
     719             : 
     720             : void
     721             : fd_svm_mini_freeze( fd_svm_mini_t * mini,
     722           0 :                     ulong           bank_idx ) {
     723           0 :   fd_bank_t * bank = fd_svm_mini_bank( mini, bank_idx );
     724           0 :   if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "invalid bank_idx" ));
     725             :   /* Derive a mock POH hash so each frozen slot registers a unique
     726             :      blockhash.  (Real POH is computed by the PoH tile.) */
     727           0 :   fd_sha256_hash( bank->f.poh.hash, 32UL, bank->f.poh.hash );
     728           0 :   fd_runtime_block_execute_finalize( bank, mini->runtime->accdb, NULL, NULL, (ushort)0, NULL );
     729           0 :   fd_hash_t const * block_hash = fd_blockhashes_peek_last_hash( &bank->f.block_hash_queue );
     730           0 :   FD_TEST( block_hash );
     731           0 :   fd_txncache_finalize_fork( mini->txncache, bank->txncache_fork_id, 0UL, block_hash->uc );
     732           0 :   fd_banks_mark_bank_frozen( bank );
     733           0 : }
     734             : 
     735             : void
     736             : fd_svm_mini_register_blockhash( fd_svm_mini_t *   mini,
     737             :                                 ulong             bank_idx,
     738           0 :                                 fd_hash_t const * blockhash ) {
     739           0 :   fd_bank_t * bank = fd_svm_mini_bank( mini, bank_idx );
     740           0 :   if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "invalid bank_idx" ));
     741             :   /* A txncache query for blockhash B from fork F succeeds only if F
     742             :      descends from the fork B is registered on; a fork's descends-set
     743             :      contains its ancestors, NOT itself.  So register B on bank_idx's
     744             :      PARENT fork — the bank then executes as a descendant and the
     745             :      status-cache query resolves B. */
     746           0 :   fd_bank_t * parent = fd_banks_get_parent( mini->banks, bank );
     747           0 :   if( FD_UNLIKELY( !parent ) ) FD_LOG_ERR(( "bank_idx has no parent fork to register the blockhash on" ));
     748             :   /* finalize_fork (not attach_blockhash): the status-cache query
     749             :      requires the carrier fork to be fully finalized (frozen==2), which
     750             :      attach_blockhash (frozen==1) does not satisfy.  finalize_fork
     751             :      registers the blockhash in the map and sets frozen==2. */
     752           0 :   fd_txncache_finalize_fork( mini->txncache, parent->txncache_fork_id, 0UL, blockhash->uc );
     753           0 : }
     754             : 
     755             : static void
     756             : fd_svm_mini_txncache_cancel_subtree( fd_svm_mini_t * mini,
     757           0 :                                      fd_bank_t *     bank ) {
     758           0 :   ulong child_idx = bank->child_idx;
     759           0 :   while( child_idx!=ULONG_MAX ) {
     760           0 :     fd_bank_t * child = fd_banks_bank_query( mini->banks, child_idx );
     761           0 :     FD_TEST( child );
     762           0 :     ulong sibling_idx = child->sibling_idx;
     763           0 :     fd_svm_mini_txncache_cancel_subtree( mini, child );
     764           0 :     child_idx = sibling_idx;
     765           0 :   }
     766           0 :   fd_txncache_cancel_fork( mini->txncache, bank->txncache_fork_id );
     767           0 : }
     768             : 
     769             : void
     770             : fd_svm_mini_cancel_fork( fd_svm_mini_t * mini,
     771           0 :                          ulong           bank_idx ) {
     772             : 
     773           0 :   fd_bank_t * bank = fd_svm_mini_bank( mini, bank_idx );
     774           0 :   if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "invalid bank_idx" ));
     775             : 
     776           0 :   fd_progcache_cancel_fork( mini->progcache->join, bank->progcache_fork_id );
     777           0 :   fd_svm_mini_txncache_cancel_subtree( mini, bank );
     778             : 
     779           0 :   fd_accdb_purge( mini->runtime->accdb, bank->accdb_fork_id );
     780           0 :   drain_background( mini->runtime->accdb );
     781           0 : }
     782             : 
     783             : void
     784             : fd_svm_mini_advance_root( fd_svm_mini_t * mini,
     785           0 :                           ulong           bank_idx ) {
     786             : 
     787           0 :   fd_bank_t * bank = fd_banks_bank_query( mini->banks, bank_idx );
     788           0 :   if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "invalid bank_idx" ));
     789             : 
     790           0 :   fd_progcache_advance_root( mini->progcache->join, bank->progcache_fork_id );
     791           0 :   fd_txncache_advance_root ( mini->txncache,        bank->txncache_fork_id  );
     792           0 :   fd_accdb_advance_root( mini->runtime->accdb, bank->accdb_fork_id );
     793           0 :   drain_background( mini->runtime->accdb );
     794           0 :   fd_banks_advance_root( mini->banks, bank_idx );
     795           0 : }
     796             : 
     797             : fd_bank_t *
     798             : fd_svm_mini_bank( fd_svm_mini_t * mini,
     799           0 :                   ulong           bank_idx ) {
     800           0 :   if( FD_UNLIKELY( bank_idx>=fd_banks_pool_max_cnt( mini->banks ) ) ) return NULL;
     801           0 :   fd_bank_t * bank = fd_banks_bank_query( mini->banks, bank_idx );
     802           0 :   if( FD_UNLIKELY( !bank ) ) return NULL;
     803           0 :   return bank;
     804           0 : }
     805             : 
     806             : fd_accdb_fork_id_t
     807             : fd_svm_mini_fork_id( fd_svm_mini_t * mini,
     808           0 :                      ulong           bank_idx ) {
     809           0 :   fd_bank_t * bank = fd_banks_bank_query( mini->banks, bank_idx );
     810           0 :   if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "invalid bank_idx" ));
     811           0 :   return bank->accdb_fork_id;
     812           0 : }
     813             : 
     814             : void
     815             : fd_svm_mini_put_account_rooted( fd_svm_mini_t *  mini,
     816           0 :                                 fd_acc_t const * ro ) {
     817           0 :   fd_accdb_t *       accdb   = mini->runtime->accdb;
     818           0 :   fd_bank_t *        root    = fd_banks_root( mini->banks );
     819           0 :   fd_accdb_fork_id_t root_fk = root->accdb_fork_id;
     820             : 
     821           0 :   ulong old_lamports = fd_accdb_lamports( accdb, root_fk, ro->pubkey );
     822           0 :   if( old_lamports==ULONG_MAX ) old_lamports = 0UL;
     823             : 
     824           0 :   fd_acc_t acc = fd_accdb_write_one( accdb, root_fk, ro->pubkey );
     825           0 :   acc.lamports   = ro->lamports;
     826           0 :   fd_memcpy( acc.owner, ro->owner, 32UL );
     827           0 :   acc.executable = ro->executable;
     828           0 :   acc.data_len   = ro->data_len;
     829           0 :   if( ro->data_len && ro->data ) fd_memcpy( acc.data, ro->data, ro->data_len );
     830           0 :   acc.commit = 1;
     831           0 :   fd_accdb_unwrite_one( accdb, &acc );
     832             : 
     833           0 :   if( root ) {
     834           0 :     if( ro->lamports >= old_lamports )
     835           0 :       root->f.capitalization += ro->lamports - old_lamports;
     836           0 :     else
     837           0 :       root->f.capitalization -= old_lamports - ro->lamports;
     838           0 :   }
     839           0 : }
     840             : 
     841             : void
     842             : fd_svm_mini_add_lamports_rooted( fd_svm_mini_t *     mini,
     843             :                                  fd_pubkey_t const * pubkey,
     844           0 :                                  ulong               lamports ) {
     845           0 :   fd_accdb_t *       accdb   = mini->runtime->accdb;
     846           0 :   fd_bank_t *        root    = fd_banks_root( mini->banks );
     847           0 :   fd_accdb_fork_id_t root_fk = root->accdb_fork_id;
     848             : 
     849           0 :   fd_acc_t acc = fd_accdb_write_one( accdb, root_fk, pubkey->uc );
     850           0 :   ulong balance = acc.lamports;
     851           0 :   FD_TEST( !__builtin_uaddl_overflow( balance, lamports, &balance ) );
     852           0 :   acc.lamports = balance;
     853           0 :   acc.commit = 1;
     854           0 :   fd_accdb_unwrite_one( accdb, &acc );
     855             : 
     856           0 :   if( root ) root->f.capitalization += lamports;
     857           0 : }
     858             : 
     859             : void
     860             : fd_svm_mini_add_lamports( fd_svm_mini_t *     mini,
     861             :                           fd_accdb_fork_id_t  fork_id,
     862             :                           fd_pubkey_t const * pubkey,
     863           0 :                           ulong               lamports ) {
     864           0 :   fd_accdb_t * accdb = mini->runtime->accdb;
     865             : 
     866           0 :   fd_acc_t acc = fd_accdb_write_one( accdb, fork_id, pubkey->uc );
     867           0 :   ulong balance = acc.lamports;
     868           0 :   FD_TEST( !__builtin_uaddl_overflow( balance, lamports, &balance ) );
     869           0 :   acc.lamports = balance;
     870           0 :   acc.commit = 1;
     871           0 :   fd_accdb_unwrite_one( accdb, &acc );
     872             : 
     873             :   /* Find the bank with this fork_id to update capitalization.
     874             :      Linear scan is fine for test code. */
     875           0 :   for( ulong i=0UL; i<fd_banks_pool_max_cnt( mini->banks ); i++ ) {
     876           0 :     fd_bank_t * bank = fd_banks_bank_query( mini->banks, i );
     877           0 :     if( bank && bank->accdb_fork_id.val==fork_id.val ) {
     878           0 :       bank->f.capitalization += lamports;
     879           0 :       break;
     880           0 :     }
     881           0 :   }
     882           0 : }

Generated by: LCOV version 1.14