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