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