Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_svm_mini.h"
3 : #include "../../accdb/fd_accdb_admin_v1.h"
4 : #include "../../accdb/fd_accdb_sync.h"
5 : #include "../../accdb/fd_accdb_impl_v1.h"
6 : #include "../../accdb/fd_accdb_funk.h"
7 : #include "../../progcache/fd_progcache_admin.h"
8 : #include "../../progcache/fd_progcache_user.h"
9 : #include "../../runtime/fd_bank.h"
10 : #include "../../runtime/program/fd_builtin_programs.h"
11 : #include "../../runtime/fd_system_ids.h"
12 : #include "../../runtime/fd_runtime_const.h"
13 : #include "../../log_collector/fd_log_collector.h"
14 : #include "../../runtime/fd_runtime.h"
15 : #include "../../runtime/sysvar/fd_sysvar_cache.h"
16 : #include "../../runtime/sysvar/fd_sysvar_rent.h"
17 : #include "../../runtime/sysvar/fd_sysvar_epoch_schedule.h"
18 : #include "../../runtime/sysvar/fd_sysvar_slot_history.h"
19 : #include "../../runtime/program/fd_vote_program.h"
20 : #include "../../stakes/fd_stake_types.h"
21 : #include "../../stakes/fd_vote_stakes.h"
22 : #include "../../stakes/fd_stake_delegations.h"
23 : #include "../../stakes/fd_top_votes.h"
24 : #include "../../leaders/fd_leaders.h"
25 : #include <errno.h>
26 : #include <stdlib.h>
27 : #include <sys/mman.h>
28 :
29 : static fd_wksp_t *
30 0 : fd_wksp_new_lazy( ulong footprint ) {
31 0 : footprint = fd_ulong_align_up( footprint, FD_SHMEM_NORMAL_PAGE_SZ );
32 0 : void * mem = mmap( NULL, footprint, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 );
33 0 : if( FD_UNLIKELY( mem==MAP_FAILED ) ) {
34 0 : FD_LOG_ERR(( "mmap(NULL,%lu KiB,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS) failed (%i-%s)",
35 0 : footprint>>10, errno, fd_io_strerror( errno ) ));
36 0 : }
37 :
38 0 : ulong part_max = fd_wksp_part_max_est( footprint, 64UL<<10 );
39 0 : FD_TEST( part_max );
40 0 : ulong data_max = fd_wksp_data_max_est( footprint, part_max );
41 0 : FD_TEST( data_max );
42 0 : fd_wksp_t * wksp = fd_wksp_join( fd_wksp_new( mem, "wksp", 1U, part_max, data_max ) );
43 0 : FD_TEST( wksp );
44 :
45 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 ) );
46 0 : return wksp;
47 0 : }
48 :
49 : fd_svm_mini_t *
50 : fd_svm_test_boot( int * pargc,
51 : char *** pargv,
52 30 : fd_svm_mini_limits_t const * limits ) {
53 :
54 30 : fd_boot( pargc, pargv );
55 :
56 30 : char const * page_sz_cstr = fd_env_strip_cmdline_cstr ( pargc, pargv, "--page-sz", NULL, NULL );
57 30 : ulong page_cnt = fd_env_strip_cmdline_ulong( pargc, pargv, "--page-cnt", NULL, 0UL );
58 30 : char const * wksp_name = fd_env_strip_cmdline_cstr ( pargc, pargv, "--wksp", NULL, NULL );
59 30 : ulong near_cpu = fd_env_strip_cmdline_ulong( pargc, pargv, "--near-cpu", NULL, fd_log_cpu_id() );
60 30 : ulong wksp_tag = limits->wksp_tag ? limits->wksp_tag : 42UL;
61 :
62 30 : ulong data_max = fd_svm_mini_wksp_data_max( limits );
63 30 : ulong part_max = fd_wksp_part_max_est( data_max, 64UL<<10 );
64 30 : ulong wksp_sz = fd_wksp_footprint( part_max, data_max );
65 30 : fd_wksp_t * wksp = NULL;
66 30 : if( wksp_name && !!wksp_name[0] ) {
67 0 : FD_LOG_NOTICE(( "Attaching to --wksp %s", wksp_name ));
68 0 : wksp = fd_wksp_attach( wksp_name );
69 30 : } else if( page_sz_cstr ) {
70 30 : ulong page_sz = fd_cstr_to_shmem_page_sz( page_sz_cstr );
71 30 : if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "Invalid --page-sz %s", page_sz_cstr ));
72 30 : if( FD_UNLIKELY( page_sz*page_cnt < wksp_sz ) ) {
73 0 : FD_LOG_WARNING(( "--page-sz %s * --page-cnt %lu is smaller than required wksp_sz %lu KiB; falling back to lazy anonymous memory",
74 0 : page_sz_cstr, page_cnt, wksp_sz>>10 ));
75 0 : goto fallback;
76 0 : }
77 30 : FD_LOG_NOTICE(( "--wksp not specified, using anonymous pinned shmem" ));
78 30 : wksp = fd_wksp_new_anonymous( page_sz, page_cnt, near_cpu, "wksp", wksp_tag );
79 30 : } else {
80 0 : fallback:
81 0 : FD_LOG_NOTICE(( "--page-sz not specified, using lazy paged memory" ));
82 0 : wksp = fd_wksp_new_lazy( wksp_sz );
83 0 : }
84 30 : if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "Unable to attach to wksp" ));
85 :
86 30 : return fd_svm_mini_create( wksp, limits );
87 30 : }
88 :
89 : void
90 30 : fd_svm_test_halt( fd_svm_mini_t * mini ) {
91 30 : fd_svm_mini_destroy( mini );
92 30 : fd_halt();
93 30 : }
94 :
95 : ulong
96 30 : fd_svm_mini_wksp_data_max( fd_svm_mini_limits_t const * limits ) {
97 30 : ulong txn_max = limits->max_live_slots;
98 30 : ulong rec_max = limits->max_accounts;
99 :
100 30 : ulong acc_pool_cnt = fd_ulong_max( limits->max_txn_write_locks + 2UL, FD_ACC_POOL_MIN_ACCOUNT_CNT_PER_TX );
101 30 : ulong funk_sz = fd_funk_shmem_footprint( txn_max, rec_max );
102 30 : ulong funk_lock_sz = fd_funk_locks_footprint( txn_max, rec_max );
103 30 : ulong pcache_sz = fd_progcache_shmem_footprint( txn_max, limits->max_progcache_recs );
104 30 : ulong banks_sz = fd_banks_footprint( txn_max, limits->max_fork_width, limits->max_stake_accounts, limits->max_vote_accounts );
105 30 : ulong acc_pool_sz = fd_acc_pool_footprint( acc_pool_cnt );
106 30 : ulong runtime_stack_sz = fd_runtime_stack_footprint( limits->max_vote_accounts, limits->max_vote_accounts, limits->max_stake_accounts );
107 :
108 360 : # define WKSP_ALLOC(a,s) fd_ulong_align_up( fd_ulong_max((s),1UL), fd_ulong_max((a),FD_WKSP_ALIGN_DEFAULT) )
109 30 : ulong sz = 0UL;
110 30 : sz += WKSP_ALLOC( alignof(fd_svm_mini_t), sizeof(fd_svm_mini_t) );
111 30 : sz += WKSP_ALLOC( fd_funk_align(), funk_sz );
112 30 : sz += WKSP_ALLOC( fd_funk_align(), funk_lock_sz );
113 30 : sz += WKSP_ALLOC( fd_progcache_shmem_align(), pcache_sz );
114 30 : sz += WKSP_ALLOC( FD_PROGCACHE_SCRATCH_ALIGN, FD_PROGCACHE_SCRATCH_FOOTPRINT );
115 30 : sz += WKSP_ALLOC( fd_banks_align(), banks_sz );
116 30 : sz += WKSP_ALLOC( fd_acc_pool_align(), acc_pool_sz );
117 30 : sz += WKSP_ALLOC( alignof(fd_runtime_t), sizeof(fd_runtime_t) );
118 30 : sz += WKSP_ALLOC( fd_runtime_stack_align(), runtime_stack_sz );
119 30 : sz += WKSP_ALLOC( fd_vm_align(), fd_vm_footprint() );
120 30 : sz += WKSP_ALLOC( 16UL, limits->max_account_space_bytes );
121 30 : sz += WKSP_ALLOC( 1UL, limits->max_progcache_heap_bytes );
122 30 : # undef WKSP_ALLOC
123 :
124 30 : return sz;
125 30 : }
126 :
127 : fd_svm_mini_t *
128 : fd_svm_mini_create( fd_wksp_t * wksp,
129 30 : fd_svm_mini_limits_t const * limits ) {
130 :
131 30 : ulong const wksp_tag = limits->wksp_tag ? limits->wksp_tag : 42UL;
132 30 : ulong const txn_max = limits->max_live_slots;
133 30 : ulong const rec_max = limits->max_accounts;
134 :
135 30 : ulong acc_pool_cnt = fd_ulong_max( limits->max_txn_write_locks + 2UL, FD_ACC_POOL_MIN_ACCOUNT_CNT_PER_TX );
136 30 : ulong funk_sz = fd_funk_shmem_footprint( txn_max, rec_max );
137 30 : ulong funk_lock_sz = fd_funk_locks_footprint( txn_max, rec_max );
138 30 : ulong pcache_sz = fd_progcache_shmem_footprint( txn_max, limits->max_progcache_recs );
139 30 : ulong banks_sz = fd_banks_footprint( txn_max, limits->max_fork_width,
140 30 : limits->max_stake_accounts, limits->max_vote_accounts );
141 30 : ulong acc_pool_sz = fd_acc_pool_footprint( acc_pool_cnt );
142 30 : ulong runtime_stack_sz = fd_runtime_stack_footprint( limits->max_vote_accounts, limits->max_vote_accounts, limits->max_stake_accounts );
143 :
144 : /* Allocate objects */
145 :
146 30 : 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 )) );
147 30 : void * funk_mem; FD_TEST( (funk_mem = fd_wksp_alloc_laddr( wksp, fd_funk_align(), funk_sz, wksp_tag )) );
148 30 : void * funk_locks; FD_TEST( (funk_locks = fd_wksp_alloc_laddr( wksp, fd_funk_align(), funk_lock_sz, wksp_tag )) );
149 30 : void * pcache_mem; FD_TEST( (pcache_mem = fd_wksp_alloc_laddr( wksp, fd_progcache_shmem_align(), pcache_sz, wksp_tag )) );
150 30 : uchar * scratch; FD_TEST( (scratch = fd_wksp_alloc_laddr( wksp, FD_PROGCACHE_SCRATCH_ALIGN, FD_PROGCACHE_SCRATCH_FOOTPRINT, wksp_tag )) );
151 30 : void * banks_mem; FD_TEST( (banks_mem = fd_wksp_alloc_laddr( wksp, fd_banks_align(), banks_sz, wksp_tag )) );
152 30 : void * acc_pool_mem; FD_TEST( (acc_pool_mem = fd_wksp_alloc_laddr( wksp, fd_acc_pool_align(), acc_pool_sz, wksp_tag )) );
153 30 : fd_runtime_t * runtime; FD_TEST( (runtime = fd_wksp_alloc_laddr( wksp, alignof(fd_runtime_t), sizeof(fd_runtime_t), wksp_tag )) );
154 30 : void * rstack_mem; FD_TEST( (rstack_mem = fd_wksp_alloc_laddr( wksp, fd_runtime_stack_align(), runtime_stack_sz, wksp_tag )) );
155 30 : void * vm_mem; FD_TEST( (vm_mem = fd_wksp_alloc_laddr( wksp, fd_vm_align(), fd_vm_footprint(), wksp_tag )) );
156 :
157 : /* Initialize objects */
158 :
159 30 : fd_memset( mini, 0, sizeof(fd_svm_mini_t) );
160 30 : mini->wksp = wksp;
161 :
162 30 : void * shfunk = fd_funk_shmem_new ( funk_mem, wksp_tag, 1UL, txn_max, rec_max );
163 30 : void * shpcache = fd_progcache_shmem_new( pcache_mem, wksp_tag, 1UL, txn_max, limits->max_progcache_recs );
164 30 : if( FD_UNLIKELY( !shfunk ) ) FD_LOG_ERR(( "fd_funk_shmem_new failed" ));
165 30 : if( FD_UNLIKELY( !shpcache ) ) FD_LOG_ERR(( "fd_progcache_shmem_new failed" ));
166 30 : FD_TEST( fd_funk_locks_new( funk_locks, txn_max, rec_max ) );
167 :
168 30 : FD_TEST( fd_accdb_admin_v1_init( mini->accdb_admin, funk_mem, funk_locks ) );
169 30 : FD_TEST( fd_accdb_user_v1_init ( mini->accdb, funk_mem, funk_locks, txn_max ) );
170 :
171 30 : FD_TEST( fd_progcache_join( mini->progcache, pcache_mem, scratch, FD_PROGCACHE_SCRATCH_FOOTPRINT ) );
172 :
173 30 : mini->banks = fd_banks_join( fd_banks_new( banks_mem, txn_max, limits->max_fork_width,
174 30 : limits->max_stake_accounts, limits->max_vote_accounts, 0, 8888UL ) );
175 30 : FD_TEST( mini->banks );
176 :
177 30 : mini->acc_pool = fd_acc_pool_join( fd_acc_pool_new( acc_pool_mem, acc_pool_cnt ) );
178 30 : FD_TEST( mini->acc_pool );
179 :
180 30 : mini->runtime = runtime;
181 :
182 30 : runtime->accdb = mini->accdb;
183 30 : runtime->status_cache = NULL;
184 30 : runtime->progcache = mini->progcache;
185 30 : runtime->acc_pool = mini->acc_pool;
186 :
187 30 : runtime->instr.stack_sz = 0;
188 30 : runtime->instr.trace_length = 0;
189 30 : runtime->instr.current_idx = 0;
190 30 : runtime->accounts.executable_cnt = 0;
191 30 : fd_memset( &runtime->log, 0, sizeof(runtime->log) );
192 30 : fd_memset( &runtime->metrics, 0, sizeof(runtime->metrics) );
193 30 : fd_memset( &runtime->fuzz, 0, sizeof(runtime->fuzz) );
194 :
195 30 : mini->runtime_stack = fd_runtime_stack_join( fd_runtime_stack_new( rstack_mem,
196 30 : limits->max_vote_accounts, limits->max_vote_accounts, limits->max_stake_accounts, 42UL ) );
197 30 : FD_TEST( mini->runtime_stack );
198 :
199 30 : fd_log_collector_init( mini->log_collector, 1 );
200 30 : runtime->log.enable_log_collector = 0;
201 30 : runtime->log.log_collector = mini->log_collector;
202 :
203 30 : fd_features_disable_all( mini->features );
204 30 : fd_features_enable_cleaned_up( mini->features );
205 :
206 30 : FD_TEST( fd_sha256_join( fd_sha256_new( mini->sha256 ) ) );
207 :
208 30 : mini->vm = fd_vm_join( fd_vm_new( vm_mem ) );
209 30 : FD_TEST( mini->vm );
210 :
211 30 : return mini;
212 30 : }
213 :
214 : void
215 30 : fd_svm_mini_destroy( fd_svm_mini_t * mini ) {
216 30 : if( FD_UNLIKELY( !mini ) ) return;
217 :
218 30 : if( mini->vm ) fd_wksp_free_laddr( fd_vm_delete( fd_vm_leave( mini->vm ) ) );
219 30 : if( mini->runtime_stack ) fd_wksp_free_laddr( mini->runtime_stack );
220 30 : if( mini->runtime ) fd_wksp_free_laddr( mini->runtime );
221 30 : if( mini->acc_pool ) fd_wksp_free_laddr( mini->acc_pool );
222 30 : if( mini->banks ) fd_wksp_free_laddr( mini->banks );
223 :
224 30 : uchar * scratch = mini->progcache->scratch;
225 30 : fd_progcache_shmem_t * shpcache = NULL;
226 30 : fd_progcache_leave( mini->progcache, &shpcache );
227 30 : if( scratch ) fd_wksp_free_laddr( scratch );
228 30 : if( shpcache ) fd_wksp_free_laddr( fd_progcache_shmem_delete( shpcache ) );
229 :
230 30 : void * shfunk = fd_accdb_user_v1_funk( mini->accdb )->shmem;
231 30 : void * shfunk_lock = (void *)fd_accdb_user_v1_funk( mini->accdb )->txn_lock;
232 30 : fd_accdb_user_fini ( mini->accdb );
233 30 : fd_accdb_admin_fini( mini->accdb_admin );
234 30 : fd_wksp_free_laddr( shfunk_lock );
235 30 : fd_wksp_free_laddr( fd_funk_delete( shfunk ) );
236 :
237 30 : fd_wksp_free_laddr( mini );
238 30 : }
239 :
240 : static void
241 : fd_svm_mini_init_mock_validators( fd_svm_mini_t * mini,
242 : fd_bank_t * bank,
243 3438 : fd_svm_mini_params_t const * params ) {
244 :
245 3438 : ulong const N = params->mock_validator_cnt;
246 3438 : ulong const uniform_stake = 1000000000UL; /* 1 SOL */
247 3438 : ulong const vote_min_bal = fd_rent_exempt_minimum_balance( &bank->f.rent, FD_VOTE_STATE_V3_SZ );
248 3438 : ulong const stake_min_bal = fd_rent_exempt_minimum_balance( &bank->f.rent, FD_STAKE_STATE_SZ );
249 :
250 3438 : fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
251 3438 : fd_vote_stakes_reset( vote_stakes );
252 :
253 3438 : fd_top_votes_t * top_votes_t_1 = fd_bank_top_votes_t_1_modify( bank );
254 3438 : fd_top_votes_init( top_votes_t_1 );
255 3438 : fd_top_votes_t * top_votes_t_2 = fd_bank_top_votes_t_2_modify( bank );
256 3438 : fd_top_votes_init( top_votes_t_2 );
257 :
258 3438 : fd_stake_delegations_t * stake_delegations = fd_banks_stake_delegations_root_query( mini->banks );
259 :
260 3438 : fd_vote_stake_weight_t * stakes = calloc( N, sizeof(fd_vote_stake_weight_t) );
261 3438 : FD_TEST( stakes );
262 :
263 3438 : fd_rng_t rng[1];
264 3438 : fd_rng_join( fd_rng_new( rng, (uint)params->hash_seed, 0UL ) );
265 :
266 6882 : for( ulong i=0UL; i<N; i++ ) {
267 :
268 : /* Generate deterministic pubkeys */
269 :
270 3444 : fd_pubkey_t identity_key, vote_key, stake_key;
271 17220 : for( ulong j=0UL; j<4UL; j++ ) identity_key.ul[j] = fd_rng_ulong( rng );
272 17220 : for( ulong j=0UL; j<4UL; j++ ) vote_key.ul[j] = fd_rng_ulong( rng );
273 17220 : for( ulong j=0UL; j<4UL; j++ ) stake_key.ul[j] = fd_rng_ulong( rng );
274 :
275 : /* Identity account */
276 :
277 3444 : fd_svm_mini_add_lamports_rooted( mini, &identity_key, 500000000000UL /* 500 SOL */ );
278 :
279 : /* Vote account */
280 :
281 3444 : {
282 3444 : uchar vote_state_data[ FD_VOTE_STATE_V3_SZ ] = {0};
283 :
284 3444 : fd_vote_state_versioned_t versioned[1];
285 3444 : fd_vote_state_versioned_new( versioned, fd_vote_state_versioned_enum_v3 );
286 :
287 3444 : fd_vote_state_v3_t * vs = &versioned->v3;
288 3444 : vs->node_pubkey = identity_key;
289 3444 : vs->authorized_withdrawer = identity_key;
290 3444 : vs->commission = 100;
291 :
292 3444 : fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( vs->authorized_voters.pool );
293 3444 : *ele = (fd_vote_authorized_voter_t){
294 3444 : .epoch = 0UL,
295 3444 : .pubkey = identity_key,
296 3444 : .prio = identity_key.uc[0],
297 3444 : };
298 3444 : fd_vote_authorized_voters_treap_ele_insert( vs->authorized_voters.treap, ele, vs->authorized_voters.pool );
299 3444 : FD_TEST( !fd_vote_state_versioned_serialize( versioned, vote_state_data, sizeof(vote_state_data) ) );
300 :
301 3444 : fd_account_meta_t meta = { .lamports = vote_min_bal, .dlen = FD_VOTE_STATE_V3_SZ };
302 3444 : memcpy( meta.owner, fd_solana_vote_program_id.uc, 32UL );
303 3444 : fd_accdb_ro_t ro[1];
304 3444 : fd_accdb_ro_init_nodb_oob( ro, &vote_key, &meta, vote_state_data );
305 3444 : fd_svm_mini_put_account_rooted( mini, ro );
306 3444 : }
307 :
308 : /* Stake account */
309 :
310 0 : {
311 3444 : uchar stake_data[ FD_STAKE_STATE_SZ ] = {0};
312 3444 : FD_STORE( fd_stake_state_t, stake_data, ((fd_stake_state_t) {
313 3444 : .stake_type = FD_STAKE_STATE_STAKE,
314 3444 : .stake = {
315 3444 : .meta = {
316 3444 : .rent_exempt_reserve = stake_min_bal,
317 3444 : .staker = identity_key,
318 3444 : .withdrawer = identity_key,
319 3444 : },
320 3444 : .stake = (fd_stake_t) {
321 3444 : .delegation = (fd_delegation_t) {
322 3444 : .voter_pubkey = vote_key,
323 3444 : .stake = uniform_stake,
324 3444 : .activation_epoch = ULONG_MAX,
325 3444 : .deactivation_epoch = ULONG_MAX,
326 3444 : .warmup_cooldown_rate = 0.25,
327 3444 : },
328 3444 : .credits_observed = 0UL,
329 3444 : },
330 3444 : },
331 3444 : }) );
332 :
333 3444 : fd_account_meta_t meta = {
334 3444 : .lamports = fd_ulong_max( stake_min_bal, uniform_stake ),
335 3444 : .dlen = FD_STAKE_STATE_SZ,
336 3444 : };
337 3444 : memcpy( meta.owner, fd_solana_stake_program_id.uc, 32UL );
338 3444 : fd_accdb_ro_t ro[1];
339 3444 : fd_accdb_ro_init_nodb_oob( ro, &stake_key, &meta, stake_data );
340 3444 : fd_svm_mini_put_account_rooted( mini, ro );
341 3444 : }
342 :
343 : /* Populate bank structures */
344 :
345 3444 : fd_vote_stakes_root_insert_key ( vote_stakes, &vote_key, &identity_key, uniform_stake, 0, 0UL );
346 3444 : fd_vote_stakes_root_update_meta( vote_stakes, &vote_key, &identity_key, uniform_stake, 0, 0UL );
347 :
348 3444 : fd_top_votes_insert( top_votes_t_1, &vote_key, &identity_key, uniform_stake, 0 );
349 3444 : fd_top_votes_insert( top_votes_t_2, &vote_key, &identity_key, uniform_stake, 0 );
350 :
351 3444 : fd_stake_delegations_root_update( stake_delegations,
352 3444 : &stake_key, &vote_key,
353 3444 : uniform_stake,
354 3444 : ULONG_MAX, /* activation_epoch (bootstrap) */
355 3444 : ULONG_MAX, /* deactivation_epoch */
356 3444 : 0UL, /* credits_observed */
357 3444 : FD_STAKE_DELEGATIONS_WARMUP_COOLDOWN_RATE_ENUM_025 /* warmup_cooldown_rate */ );
358 :
359 3444 : stakes[i] = (fd_vote_stake_weight_t){
360 3444 : .vote_key = vote_key,
361 3444 : .id_key = identity_key,
362 3444 : .stake = uniform_stake,
363 3444 : };
364 3444 : }
365 :
366 3438 : fd_vote_stakes_genesis_fini( vote_stakes );
367 :
368 : /* Create leader schedule */
369 :
370 3438 : ulong epoch = bank->f.epoch;
371 3438 : ulong slot0 = fd_epoch_slot0( &bank->f.epoch_schedule, epoch );
372 3438 : ulong slot_cnt = bank->f.epoch_schedule.slots_per_epoch;
373 :
374 3438 : void * leaders_mem = fd_bank_epoch_leaders_modify( bank, epoch );
375 3438 : FD_TEST( fd_epoch_leaders_join( fd_epoch_leaders_new(
376 3438 : leaders_mem, epoch, slot0, slot_cnt, N, stakes, 0UL ) ) );
377 :
378 3438 : fd_rng_delete( fd_rng_leave( rng ) );
379 3438 : free( stakes );
380 3438 : }
381 :
382 : ulong
383 : fd_svm_mini_reset( fd_svm_mini_t * mini,
384 3447 : fd_svm_mini_params_t * params ) {
385 :
386 3447 : fd_accdb_v1_clear( mini->accdb_admin );
387 3447 : fd_progcache_reset( mini->progcache->join );
388 3447 : fd_banks_clear( mini->banks );
389 :
390 3447 : fd_bank_t * bank = fd_banks_init_bank( mini->banks );
391 3447 : FD_TEST( bank );
392 3447 : ulong bank_idx = bank->idx;
393 :
394 3447 : bank->f.slot = params->root_slot;
395 :
396 3447 : fd_xid_t root_xid = fd_bank_xid( bank );
397 3447 : fd_funk_t * funk = fd_accdb_user_v1_funk( mini->accdb );
398 3447 : fd_funk_txn_xid_copy( funk->shmem->last_publish, &root_xid );
399 3447 : bank->progcache_fork_id = fd_progcache_fork_id_initial();
400 :
401 3447 : if( params->clock ) {
402 0 : bank->f.slot = params->clock->slot;
403 0 : bank->f.epoch = params->clock->epoch;
404 0 : }
405 :
406 3447 : if( params->epoch_schedule ) {
407 0 : bank->f.epoch_schedule = *params->epoch_schedule;
408 3447 : } else {
409 3447 : bank->f.epoch_schedule = (fd_epoch_schedule_t) {
410 3447 : .slots_per_epoch = params->slots_per_epoch,
411 3447 : .leader_schedule_slot_offset = params->slots_per_epoch,
412 3447 : .warmup = 0,
413 3447 : .first_normal_epoch = 0UL,
414 3447 : .first_normal_slot = 0UL,
415 3447 : };
416 3447 : }
417 :
418 : /* Default slots_per_year matches Solana mainnet genesis defaults
419 : (target_tick_duration=6250000ns, ticks_per_slot=64). */
420 3447 : bank->f.slots_per_year = SECONDS_PER_YEAR * (1000000000.0 / 6250000.0) / 64.0;
421 :
422 3447 : if( params->rent ) {
423 0 : bank->f.rent = *params->rent;
424 3447 : } else {
425 3447 : bank->f.rent = (fd_rent_t) {
426 3447 : .lamports_per_uint8_year = 3480UL,
427 3447 : .exemption_threshold = 2.0,
428 3447 : .burn_percent = 50,
429 3447 : };
430 3447 : }
431 :
432 3447 : fd_features_disable_all( &bank->f.features );
433 3447 : fd_features_enable_cleaned_up( &bank->f.features );
434 :
435 3447 : if( params->init_builtins ) {
436 3447 : fd_builtin_program_t const * builtins = fd_builtins();
437 34470 : for( ulong i=0UL; i<fd_num_builtins(); i++ ) {
438 31023 : char const * data = builtins[i].data;
439 31023 : ulong sz = strlen( data );
440 31023 : fd_account_meta_t meta = { .lamports = 1UL, .dlen = (uint)sz, .executable = 1 };
441 31023 : memcpy( meta.owner, fd_solana_native_loader_id.uc, 32UL );
442 31023 : fd_accdb_ro_t ro[1];
443 31023 : fd_accdb_ro_init_nodb_oob( ro, builtins[i].pubkey, &meta, data );
444 31023 : fd_svm_mini_put_account_rooted( mini, ro );
445 31023 : }
446 :
447 3447 : fd_pubkey_t const * precompiles[] = {
448 3447 : &fd_solana_keccak_secp_256k_program_id,
449 3447 : &fd_solana_ed25519_sig_verify_program_id,
450 3447 : &fd_solana_secp256r1_program_id,
451 3447 : };
452 13788 : for( ulong i=0UL; i<3UL; i++ ) {
453 10341 : fd_account_meta_t meta = { .lamports = 1UL, .dlen = 0, .executable = 1 };
454 10341 : memcpy( meta.owner, fd_solana_native_loader_id.uc, 32UL );
455 10341 : fd_accdb_ro_t ro[1];
456 10341 : fd_accdb_ro_init_nodb_oob( ro, precompiles[i], &meta, NULL );
457 10341 : fd_svm_mini_put_account_rooted( mini, ro );
458 10341 : }
459 3447 : }
460 :
461 3447 : if( params->init_feature_accounts ) {
462 0 : for( fd_feature_id_t const * id = fd_feature_iter_init();
463 0 : !fd_feature_iter_done( id );
464 0 : id = fd_feature_iter_next( id ) ) {
465 0 : ulong activation_slot = fd_features_get( &bank->f.features, id );
466 0 : if( activation_slot==FD_FEATURE_DISABLED ) continue;
467 :
468 0 : fd_feature_t feature = { .is_active = 1, .activation_slot = activation_slot };
469 0 : fd_account_meta_t meta = { .lamports = 1UL, .dlen = sizeof(fd_feature_t) };
470 0 : memcpy( meta.owner, fd_solana_feature_program_id.uc, 32UL );
471 0 : fd_accdb_ro_t ro[1];
472 0 : fd_accdb_ro_init_nodb_oob( ro, &id->id, &meta, &feature );
473 0 : fd_svm_mini_put_account_rooted( mini, ro );
474 0 : }
475 0 : }
476 :
477 3447 : if( params->init_sysvars ) {
478 :
479 : /* Blockhash queue -- must be initialized before recent_hashes sysvar */
480 3447 : fd_blockhashes_t * bhq = fd_blockhashes_init( &bank->f.block_hash_queue, params->hash_seed );
481 3447 : fd_hash_t genesis_hash = {0};
482 3447 : fd_memset( genesis_hash.uc, 0xAB, FD_HASH_FOOTPRINT );
483 3447 : fd_blockhash_info_t * bh_info = fd_blockhashes_push_new( bhq, &genesis_hash );
484 3447 : bh_info->lamports_per_signature = 0UL;
485 3447 : bank->f.poh = genesis_hash;
486 :
487 : /* Clock */
488 3447 : fd_sol_sysvar_clock_t clock = {
489 3447 : .slot = bank->f.slot,
490 3447 : .leader_schedule_epoch = 1,
491 3447 : };
492 3447 : if( params->clock ) clock = *params->clock;
493 :
494 : /* Last restart slot */
495 3447 : uchar last_restart_enc[ FD_SYSVAR_LAST_RESTART_SLOT_BINCODE_SZ ] = {0};
496 :
497 : /* Recent hashes (encodes from blockhash queue -- initially 1 entry) */
498 3447 : uchar recent_hashes_enc[ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] = {0};
499 3447 : ulong rbh_cnt = 1UL;
500 3447 : memcpy( recent_hashes_enc, &rbh_cnt, sizeof(ulong) );
501 3447 : memcpy( recent_hashes_enc + sizeof(ulong), genesis_hash.uc, 32 );
502 : /* lamports_per_signature = 0 already zeroed */
503 :
504 : /* Slot hashes (empty) */
505 3447 : uchar slot_hashes_enc[ FD_SYSVAR_SLOT_HASHES_BINCODE_SZ ] = {0};
506 :
507 : /* Slot history -- well-formed bincode (large, heap alloc) */
508 3447 : uchar * slot_history_enc = calloc( 1, FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ );
509 3447 : FD_TEST( slot_history_enc );
510 3447 : ulong sh_blocks_len = FD_SLOT_HISTORY_MAX_ENTRIES / 64UL;
511 3447 : slot_history_enc[0] = 1; /* has_bits */
512 3447 : FD_STORE( ulong, slot_history_enc+1, sh_blocks_len );
513 3447 : uchar * sh_footer = slot_history_enc + 9UL + sh_blocks_len * sizeof(ulong);
514 3447 : FD_STORE( ulong, sh_footer, FD_SLOT_HISTORY_MAX_ENTRIES );
515 3447 : FD_STORE( ulong, sh_footer+8UL, bank->f.slot + 1UL );
516 :
517 : /* Stake history (empty) */
518 3447 : uchar stake_history_enc[ FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ] = {0};
519 :
520 3447 : struct { fd_pubkey_t const * addr; void const * data; ulong sz; } sysvars[] = {
521 3447 : { &fd_sysvar_clock_id, &clock, sizeof(clock) },
522 3447 : { &fd_sysvar_epoch_schedule_id, &bank->f.epoch_schedule, sizeof(bank->f.epoch_schedule) },
523 3447 : { &fd_sysvar_rent_id, &bank->f.rent, sizeof(bank->f.rent) },
524 3447 : { &fd_sysvar_last_restart_slot_id, last_restart_enc, sizeof(last_restart_enc) },
525 3447 : { &fd_sysvar_recent_block_hashes_id, recent_hashes_enc, sizeof(recent_hashes_enc) },
526 3447 : { &fd_sysvar_slot_hashes_id, slot_hashes_enc, sizeof(slot_hashes_enc) },
527 3447 : { &fd_sysvar_slot_history_id, slot_history_enc, FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ },
528 3447 : { &fd_sysvar_stake_history_id, stake_history_enc, sizeof(stake_history_enc) },
529 3447 : };
530 31023 : for( ulong i=0UL; i<8UL; i++ ) {
531 27576 : fd_account_meta_t meta = {
532 27576 : .lamports = fd_rent_exempt_minimum_balance( &bank->f.rent, sysvars[i].sz ),
533 27576 : .dlen = (uint)sysvars[i].sz,
534 27576 : };
535 27576 : memcpy( meta.owner, fd_sysvar_owner_id.uc, 32UL );
536 27576 : fd_accdb_ro_t ro[1];
537 27576 : fd_accdb_ro_init_nodb_oob( ro, sysvars[i].addr, &meta, sysvars[i].data );
538 27576 : fd_svm_mini_put_account_rooted( mini, ro );
539 27576 : }
540 :
541 3447 : free( slot_history_enc );
542 :
543 3447 : fd_sysvar_cache_restore( bank, mini->accdb, &root_xid );
544 3447 : }
545 :
546 3447 : if( params->mock_validator_cnt ) {
547 3438 : fd_svm_mini_init_mock_validators( mini, bank, params );
548 3438 : }
549 :
550 3447 : return bank_idx;
551 3447 : }
552 :
553 : ulong
554 : fd_svm_mini_attach_child( fd_svm_mini_t * mini,
555 : ulong parent_bank_idx,
556 3510 : ulong child_slot ) {
557 :
558 3510 : fd_bank_t * parent_bank = fd_banks_bank_query( mini->banks, parent_bank_idx );
559 3510 : if( FD_UNLIKELY( !parent_bank ) ) FD_LOG_ERR(( "invalid parent_bank_idx" ));
560 3510 : ulong parent_slot = parent_bank->f.slot;
561 3510 : if( FD_UNLIKELY( child_slot<=parent_slot ) ) FD_LOG_ERR(( "child_slot (%lu) <= parent_slot (%lu)", child_slot, parent_slot ));
562 3510 : fd_xid_t parent_xid = fd_bank_xid( parent_bank );
563 :
564 3510 : fd_bank_t * bank = fd_banks_new_bank( mini->banks, parent_bank_idx, 0L );
565 3510 : if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "fd_banks_new_bank failed" ));
566 3510 : bank = fd_banks_clone_from_parent( mini->banks, bank->idx );
567 3510 : if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "fd_banks_clone_from_parent failed" ));
568 3510 : ulong bank_idx = bank->idx;
569 3510 : bank->f.slot = child_slot;
570 3510 : fd_xid_t xid = fd_bank_xid( bank );
571 :
572 3510 : fd_accdb_attach_child( mini->accdb_admin, &parent_xid, &xid );
573 3510 : bank->progcache_fork_id = fd_progcache_attach_child( mini->progcache->join, parent_bank->progcache_fork_id );
574 :
575 3510 : int is_epoch_boundary = 0;
576 3510 : fd_runtime_block_execute_prepare( mini->banks, bank, mini->accdb, mini->runtime_stack, NULL, &is_epoch_boundary );
577 :
578 3510 : return bank_idx;
579 3510 : }
580 :
581 : void
582 : fd_svm_mini_freeze( fd_svm_mini_t * mini,
583 78 : ulong bank_idx ) {
584 78 : fd_bank_t * bank = fd_svm_mini_bank( mini, bank_idx );
585 78 : if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "invalid bank_idx" ));
586 : /* Derive a mock POH hash so each frozen slot registers a unique
587 : blockhash. (Real POH is computed by the PoH tile.) */
588 78 : fd_sha256_hash( bank->f.poh.hash, 32UL, bank->f.poh.hash );
589 78 : fd_runtime_block_execute_finalize( bank, mini->accdb, NULL );
590 78 : }
591 :
592 : void
593 : fd_svm_mini_cancel_fork( fd_svm_mini_t * mini,
594 0 : ulong bank_idx ) {
595 :
596 0 : fd_bank_t * bank = fd_svm_mini_bank( mini, bank_idx );
597 0 : if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "invalid bank_idx" ));
598 0 : fd_xid_t xid = fd_bank_xid( bank );
599 :
600 0 : fd_accdb_cancel ( mini->accdb_admin, &xid );
601 0 : fd_progcache_cancel_fork( mini->progcache->join, bank->progcache_fork_id );
602 0 : }
603 :
604 : void
605 : fd_svm_mini_advance_root( fd_svm_mini_t * mini,
606 24 : ulong bank_idx ) {
607 :
608 24 : fd_bank_t * bank = fd_banks_bank_query( mini->banks, bank_idx );
609 24 : if( FD_UNLIKELY( !bank ) ) FD_LOG_ERR(( "invalid bank_idx" ));
610 24 : fd_xid_t xid = fd_bank_xid( bank );
611 :
612 24 : fd_accdb_advance_root ( mini->accdb_admin, &xid );
613 24 : fd_progcache_advance_root( mini->progcache->join, bank->progcache_fork_id );
614 24 : fd_banks_advance_root ( mini->banks, bank_idx );
615 24 : }
616 :
617 : fd_bank_t *
618 : fd_svm_mini_bank( fd_svm_mini_t * mini,
619 6849 : ulong bank_idx ) {
620 6849 : if( FD_UNLIKELY( bank_idx>=fd_banks_pool_max_cnt( mini->banks ) ) ) return NULL;
621 6849 : fd_bank_t * bank = fd_banks_bank_query( mini->banks, bank_idx );
622 6849 : if( FD_UNLIKELY( !bank ) ) return NULL;
623 6840 : return bank;
624 6849 : }
625 :
626 : fd_xid_t
627 : fd_svm_mini_xid( fd_svm_mini_t * mini,
628 282 : ulong bank_idx ) {
629 282 : fd_bank_t * bank = fd_banks_bank_query( mini->banks, bank_idx );
630 282 : return fd_bank_xid( bank );
631 282 : }
632 :
633 : void
634 : fd_svm_mini_put_account_rooted( fd_svm_mini_t * mini,
635 83472 : fd_accdb_ro_t const * ro ) {
636 83472 : fd_funk_t * funk = fd_accdb_user_v1_funk( mini->accdb );
637 83472 : fd_funk_xid_key_pair_t pair = { .xid = {{ .ul = { ULONG_MAX, ULONG_MAX } }} };
638 83472 : memcpy( pair.key, ro->ref->address, sizeof(fd_funk_rec_key_t) );
639 83472 : fd_funk_rec_map_query_t query[1];
640 83472 : int remove_err = fd_funk_rec_map_remove( funk->rec_map, &pair, NULL, query, 0 );
641 83472 : FD_TEST( remove_err==FD_MAP_SUCCESS || remove_err==FD_MAP_ERR_KEY );
642 83472 : ulong old_lamports = 0UL;
643 83472 : if( remove_err==FD_MAP_SUCCESS ) {
644 57 : fd_funk_rec_t * old_rec = query->ele;
645 57 : fd_account_meta_t const * old_meta = fd_funk_val_const( old_rec, funk->wksp );
646 57 : if( old_meta ) old_lamports = old_meta->lamports;
647 57 : memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
648 57 : old_rec->map_next = FD_FUNK_REC_IDX_NULL;
649 57 : fd_funk_val_flush( old_rec, funk->alloc, funk->wksp );
650 57 : fd_funk_rec_pool_release( funk->rec_pool, old_rec );
651 57 : }
652 :
653 83472 : fd_accdb_rw_t rw[1];
654 83472 : FD_TEST( fd_accdb_funk_create( funk, rw, NULL, ro->ref->address, fd_accdb_ref_data_sz( ro ) ) );
655 83472 : fd_accdb_ref_lamports_set( rw, fd_accdb_ref_lamports( ro ) );
656 83472 : fd_accdb_ref_owner_set ( rw, fd_accdb_ref_owner ( ro ) );
657 83472 : fd_accdb_ref_exec_bit_set( rw, fd_accdb_ref_exec_bit( ro ) );
658 83472 : fd_accdb_ref_slot_set ( rw, fd_accdb_ref_slot ( ro ) );
659 83472 : fd_accdb_ref_data_set( mini->accdb, rw, fd_accdb_ref_data_const( ro ), fd_accdb_ref_data_sz( ro ) );
660 83472 : fd_funk_rec_t * rec = (fd_funk_rec_t *)rw->ref->user_data;
661 83472 : fd_funk_rec_prepare_t prepare = { .rec = rec, .rec_head_idx = NULL, .rec_tail_idx = NULL };
662 83472 : fd_funk_rec_publish( funk, &prepare );
663 83472 : memset( rw, 0, sizeof(fd_accdb_rw_t) );
664 :
665 83472 : fd_bank_t * root = fd_banks_root( mini->banks );
666 83472 : if( root ) {
667 83472 : ulong new_lamports = fd_accdb_ref_lamports( ro );
668 83472 : if( new_lamports >= old_lamports )
669 83469 : root->f.capitalization += new_lamports - old_lamports;
670 3 : else
671 3 : root->f.capitalization -= old_lamports - new_lamports;
672 83472 : }
673 83472 : }
674 :
675 : void
676 : fd_svm_mini_add_lamports_rooted( fd_svm_mini_t * mini,
677 : fd_pubkey_t const * pubkey,
678 3444 : ulong lamports ) {
679 3444 : fd_funk_t * funk = fd_accdb_user_v1_funk( mini->accdb );
680 3444 : fd_funk_xid_key_pair_t pair = { .xid = {{ .ul = { ULONG_MAX, ULONG_MAX } }} };
681 3444 : memcpy( pair.key, pubkey, sizeof(fd_funk_rec_key_t) );
682 3444 : fd_funk_rec_map_query_t query[1];
683 3444 : int query_err = fd_funk_rec_map_query_try( funk->rec_map, &pair, NULL, query, 0 );
684 3444 : FD_TEST( query_err==FD_MAP_SUCCESS || query_err==FD_MAP_ERR_KEY );
685 3444 : if( query_err==FD_MAP_SUCCESS ) {
686 0 : fd_funk_rec_t * rec = fd_funk_rec_map_query_ele( query );
687 0 : fd_account_meta_t * meta = fd_funk_val( rec, funk->wksp );
688 0 : ulong balance = meta->lamports;
689 0 : FD_TEST( !__builtin_uaddl_overflow( balance, lamports, &balance ) );
690 0 : meta->lamports = balance;
691 0 : FD_TEST( !fd_funk_rec_map_query_test( query ) );
692 3444 : } else if( query_err==FD_MAP_ERR_KEY ) {
693 3444 : fd_accdb_rw_t rw[1];
694 3444 : FD_TEST( fd_accdb_funk_create( funk, rw, NULL, pubkey, 0UL ) );
695 3444 : fd_accdb_ref_lamports_set( rw, lamports );
696 3444 : fd_funk_rec_t * rec = (fd_funk_rec_t *)rw->ref->user_data;
697 3444 : fd_funk_rec_prepare_t prepare = { .rec = rec, .rec_head_idx = NULL, .rec_tail_idx = NULL };
698 3444 : fd_funk_rec_publish( funk, &prepare );
699 3444 : memset( rw, 0, sizeof(fd_accdb_rw_t) );
700 3444 : } else {
701 0 : FD_LOG_ERR(( "fd_funk_rec_map_query_try failed (%i-%s)", query_err, fd_map_strerror( query_err ) ));
702 0 : }
703 :
704 3444 : fd_bank_t * root = fd_banks_root( mini->banks );
705 3444 : if( root ) root->f.capitalization += lamports;
706 3444 : }
707 :
708 : void
709 : fd_svm_mini_add_lamports( fd_svm_mini_t * mini,
710 : fd_xid_t const * xid,
711 : fd_pubkey_t const * pubkey,
712 45 : ulong lamports ) {
713 45 : fd_accdb_user_t * accdb = mini->accdb;
714 :
715 45 : fd_accdb_rw_t rw[1];
716 45 : FD_TEST( fd_accdb_open_rw( accdb, rw, xid, pubkey, 0UL, FD_ACCDB_FLAG_CREATE ) );
717 45 : ulong balance = fd_accdb_ref_lamports( rw->ro );
718 45 : FD_TEST( !__builtin_uaddl_overflow( balance, lamports, &balance ) );
719 45 : fd_accdb_ref_lamports_set( rw, balance );
720 45 : fd_accdb_close_rw( accdb, rw );
721 :
722 45 : fd_bank_t * bank = fd_svm_mini_bank( mini, xid->ul[1] );
723 45 : if( bank ) bank->f.capitalization += lamports;
724 45 : }
|