Line data Source code
1 : #include "fd_runtime.h"
2 : #include "fd_acc_mgr.h"
3 : #include "fd_runtime_err.h"
4 : #include "fd_runtime_init.h"
5 :
6 : #include "fd_executor.h"
7 : #include "fd_account.h"
8 : #include "fd_hashes.h"
9 : #include "sysvar/fd_sysvar_cache.h"
10 : #include "sysvar/fd_sysvar_clock.h"
11 : #include "sysvar/fd_sysvar_epoch_schedule.h"
12 : #include "sysvar/fd_sysvar_recent_hashes.h"
13 : #include "sysvar/fd_sysvar_stake_history.h"
14 : #include "sysvar/fd_sysvar.h"
15 : #include "../../ballet/base58/fd_base58.h"
16 : #include "../../ballet/txn/fd_txn.h"
17 : #include "../../ballet/bmtree/fd_bmtree.h"
18 : #include "../../ballet/bmtree/fd_wbmtree.h"
19 :
20 : #include "../stakes/fd_stakes.h"
21 : #include "../rewards/fd_rewards.h"
22 :
23 : #include "context/fd_exec_txn_ctx.h"
24 : #include "context/fd_exec_instr_ctx.h"
25 : #include "info/fd_microblock_batch_info.h"
26 : #include "info/fd_microblock_info.h"
27 :
28 : #include "program/fd_stake_program.h"
29 : #include "program/fd_builtin_programs.h"
30 : #include "program/fd_system_program.h"
31 : #include "program/fd_vote_program.h"
32 : #include "program/fd_bpf_program_util.h"
33 : #include "program/fd_bpf_loader_program.h"
34 : #include "program/fd_compute_budget_program.h"
35 :
36 : #include "sysvar/fd_sysvar_clock.h"
37 : #include "sysvar/fd_sysvar_fees.h"
38 : #include "sysvar/fd_sysvar_last_restart_slot.h"
39 : #include "sysvar/fd_sysvar_recent_hashes.h"
40 : #include "sysvar/fd_sysvar_rent.h"
41 : #include "sysvar/fd_sysvar_slot_hashes.h"
42 : #include "sysvar/fd_sysvar_slot_history.h"
43 :
44 : #include "../nanopb/pb_decode.h"
45 : #include "../nanopb/pb_encode.h"
46 : #include "../types/fd_solana_block.pb.h"
47 :
48 : #include "fd_system_ids.h"
49 : #include "../vm/fd_vm.h"
50 : #include "fd_blockstore.h"
51 : #include "../../ballet/pack/fd_pack.h"
52 : #include "../fd_rwlock.h"
53 :
54 : #include <stdio.h>
55 : #include <ctype.h>
56 : #include <unistd.h>
57 : #include <sys/stat.h>
58 : #include <sys/types.h>
59 : #include <errno.h>
60 : #include <fcntl.h>
61 :
62 6156 : #define MICRO_LAMPORTS_PER_LAMPORT (1000000UL)
63 :
64 : void
65 : fd_runtime_init_bank_from_genesis( fd_exec_slot_ctx_t * slot_ctx,
66 : fd_genesis_solana_t * genesis_block,
67 0 : fd_hash_t const * genesis_hash ) {
68 0 : slot_ctx->slot_bank.slot = 0;
69 :
70 0 : memcpy(&slot_ctx->slot_bank.poh, genesis_hash->hash, FD_SHA256_HASH_SZ);
71 0 : memset(slot_ctx->slot_bank.banks_hash.hash, 0, FD_SHA256_HASH_SZ);
72 :
73 0 : slot_ctx->slot_bank.fee_rate_governor = genesis_block->fee_rate_governor;
74 0 : slot_ctx->slot_bank.lamports_per_signature = 0UL;
75 0 : slot_ctx->prev_lamports_per_signature = 0UL;
76 :
77 0 : fd_poh_config_t *poh = &genesis_block->poh_config;
78 0 : fd_exec_epoch_ctx_t * epoch_ctx = slot_ctx->epoch_ctx;
79 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( epoch_ctx );
80 0 : if (poh->has_hashes_per_tick)
81 0 : epoch_bank->hashes_per_tick = poh->hashes_per_tick;
82 0 : else
83 0 : epoch_bank->hashes_per_tick = 0;
84 0 : epoch_bank->ticks_per_slot = genesis_block->ticks_per_slot;
85 0 : epoch_bank->genesis_creation_time = genesis_block->creation_time;
86 0 : uint128 target_tick_duration = ((uint128)poh->target_tick_duration.seconds * 1000000000UL + (uint128)poh->target_tick_duration.nanoseconds);
87 0 : epoch_bank->ns_per_slot = target_tick_duration * epoch_bank->ticks_per_slot;
88 :
89 0 : epoch_bank->slots_per_year = SECONDS_PER_YEAR * (1000000000.0 / (double)target_tick_duration) / (double)epoch_bank->ticks_per_slot;
90 0 : epoch_bank->genesis_creation_time = genesis_block->creation_time;
91 0 : slot_ctx->slot_bank.max_tick_height = epoch_bank->ticks_per_slot * (slot_ctx->slot_bank.slot + 1);
92 0 : epoch_bank->epoch_schedule = genesis_block->epoch_schedule;
93 0 : epoch_bank->inflation = genesis_block->inflation;
94 0 : epoch_bank->rent = genesis_block->rent;
95 0 : slot_ctx->slot_bank.block_height = 0UL;
96 :
97 0 : fd_block_block_hash_entry_t *hashes = slot_ctx->slot_bank.recent_block_hashes.hashes =
98 0 : deq_fd_block_block_hash_entry_t_alloc( slot_ctx->valloc, FD_SYSVAR_RECENT_HASHES_CAP );
99 0 : fd_block_block_hash_entry_t *elem = deq_fd_block_block_hash_entry_t_push_head_nocopy(hashes);
100 0 : fd_block_block_hash_entry_new(elem);
101 0 : fd_memcpy(elem->blockhash.hash, genesis_hash, FD_SHA256_HASH_SZ);
102 0 : elem->fee_calculator.lamports_per_signature = 0UL;
103 :
104 0 : slot_ctx->slot_bank.block_hash_queue.ages_root = NULL;
105 0 : slot_ctx->slot_bank.block_hash_queue.ages_pool = fd_hash_hash_age_pair_t_map_alloc( slot_ctx->valloc, 400 );
106 0 : fd_hash_hash_age_pair_t_mapnode_t * node = fd_hash_hash_age_pair_t_map_acquire( slot_ctx->slot_bank.block_hash_queue.ages_pool );
107 0 : node->elem = (fd_hash_hash_age_pair_t){
108 0 : .key = *genesis_hash,
109 0 : .val = (fd_hash_age_t){ .hash_index = 0, .fee_calculator = (fd_fee_calculator_t){.lamports_per_signature = 0UL}, .timestamp = (ulong)fd_log_wallclock() }
110 0 : };
111 0 : fd_hash_hash_age_pair_t_map_insert( slot_ctx->slot_bank.block_hash_queue.ages_pool, &slot_ctx->slot_bank.block_hash_queue.ages_root, node );
112 0 : slot_ctx->slot_bank.block_hash_queue.last_hash_index = 0;
113 0 : slot_ctx->slot_bank.block_hash_queue.last_hash = fd_valloc_malloc( slot_ctx->valloc, FD_HASH_ALIGN, FD_HASH_FOOTPRINT );
114 0 : fd_memcpy( slot_ctx->slot_bank.block_hash_queue.last_hash, genesis_hash, FD_SHA256_HASH_SZ );
115 0 : slot_ctx->slot_bank.block_hash_queue.max_age = FD_BLOCKHASH_QUEUE_MAX_ENTRIES;
116 :
117 0 : slot_ctx->signature_cnt = 0;
118 :
119 : /* Derive epoch stakes */
120 :
121 0 : fd_vote_accounts_pair_t_mapnode_t * vacc_pool = fd_exec_epoch_ctx_stake_votes_join( epoch_ctx );
122 :
123 0 : FD_TEST(vacc_pool);
124 0 : fd_vote_accounts_pair_t_mapnode_t * vacc_root = NULL;
125 :
126 0 : fd_delegation_pair_t_mapnode_t * sacc_pool = fd_exec_epoch_ctx_stake_delegations_join( epoch_ctx );
127 0 : fd_delegation_pair_t_mapnode_t * sacc_root = NULL;
128 :
129 0 : fd_stake_history_treap_t * stake_history_treap = fd_exec_epoch_ctx_stake_history_treap_join( epoch_ctx );
130 0 : fd_stake_history_entry_t * stake_history_pool = fd_exec_epoch_ctx_stake_history_pool_join ( epoch_ctx );
131 :
132 0 : fd_acc_lamports_t capitalization = 0UL;
133 :
134 0 : for (ulong i = 0UL; i < genesis_block->accounts_len; i++) {
135 0 : fd_pubkey_account_pair_t const *acc = &genesis_block->accounts[i];
136 0 : capitalization = fd_ulong_sat_add(capitalization, acc->account.lamports);
137 :
138 0 : if (0 == memcmp(acc->account.owner.key, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t))) {
139 : /* Vote Program Account */
140 :
141 0 : fd_vote_accounts_pair_t_mapnode_t *node =
142 0 : fd_vote_accounts_pair_t_map_acquire(vacc_pool);
143 0 : FD_TEST(node);
144 :
145 0 : fd_vote_block_timestamp_t last_timestamp;
146 0 : fd_pubkey_t node_pubkey;
147 0 : FD_SCRATCH_SCOPE_BEGIN {
148 : /* Deserialize content */
149 0 : fd_vote_state_versioned_t vs[1];
150 0 : fd_bincode_decode_ctx_t decode =
151 0 : { .data = acc->account.data,
152 0 : .dataend = acc->account.data + acc->account.data_len,
153 0 : .valloc = fd_scratch_virtual() };
154 0 : int decode_err = fd_vote_state_versioned_decode( vs, &decode );
155 0 : if( FD_UNLIKELY( decode_err!=FD_BINCODE_SUCCESS ) ) {
156 0 : FD_LOG_WARNING(( "fd_vote_state_versioned_decode failed (%d)", decode_err ));
157 0 : return;
158 0 : }
159 :
160 0 : switch( vs->discriminant )
161 0 : {
162 0 : case fd_vote_state_versioned_enum_current:
163 0 : last_timestamp = vs->inner.current.last_timestamp;
164 0 : node_pubkey = vs->inner.current.node_pubkey;
165 0 : break;
166 0 : case fd_vote_state_versioned_enum_v0_23_5:
167 0 : last_timestamp = vs->inner.v0_23_5.last_timestamp;
168 0 : node_pubkey = vs->inner.v0_23_5.node_pubkey;
169 0 : break;
170 0 : case fd_vote_state_versioned_enum_v1_14_11:
171 0 : last_timestamp = vs->inner.v1_14_11.last_timestamp;
172 0 : node_pubkey = vs->inner.v1_14_11.node_pubkey;
173 0 : break;
174 0 : default:
175 0 : __builtin_unreachable();
176 0 : }
177 :
178 0 : } FD_SCRATCH_SCOPE_END;
179 :
180 0 : fd_memcpy(node->elem.key.key, acc->key.key, sizeof(fd_pubkey_t));
181 0 : node->elem.stake = acc->account.lamports;
182 0 : node->elem.value = (fd_solana_vote_account_t){
183 0 : .lamports = acc->account.lamports,
184 0 : .node_pubkey = node_pubkey,
185 0 : .last_timestamp_ts = last_timestamp.timestamp,
186 0 : .last_timestamp_slot = last_timestamp.slot,
187 0 : .owner = acc->account.owner,
188 0 : .executable = acc->account.executable,
189 0 : .rent_epoch = acc->account.rent_epoch};
190 :
191 0 : fd_vote_accounts_pair_t_map_insert(vacc_pool, &vacc_root, node);
192 :
193 0 : FD_LOG_INFO(( "Adding genesis vote account: key=%s stake=%lu",
194 0 : FD_BASE58_ENC_32_ALLOCA( node->elem.key.key ),
195 0 : node->elem.stake ));
196 0 : } else if (0 == memcmp(acc->account.owner.key, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t))) {
197 : /* stake program account */
198 0 : fd_stake_state_v2_t stake_state = {0};
199 :
200 0 : fd_account_meta_t meta = {.dlen = acc->account.data_len};
201 0 : fd_borrowed_account_t stake_account = {
202 0 : .const_data = acc->account.data,
203 0 : .const_meta = &meta,
204 0 : .data = acc->account.data,
205 0 : .meta = &meta};
206 0 : FD_TEST(fd_stake_get_state(&stake_account, &slot_ctx->valloc, &stake_state) == 0);
207 0 : if( stake_state.inner.stake.stake.delegation.stake == 0 ) continue;
208 0 : fd_delegation_pair_t_mapnode_t query_node;
209 0 : fd_memcpy(&query_node.elem.account, acc->key.key, sizeof(fd_pubkey_t));
210 0 : fd_delegation_pair_t_mapnode_t *node = fd_delegation_pair_t_map_find(sacc_pool, sacc_root, &query_node);
211 :
212 0 : if (node == NULL) {
213 0 : node = fd_delegation_pair_t_map_acquire(sacc_pool);
214 0 : fd_memcpy(&node->elem.account, acc->key.key, sizeof(fd_pubkey_t));
215 0 : fd_memcpy(&node->elem.delegation, &stake_state.inner.stake.stake.delegation, sizeof(fd_delegation_t));
216 0 : fd_delegation_pair_t_map_insert(sacc_pool, &sacc_root, node);
217 0 : } else {
218 0 : fd_memcpy(&node->elem.account, acc->key.key, sizeof(fd_pubkey_t));
219 0 : fd_memcpy(&node->elem.delegation, &stake_state.inner.stake.stake.delegation, sizeof(fd_delegation_t));
220 0 : }
221 0 : } else if (0 == memcmp(acc->account.owner.key, fd_solana_feature_program_id.key, sizeof(fd_pubkey_t))) {
222 : /* Feature Account */
223 :
224 : /* Scan list of feature IDs to resolve address => feature offset */
225 0 : fd_feature_id_t const *found = NULL;
226 0 : for (fd_feature_id_t const *id = fd_feature_iter_init();
227 0 : !fd_feature_iter_done(id);
228 0 : id = fd_feature_iter_next(id)) {
229 0 : if (0 == memcmp(acc->key.key, id->id.key, sizeof(fd_pubkey_t)))
230 0 : {
231 0 : found = id;
232 0 : break;
233 0 : }
234 0 : }
235 :
236 0 : if (found) {
237 : /* Load feature activation */
238 0 : FD_SCRATCH_SCOPE_BEGIN {
239 0 : fd_bincode_decode_ctx_t decode = {.data = acc->account.data,
240 0 : .dataend = acc->account.data + acc->account.data_len,
241 0 : .valloc = fd_scratch_virtual()};
242 0 : fd_feature_t feature;
243 0 : int err = fd_feature_decode( &feature, &decode );
244 0 : FD_TEST( err==FD_BINCODE_SUCCESS );
245 0 : if( feature.has_activated_at ) {
246 0 : FD_LOG_DEBUG(( "Feature %s activated at %lu (genesis)", FD_BASE58_ENC_32_ALLOCA( acc->key.key ), feature.activated_at ));
247 0 : fd_features_set( &slot_ctx->epoch_ctx->features, found, feature.activated_at);
248 0 : } else {
249 0 : FD_LOG_DEBUG(( "Feature %s not activated (genesis)", FD_BASE58_ENC_32_ALLOCA( acc->key.key ) ));
250 0 : fd_features_set( &slot_ctx->epoch_ctx->features, found, ULONG_MAX);
251 0 : }
252 0 : } FD_SCRATCH_SCOPE_END;
253 0 : }
254 0 : }
255 0 : }
256 :
257 0 : slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool = fd_vote_accounts_pair_t_map_alloc( slot_ctx->valloc, 100000 ); /* FIXME remove magic constant */
258 0 : slot_ctx->slot_bank.epoch_stakes.vote_accounts_root = NULL;
259 :
260 0 : fd_vote_accounts_pair_t_mapnode_t * next_pool = fd_exec_epoch_ctx_next_epoch_stakes_join( slot_ctx->epoch_ctx );
261 0 : fd_vote_accounts_pair_t_mapnode_t * next_root = NULL;
262 :
263 0 : for( fd_vote_accounts_pair_t_mapnode_t *n = fd_vote_accounts_pair_t_map_minimum( vacc_pool, vacc_root );
264 0 : n;
265 0 : n = fd_vote_accounts_pair_t_map_successor( vacc_pool, n )) {
266 0 : fd_vote_accounts_pair_t_mapnode_t * e = fd_vote_accounts_pair_t_map_acquire( slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool );
267 0 : fd_memcpy( &e->elem, &n->elem, sizeof(fd_vote_accounts_pair_t));
268 0 : fd_vote_accounts_pair_t_map_insert( slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool, &slot_ctx->slot_bank.epoch_stakes.vote_accounts_root, e );
269 :
270 0 : fd_vote_accounts_pair_t_mapnode_t * next_e = fd_vote_accounts_pair_t_map_acquire( next_pool );
271 0 : fd_memcpy( &next_e->elem, &n->elem, sizeof(fd_vote_accounts_pair_t));
272 0 : fd_vote_accounts_pair_t_map_insert( next_pool, &next_root, next_e );
273 0 : }
274 :
275 0 : for( fd_delegation_pair_t_mapnode_t *n = fd_delegation_pair_t_map_minimum( sacc_pool, sacc_root );
276 0 : n;
277 0 : n = fd_delegation_pair_t_map_successor( sacc_pool, n )) {
278 0 : fd_vote_accounts_pair_t_mapnode_t query_voter;
279 0 : fd_pubkey_t *voter_pubkey = &n->elem.delegation.voter_pubkey;
280 0 : fd_memcpy(&query_voter.elem.key, voter_pubkey, sizeof(fd_pubkey_t));
281 :
282 0 : fd_vote_accounts_pair_t_mapnode_t *voter = fd_vote_accounts_pair_t_map_find(vacc_pool, vacc_root, &query_voter);
283 :
284 0 : if (voter != NULL)
285 0 : voter->elem.stake = fd_ulong_sat_add(voter->elem.stake, n->elem.delegation.stake);
286 0 : }
287 :
288 0 : epoch_bank->next_epoch_stakes = (fd_vote_accounts_t){
289 0 : .vote_accounts_pool = next_pool,
290 0 : .vote_accounts_root = next_root,
291 0 : };
292 :
293 : /* Initializes the stakes cache in the Bank structure. */
294 0 : epoch_bank->stakes = (fd_stakes_t){
295 0 : .stake_delegations_pool = sacc_pool,
296 0 : .stake_delegations_root = sacc_root,
297 0 : .epoch = 0,
298 0 : .unused = 0,
299 0 : .vote_accounts = (fd_vote_accounts_t){
300 0 : .vote_accounts_pool = vacc_pool,
301 0 : .vote_accounts_root = vacc_root},
302 0 : .stake_history = (fd_stake_history_t){.pool = stake_history_pool, .treap = stake_history_treap}};
303 :
304 0 : slot_ctx->slot_bank.capitalization = capitalization;
305 :
306 0 : slot_ctx->slot_bank.timestamp_votes.votes_pool =
307 0 : fd_clock_timestamp_vote_t_map_alloc( slot_ctx->valloc, 10000 ); /* FIXME: remove magic constant */
308 0 : slot_ctx->slot_bank.timestamp_votes.votes_root = NULL;
309 :
310 0 : }
311 :
312 : void fd_runtime_init_program(fd_exec_slot_ctx_t *slot_ctx)
313 0 : {
314 0 : fd_sysvar_recent_hashes_init(slot_ctx);
315 0 : fd_sysvar_clock_init(slot_ctx);
316 0 : fd_sysvar_slot_history_init(slot_ctx);
317 : // fd_sysvar_slot_hashes_init( slot_ctx );
318 0 : fd_sysvar_epoch_schedule_init(slot_ctx);
319 0 : if( !FD_FEATURE_ACTIVE(slot_ctx, disable_fees_sysvar) ) {
320 0 : fd_sysvar_fees_init(slot_ctx);
321 0 : }
322 0 : fd_sysvar_rent_init(slot_ctx);
323 0 : fd_sysvar_stake_history_init(slot_ctx);
324 0 : fd_sysvar_last_restart_slot_init(slot_ctx);
325 :
326 0 : fd_builtin_programs_init(slot_ctx);
327 0 : fd_stake_program_config_init(slot_ctx);
328 0 : }
329 :
330 : int fd_runtime_parse_microblock_hdr(void const *buf,
331 : ulong buf_sz,
332 : fd_microblock_hdr_t *opt_microblock_hdr,
333 : ulong *opt_microblock_hdr_size)
334 0 : {
335 0 : if (buf_sz < sizeof(fd_microblock_hdr_t))
336 0 : {
337 0 : return -1;
338 0 : }
339 :
340 0 : if (opt_microblock_hdr != NULL)
341 0 : {
342 0 : *opt_microblock_hdr = *(fd_microblock_hdr_t *)buf;
343 0 : }
344 :
345 0 : if (opt_microblock_hdr_size != NULL)
346 0 : {
347 0 : *opt_microblock_hdr_size = sizeof(fd_microblock_hdr_t);
348 0 : }
349 :
350 0 : return 0;
351 0 : }
352 :
353 : int fd_runtime_parse_microblock_txns( void const * buf,
354 : ulong buf_sz,
355 : fd_microblock_hdr_t const * microblock_hdr,
356 : fd_txn_p_t * out_txns,
357 : ulong * out_signature_cnt,
358 : ulong * out_account_cnt,
359 0 : ulong * out_microblock_txns_sz ) {
360 0 : ulong buf_off = 0;
361 0 : ulong signature_cnt = 0;
362 0 : ulong account_cnt = 0;
363 :
364 0 : for (ulong i = 0; i < microblock_hdr->txn_cnt; i++) {
365 0 : ulong payload_sz = 0;
366 0 : ulong txn_sz = fd_txn_parse_core( (uchar const *)buf + buf_off, fd_ulong_min( buf_sz-buf_off, FD_TXN_MTU), TXN(&out_txns[i]), NULL, &payload_sz );
367 0 : if (txn_sz == 0 || txn_sz > FD_TXN_MTU) {
368 0 : return -1;
369 0 : }
370 :
371 0 : fd_memcpy( out_txns[i].payload, (uchar *)buf + buf_off, payload_sz );
372 0 : out_txns[i].payload_sz = (ushort)payload_sz;
373 :
374 0 : signature_cnt += TXN(&out_txns[i])->signature_cnt;
375 0 : account_cnt += fd_txn_account_cnt( TXN(&out_txns[i]), FD_TXN_ACCT_CAT_ALL );
376 0 : buf_off += payload_sz;
377 0 : }
378 :
379 0 : *out_signature_cnt = signature_cnt;
380 0 : *out_account_cnt = account_cnt;
381 0 : *out_microblock_txns_sz = buf_off;
382 :
383 0 : return 0;
384 0 : }
385 :
386 : int fd_runtime_microblock_prepare(void const *buf,
387 : ulong buf_sz,
388 : fd_valloc_t valloc,
389 0 : fd_microblock_info_t *out_microblock_info) {
390 0 : fd_microblock_info_t microblock_info = {
391 0 : .raw_microblock = buf,
392 0 : .signature_cnt = 0,
393 0 : };
394 0 : ulong buf_off = 0;
395 :
396 0 : ulong hdr_sz = 0;
397 0 : if (fd_runtime_parse_microblock_hdr(buf, buf_sz, µblock_info.microblock_hdr, &hdr_sz) != 0)
398 0 : {
399 0 : return -1;
400 0 : }
401 0 : buf_off += hdr_sz;
402 :
403 0 : ulong txn_cnt = microblock_info.microblock_hdr.txn_cnt;
404 0 : microblock_info.txns = fd_valloc_malloc(valloc, alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t));
405 :
406 0 : ulong txns_sz = 0;
407 0 : if (fd_runtime_parse_microblock_txns((uchar *)buf + buf_off,
408 0 : buf_sz - buf_off,
409 0 : µblock_info.microblock_hdr,
410 0 : microblock_info.txns,
411 0 : µblock_info.signature_cnt,
412 0 : µblock_info.account_cnt,
413 0 : &txns_sz) != 0)
414 0 : {
415 0 : fd_valloc_free(valloc, microblock_info.txns);
416 0 : return -1;
417 0 : }
418 0 : buf_off += txns_sz;
419 :
420 0 : microblock_info.raw_microblock_sz = buf_off;
421 :
422 0 : *out_microblock_info = microblock_info;
423 :
424 0 : return 0;
425 0 : }
426 :
427 : int fd_runtime_microblock_batch_prepare(void const *buf,
428 : ulong buf_sz,
429 : fd_valloc_t valloc,
430 0 : fd_microblock_batch_info_t * out_microblock_batch_info) {
431 0 : fd_microblock_batch_info_t microblock_batch_info = {
432 0 : .raw_microblock_batch = buf,
433 0 : .signature_cnt = 0,
434 0 : .txn_cnt = 0,
435 0 : .account_cnt = 0,
436 0 : };
437 0 : ulong buf_off = 0;
438 :
439 0 : if (FD_UNLIKELY(buf_sz < sizeof(ulong)))
440 0 : {
441 0 : FD_LOG_WARNING(("microblock batch buffer too small"));
442 0 : return -1;
443 0 : }
444 0 : ulong microblock_cnt = FD_LOAD(ulong, buf);
445 0 : buf_off += sizeof(ulong);
446 :
447 0 : microblock_batch_info.microblock_cnt = microblock_cnt;
448 0 : microblock_batch_info.microblock_infos = fd_valloc_malloc(valloc, alignof(fd_microblock_info_t), microblock_cnt * sizeof(fd_microblock_info_t));
449 :
450 0 : ulong signature_cnt = 0;
451 0 : ulong txn_cnt = 0;
452 0 : ulong account_cnt = 0;
453 0 : for (ulong i = 0; i < microblock_cnt; i++) {
454 0 : fd_microblock_info_t * microblock_info = µblock_batch_info.microblock_infos[i];
455 0 : if (fd_runtime_microblock_prepare((uchar const *)buf + buf_off, buf_sz - buf_off, valloc, microblock_info) != 0)
456 0 : {
457 0 : fd_valloc_free(valloc, microblock_batch_info.microblock_infos);
458 0 : return -1;
459 0 : }
460 :
461 0 : signature_cnt += microblock_info->signature_cnt;
462 0 : txn_cnt += microblock_info->microblock_hdr.txn_cnt;
463 0 : account_cnt += microblock_info->account_cnt;
464 0 : buf_off += microblock_info->raw_microblock_sz;
465 0 : }
466 :
467 0 : microblock_batch_info.signature_cnt = signature_cnt;
468 0 : microblock_batch_info.txn_cnt = txn_cnt;
469 0 : microblock_batch_info.account_cnt = account_cnt;
470 0 : microblock_batch_info.raw_microblock_batch_sz = buf_off;
471 :
472 0 : *out_microblock_batch_info = microblock_batch_info;
473 :
474 0 : return 0;
475 0 : }
476 :
477 : // static void dump_iter( fd_raw_block_txn_iter_t iter ) {
478 : // FD_LOG_WARNING(( "Curr iter data sz %lu offset %lu num txns %lu num mblks %lu curr txn sz %lu", iter.data_sz, iter.curr_offset, iter.remaining_txns, iter.remaining_microblocks, iter.curr_txn_sz ));
479 : // }
480 :
481 : static fd_raw_block_txn_iter_t
482 0 : find_next_txn_in_raw_block( uchar const * data, ulong data_sz, ulong existing_offset, ulong num_microblocks ) {
483 0 : uchar const * base = data;
484 0 : ulong num_txns = 0UL;
485 0 : ulong sz = (ulong)data - (ulong)base;
486 0 : while( !num_txns && (sz < data_sz) ) {
487 0 : while( num_microblocks == 0 && (sz < data_sz) ) {
488 0 : num_microblocks = FD_LOAD( ulong, data );
489 0 : data += sizeof( ulong );
490 0 : sz = (ulong)data - (ulong)base;
491 0 : }
492 :
493 0 : fd_microblock_info_t microblock_info = {
494 0 : .raw_microblock = data,
495 0 : .signature_cnt = 0,
496 0 : };
497 :
498 0 : while( microblock_info.microblock_hdr.txn_cnt == 0 && num_microblocks && sz < data_sz ) {
499 0 : ulong hdr_sz = 0;
500 0 : memset( µblock_info, 0UL, sizeof(fd_microblock_info_t) );
501 0 : microblock_info.raw_microblock = data;
502 0 : if (fd_runtime_parse_microblock_hdr(data, data_sz - sz, µblock_info.microblock_hdr, &hdr_sz) != 0) {
503 0 : return (fd_raw_block_txn_iter_t){
504 0 : .data_sz = 0,
505 0 : .curr_offset = data_sz,
506 0 : .remaining_microblocks = 0,
507 0 : .remaining_txns = 0,
508 0 : .curr_txn_sz = ULONG_MAX
509 0 : };
510 0 : }
511 0 : data += hdr_sz;
512 0 : sz = (ulong)data - (ulong)base;
513 0 : num_microblocks--;
514 0 : }
515 :
516 0 : num_txns = microblock_info.microblock_hdr.txn_cnt;
517 0 : }
518 :
519 0 : ulong curr_off = sz;
520 0 : return (fd_raw_block_txn_iter_t){
521 0 : .data_sz = fd_ulong_sat_sub(data_sz, curr_off),
522 0 : .curr_offset = existing_offset + curr_off,
523 0 : .remaining_microblocks = num_microblocks,
524 0 : .remaining_txns = num_txns,
525 0 : .curr_txn_sz = ULONG_MAX
526 0 : };
527 0 : }
528 :
529 : fd_raw_block_txn_iter_t
530 0 : fd_raw_block_txn_iter_init( uchar const * data, ulong data_sz ) {
531 0 : return find_next_txn_in_raw_block( data, data_sz, 0, 0 );
532 0 : }
533 :
534 : ulong
535 0 : fd_raw_block_txn_iter_done( fd_raw_block_txn_iter_t iter ) {
536 0 : return iter.data_sz == 0;
537 0 : }
538 :
539 : fd_raw_block_txn_iter_t
540 0 : fd_raw_block_txn_iter_next( uchar const * data, fd_raw_block_txn_iter_t iter ) {
541 0 : fd_txn_p_t out_txn;
542 0 : if( iter.curr_txn_sz == ULONG_MAX ) {
543 0 : ulong payload_sz = 0;
544 0 : ulong txn_sz = fd_txn_parse_core( data + iter.curr_offset, fd_ulong_min( iter.data_sz, FD_TXN_MTU), TXN(&out_txn), NULL, &payload_sz );
545 0 : if (txn_sz == 0 || txn_sz > FD_TXN_MTU) {
546 0 : FD_LOG_ERR(("Invalid txn parse"));
547 0 : }
548 0 : iter.data_sz -= payload_sz;
549 0 : iter.curr_offset += payload_sz;
550 0 : } else {
551 0 : iter.data_sz -= iter.curr_txn_sz;
552 0 : iter.curr_offset += iter.curr_txn_sz;
553 0 : iter.curr_txn_sz = ULONG_MAX;
554 0 : }
555 :
556 0 : if( --iter.remaining_txns ) {
557 0 : return iter;
558 0 : }
559 :
560 0 : return find_next_txn_in_raw_block( data + iter.curr_offset, iter.data_sz, iter.curr_offset, iter.remaining_microblocks );
561 0 : }
562 :
563 : void
564 0 : fd_raw_block_txn_iter_ele( uchar const * data, fd_raw_block_txn_iter_t iter, fd_txn_p_t * out_txn ) {
565 0 : ulong payload_sz = 0;
566 0 : ulong txn_sz = fd_txn_parse_core( data + iter.curr_offset, fd_ulong_min( iter.data_sz, FD_TXN_MTU), TXN(out_txn), NULL, &payload_sz );
567 0 : if (txn_sz == 0 || txn_sz > FD_TXN_MTU) {
568 0 : FD_LOG_ERR(("Invalid txn parse %lu", txn_sz));
569 0 : }
570 0 : fd_memcpy( out_txn->payload, data + iter.curr_offset, payload_sz );
571 0 : out_txn->payload_sz = (ushort)payload_sz;
572 0 : iter.curr_txn_sz = payload_sz;
573 0 : }
574 :
575 : fd_microblock_txn_iter_t
576 0 : fd_microblock_txn_iter_init( fd_microblock_info_t const * microblock_info FD_PARAM_UNUSED ) {
577 0 : return 0UL;
578 0 : }
579 :
580 : ulong
581 0 : fd_microblock_txn_iter_done( fd_microblock_info_t const * microblock_info, fd_microblock_txn_iter_t iter ) {
582 0 : return iter >= microblock_info->microblock_hdr.txn_cnt;
583 0 : }
584 :
585 : fd_microblock_txn_iter_t
586 0 : fd_microblock_txn_iter_next( fd_microblock_info_t const * microblock_info FD_PARAM_UNUSED, fd_microblock_txn_iter_t iter ) {
587 0 : return iter + 1UL;
588 0 : }
589 :
590 : fd_txn_p_t *
591 0 : fd_microblock_txn_iter_ele( fd_microblock_info_t const * microblock_info, fd_microblock_txn_iter_t iter ) {
592 0 : return µblock_info->txns[iter];
593 0 : }
594 :
595 : fd_microblock_batch_txn_iter_t
596 0 : fd_microblock_batch_txn_iter_init( fd_microblock_batch_info_t const * microblock_batch_info ) {
597 0 : fd_microblock_batch_txn_iter_t iter = {
598 0 : .curr_microblock = ULONG_MAX,
599 0 : };
600 :
601 0 : for( ulong i = 0UL; i < microblock_batch_info->microblock_cnt; i++ ) {
602 0 : if( microblock_batch_info->microblock_infos[i].microblock_hdr.txn_cnt > 0 ) {
603 0 : iter.curr_microblock = i;
604 0 : break;
605 0 : }
606 0 : }
607 :
608 0 : iter.microblock_iter = fd_microblock_txn_iter_init( µblock_batch_info->microblock_infos[iter.curr_microblock] );
609 0 : return iter;
610 0 : }
611 :
612 : ulong
613 0 : fd_microblock_batch_txn_iter_done( fd_microblock_batch_info_t const * microblock_batch_info, fd_microblock_batch_txn_iter_t iter ) {
614 0 : return iter.curr_microblock >= microblock_batch_info->microblock_cnt;
615 0 : }
616 :
617 : fd_microblock_batch_txn_iter_t
618 0 : fd_microblock_batch_txn_iter_next( fd_microblock_batch_info_t const * microblock_batch_info, fd_microblock_batch_txn_iter_t iter ) {
619 0 : iter.microblock_iter = fd_microblock_txn_iter_next( µblock_batch_info->microblock_infos[iter.curr_microblock], iter.microblock_iter );
620 0 : while( fd_microblock_txn_iter_done( µblock_batch_info->microblock_infos[iter.curr_microblock], iter.microblock_iter ) ) {
621 0 : iter.curr_microblock++;
622 0 : if( iter.curr_microblock >= microblock_batch_info->microblock_cnt ) {
623 0 : break;
624 0 : }
625 0 : iter.microblock_iter = fd_microblock_txn_iter_init( µblock_batch_info->microblock_infos[iter.curr_microblock] );
626 0 : }
627 0 : return iter;
628 0 : }
629 :
630 : fd_txn_p_t *
631 0 : fd_microblock_batch_txn_iter_ele( fd_microblock_batch_info_t const * microblock_batch_info, fd_microblock_batch_txn_iter_t iter ) {
632 0 : return fd_microblock_txn_iter_ele( µblock_batch_info->microblock_infos[iter.curr_microblock], iter.microblock_iter );
633 0 : }
634 :
635 : fd_block_txn_iter_t
636 0 : fd_block_txn_iter_init( fd_block_info_t const * block_info ) {
637 0 : fd_block_txn_iter_t iter = {
638 0 : .curr_batch = ULONG_MAX,
639 0 : };
640 :
641 0 : for( ulong i = 0UL; i < block_info->microblock_batch_cnt; i++ ) {
642 0 : if( block_info->microblock_batch_infos[i].txn_cnt > 0 ) {
643 0 : iter.curr_batch = i;
644 0 : break;
645 0 : }
646 0 : }
647 :
648 0 : iter.microblock_batch_iter = fd_microblock_batch_txn_iter_init( &block_info->microblock_batch_infos[iter.curr_batch] );
649 0 : return iter;
650 0 : }
651 :
652 : ulong
653 0 : fd_block_txn_iter_done( fd_block_info_t const * block_info, fd_block_txn_iter_t iter ) {
654 0 : return iter.curr_batch >= block_info->microblock_batch_cnt;
655 0 : }
656 :
657 : fd_block_txn_iter_t
658 0 : fd_block_txn_iter_next( fd_block_info_t const * block_info, fd_block_txn_iter_t iter ) {
659 0 : iter.microblock_batch_iter = fd_microblock_batch_txn_iter_next( &block_info->microblock_batch_infos[iter.curr_batch], iter.microblock_batch_iter );
660 0 : while( fd_microblock_batch_txn_iter_done( &block_info->microblock_batch_infos[iter.curr_batch], iter.microblock_batch_iter ) ) {
661 0 : iter.curr_batch++;
662 0 : if( iter.curr_batch >= block_info->microblock_batch_cnt ) {
663 0 : break;
664 0 : }
665 0 : iter.microblock_batch_iter = fd_microblock_batch_txn_iter_init( &block_info->microblock_batch_infos[iter.curr_batch] );
666 :
667 0 : }
668 0 : return iter;
669 0 : }
670 :
671 : fd_txn_p_t *
672 0 : fd_block_txn_iter_ele( fd_block_info_t const * block_info, fd_block_txn_iter_t iter ) {
673 0 : return fd_microblock_batch_txn_iter_ele( &block_info->microblock_batch_infos[iter.curr_batch], iter.microblock_batch_iter );
674 0 : }
675 :
676 : ulong
677 : fd_runtime_microblock_collect_txns( fd_microblock_info_t const * microblock_info,
678 0 : fd_txn_p_t * out_txns ) {
679 0 : ulong txn_cnt = microblock_info->microblock_hdr.txn_cnt;
680 0 : fd_memcpy( out_txns, microblock_info->txns, txn_cnt * sizeof(fd_txn_p_t) );
681 :
682 0 : return txn_cnt;
683 0 : }
684 :
685 : ulong
686 : fd_runtime_microblock_batch_collect_txns( fd_microblock_batch_info_t const * microblock_batch_info,
687 0 : fd_txn_p_t * out_txns ) {
688 0 : for( ulong i = 0; i < microblock_batch_info->microblock_cnt; i++ ) {
689 0 : ulong txns_collected = fd_runtime_microblock_collect_txns( µblock_batch_info->microblock_infos[i], out_txns );
690 0 : out_txns += txns_collected;
691 0 : }
692 :
693 0 : return microblock_batch_info->txn_cnt;
694 0 : }
695 :
696 : ulong
697 : fd_runtime_block_collect_txns( fd_block_info_t const * block_info,
698 0 : fd_txn_p_t * out_txns ) {
699 0 : for( ulong i = 0; i < block_info->microblock_batch_cnt; i++ ) {
700 0 : ulong txns_collected = fd_runtime_microblock_batch_collect_txns( &block_info->microblock_batch_infos[i], out_txns );
701 0 : out_txns += txns_collected;
702 0 : }
703 :
704 0 : return block_info->txn_cnt;
705 0 : }
706 :
707 : /* This is also the maximum number of microblock batches per block */
708 0 : #define FD_MAX_DATA_SHREDS_PER_SLOT (32768UL)
709 :
710 : int fd_runtime_block_prepare(void const *buf,
711 : ulong buf_sz,
712 : fd_valloc_t valloc,
713 0 : fd_block_info_t *out_block_info) {
714 0 : fd_block_info_t block_info = {
715 0 : .raw_block = buf,
716 0 : .signature_cnt = 0,
717 0 : .txn_cnt = 0,
718 0 : };
719 0 : ulong buf_off = 0;
720 :
721 0 : ulong microblock_batch_cnt = 0;
722 0 : ulong microblock_cnt = 0;
723 0 : ulong signature_cnt = 0;
724 0 : ulong txn_cnt = 0;
725 0 : ulong account_cnt = 0;
726 0 : block_info.microblock_batch_infos = fd_valloc_malloc(valloc, alignof(fd_microblock_batch_info_t), FD_MAX_DATA_SHREDS_PER_SLOT * sizeof(fd_microblock_batch_info_t));
727 0 : while (buf_off < buf_sz)
728 0 : {
729 0 : fd_microblock_batch_info_t *microblock_batch_info = &block_info.microblock_batch_infos[microblock_batch_cnt];
730 0 : if (fd_runtime_microblock_batch_prepare((uchar const *)buf + buf_off, buf_sz - buf_off, valloc, microblock_batch_info) != 0)
731 0 : {
732 0 : return -1;
733 0 : }
734 :
735 0 : signature_cnt += microblock_batch_info->signature_cnt;
736 0 : txn_cnt += microblock_batch_info->txn_cnt;
737 0 : account_cnt += microblock_batch_info->account_cnt;
738 0 : buf_off += microblock_batch_info->raw_microblock_batch_sz;
739 0 : microblock_batch_cnt++;
740 0 : microblock_cnt += microblock_batch_info->microblock_cnt;
741 0 : }
742 :
743 0 : block_info.microblock_batch_cnt = microblock_batch_cnt;
744 0 : block_info.microblock_cnt = microblock_cnt;
745 0 : block_info.signature_cnt = signature_cnt;
746 0 : block_info.txn_cnt = txn_cnt;
747 0 : block_info.account_cnt += account_cnt;
748 0 : block_info.raw_block_sz = buf_off;
749 :
750 0 : if (buf_off != buf_sz)
751 0 : {
752 0 : FD_LOG_WARNING(("junk at end of block - consumed: %lu, size: %lu", buf_off, buf_sz));
753 0 : return -1;
754 0 : }
755 :
756 0 : *out_block_info = block_info;
757 :
758 0 : return 0;
759 0 : }
760 :
761 : // TODO: this function doesnt do anything!
762 : int
763 : fd_runtime_block_verify_ticks( fd_block_info_t const * block_info,
764 : ulong tick_height,
765 0 : ulong max_tick_height ) {
766 0 : (void)tick_height; (void)max_tick_height;
767 0 : ulong tick_count = 0UL;
768 0 : for( ulong i = 0UL; i < block_info->microblock_batch_cnt; i++ ) {
769 0 : fd_microblock_batch_info_t const * microblock_batch_info = &block_info->microblock_batch_infos[ i ];
770 0 : for( ulong j = 0UL; j < microblock_batch_info->microblock_cnt; j++ ) {
771 0 : fd_microblock_info_t const * microblock_info = µblock_batch_info->microblock_infos[ i ];
772 0 : if( microblock_info->microblock_hdr.txn_cnt == 0UL ) {
773 : /* if this mblk is a tick */
774 0 : tick_count++;
775 0 : }
776 0 : }
777 0 : }
778 0 : (void)tick_count;
779 0 : return 0;
780 0 : }
781 :
782 : void
783 : fd_runtime_microblock_destroy( fd_valloc_t valloc,
784 0 : fd_microblock_info_t * microblock_info ) {
785 0 : if( microblock_info == NULL ) {
786 0 : return;
787 0 : }
788 :
789 0 : fd_valloc_free( valloc, microblock_info->txns );
790 0 : }
791 :
792 : void
793 : fd_runtime_microblock_batch_destroy( fd_valloc_t valloc,
794 0 : fd_microblock_batch_info_t * microblock_batch_info ) {
795 0 : if( microblock_batch_info == NULL ) {
796 0 : return;
797 0 : }
798 :
799 0 : for( ulong i = 0; i < microblock_batch_info->microblock_cnt; i++ ) {
800 0 : fd_runtime_microblock_destroy( valloc, µblock_batch_info->microblock_infos[i] );
801 0 : }
802 :
803 0 : fd_valloc_free( valloc, microblock_batch_info->microblock_infos );
804 0 : }
805 :
806 : void
807 : fd_runtime_block_destroy( fd_valloc_t valloc,
808 0 : fd_block_info_t * block_info ) {
809 0 : for( ulong i = 0; i < block_info->microblock_batch_cnt; i++ ) {
810 0 : fd_runtime_microblock_batch_destroy( valloc, &block_info->microblock_batch_infos[i] );
811 0 : }
812 :
813 0 : fd_valloc_free( valloc, block_info->microblock_batch_infos );
814 0 : }
815 :
816 : static void FD_FN_UNUSED
817 : fd_runtime_execute_txn_task(void *tpool,
818 : ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
819 : void *args FD_PARAM_UNUSED,
820 : void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
821 : ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
822 : ulong m0, ulong m1 FD_PARAM_UNUSED,
823 0 : ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED) {
824 0 :
825 0 : fd_execute_txn_task_info_t * task_info = (fd_execute_txn_task_info_t *)tpool + m0;
826 0 :
827 0 : if( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) {
828 0 : task_info->exec_res = -1;
829 0 : return;
830 0 : }
831 0 :
832 0 : task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
833 0 : // fd_txn_t const *txn = task_info->txn_ctx->txn_descriptor;
834 0 : // fd_rawtxn_b_t const *raw_txn = task_info->txn_ctx->_txn_raw;
835 0 : #ifdef VLOG
836 0 : FD_LOG_WARNING(("executing txn - slot: %lu, txn_idx: %lu, sig: %s",
837 0 : task_info->txn_ctx->slot_ctx->slot_bank.slot,
838 0 : m0,
839 0 : FD_BASE58_ENC_64_ALLOCA( (uchar *)raw_txn->raw + txn->signature_off )));
840 0 : #endif
841 0 :
842 0 : // Leave this here for debugging...
843 0 : // char txnbuf[100];
844 0 : // fd_base58_encode_64((uchar *)raw_txn->raw + txn->signature_off , NULL, txnbuf );
845 0 :
846 0 : // if (!strcmp(txnbuf, "4RGULZH1tkq5naQzD5zmvPf9T8U5Ei7U2oTExnELf8EyHLyWNQzrDukmzNBVvde2p9NrHn5EW4N38oELejX1MDZq"))
847 0 : // FD_LOG_WARNING(("hi mom"));
848 0 :
849 0 : task_info->exec_res = fd_execute_txn( task_info->txn_ctx );
850 0 : if( task_info->exec_res != 0 ) {
851 0 : return;
852 0 : }
853 0 : fd_txn_reclaim_accounts( task_info->txn_ctx );
854 0 :
855 0 : // FD_LOG_WARNING(( "Transaction result %d for %s %lu %lu %lu",
856 0 : // task_info->exec_res,
857 0 : // FD_BASE58_ENC_64_ALLOCA( (uchar *)raw_txn->raw + txn->signature_off ),
858 0 : // task_info->txn_ctx->compute_meter,
859 0 : // task_info->txn_ctx->compute_unit_limit,
860 0 : // task_info->txn_ctx->num_instructions ));
861 0 : }
862 :
863 : int
864 : fd_runtime_prepare_txns_start( fd_exec_slot_ctx_t * slot_ctx,
865 : fd_execute_txn_task_info_t * task_info,
866 : fd_txn_p_t * txns,
867 11370 : ulong txn_cnt ) {
868 11370 : int res = 0;
869 : /* Loop across transactions */
870 22740 : for (ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++) {
871 11370 : fd_txn_p_t * txn = &txns[txn_idx];
872 :
873 : /* Allocate/setup transaction context and task infos */
874 11370 : task_info[txn_idx].txn_ctx = fd_valloc_malloc( fd_scratch_virtual(), FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
875 11370 : fd_exec_txn_ctx_t * txn_ctx = task_info[txn_idx].txn_ctx;
876 11370 : task_info[txn_idx].exec_res = 0;
877 11370 : task_info[txn_idx].txn = txn;
878 11370 : fd_txn_t const * txn_descriptor = (fd_txn_t const *) txn->_;
879 :
880 11370 : fd_rawtxn_b_t raw_txn = { .raw = txn->payload, .txn_sz = (ushort)txn->payload_sz };
881 :
882 11370 : int err = fd_execute_txn_prepare_start( slot_ctx, txn_ctx, txn_descriptor, &raw_txn );
883 11370 : if( FD_UNLIKELY( err ) ) {
884 270 : task_info[txn_idx].exec_res = err;
885 270 : txn->flags = 0U;
886 270 : res |= err;
887 270 : }
888 11370 : }
889 :
890 11370 : return res;
891 11370 : }
892 :
893 : /* fd_txn_sigverify_task and fd_txn_pre_execute_checks_task are responisble
894 : for the bulk of the pre-transaction execution checks in the runtime.
895 : They aim to preserve the ordering present in the Agave client to match
896 : parity in terms of error codes. Sigverify is kept seperate from the rest
897 : of the transaction checks for fuzzing convenience.
898 :
899 : For reference this is the general code path which contains all relevant
900 : pre-transactions checks in the v2.0.x Agave client from upstream
901 : to downstream is as follows:
902 :
903 : confirm_slot_entries() which calls verify_ticks()
904 : (which is currently unimplemented in firedancer) and
905 : verify_transaction(). verify_transaction() calls verify_and_hash_message()
906 : and verify_precompiles() which parallels fd_executor_txn_verify() and
907 : fd_executor_verify_precompiles().
908 :
909 : process_entries() contains a duplicate account check which is part of
910 : agave account lock acquiring. This is checked inline in
911 : fd_txn_pre_execute_checks_task().
912 :
913 : load_and_execute_transactions() contains the function check_transactions().
914 : This contains check_age() and check_status_cache() which is paralleled by
915 : fd_check_transaction_age() and fd_executor_check_status_cache()
916 : respectively.
917 :
918 : load_and_execute_sanitized_transactions() contains validate_fees()
919 : which is responsible for executing the compute budget instructions,
920 : validating the fee payer and collecting the fee. This is mirrored in
921 : firedancer with fd_executor_compute_budget_program_execute_instructions()
922 : and fd_executor_collect_fees(). load_and_execute_sanitized_transactions()
923 : also checks the total data size of the accounts in load_accounts() and
924 : validates the program accounts in load_transaction_accounts(). This
925 : is paralled by fd_executor_load_transaction_accounts(). */
926 :
927 : static void FD_FN_UNUSED
928 : fd_txn_sigverify_task( void *tpool,
929 : ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
930 : void *args FD_PARAM_UNUSED,
931 : void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
932 : ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
933 : ulong m0, ulong m1 FD_PARAM_UNUSED,
934 0 : ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED ) {
935 0 : fd_execute_txn_task_info_t * task_info = (fd_execute_txn_task_info_t *)tpool + m0;
936 :
937 : /* the txn failed sanitize sometime earlier */
938 0 : if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
939 0 : return;
940 0 : }
941 :
942 0 : fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
943 0 : if( FD_UNLIKELY( fd_executor_txn_verify( txn_ctx )!=0 ) ) {
944 0 : FD_LOG_WARNING(("sigverify failed: %s", FD_BASE58_ENC_64_ALLOCA( (uchar *)txn_ctx->_txn_raw->raw+txn_ctx->txn_descriptor->signature_off ) ));
945 0 : task_info->txn->flags = 0U;
946 0 : task_info->exec_res = FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE;
947 0 : }
948 :
949 0 : }
950 :
951 : void
952 11370 : fd_runtime_pre_execute_check( fd_execute_txn_task_info_t * task_info ) {
953 11370 : if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
954 270 : return;
955 270 : }
956 :
957 11100 : fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
958 :
959 11100 : fd_funk_txn_t * parent_txn = txn_ctx->slot_ctx->funk_txn;
960 11100 : txn_ctx->funk_txn = parent_txn;
961 11100 : fd_executor_setup_borrowed_accounts_for_txn( txn_ctx );
962 :
963 11100 : int err;
964 :
965 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/sdk/src/transaction/sanitized.rs#L263-L275
966 : TODO: Agave's precompile verification is done at the slot level, before batching and executing transactions. This logic should probably
967 : be moved in the future. The Agave call heirarchy looks something like this:
968 : process_single_slot
969 : v
970 : confirm_full_slot
971 : v
972 : confirm_slot_entries --------->
973 : v v
974 : verify_transaction process_entries
975 : v v
976 : verify_precompiles process_batches
977 : v
978 : ...
979 : v
980 : load_and_execute_transactions
981 : v
982 : ...
983 : v
984 : load_accounts --> load_transaction_accounts
985 : v
986 : general transaction execution
987 :
988 : */
989 11100 : err = fd_executor_verify_precompiles( txn_ctx );
990 11100 : if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
991 3459 : task_info->txn->flags = 0U;
992 3459 : task_info->exec_res = err;
993 3459 : return;
994 3459 : }
995 :
996 : /* Post-sanitization checks. Called from `prepare_sanitized_batch()` which, for now, only is used
997 : to lock the accounts and perform a couple basic validations.
998 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/accounts-db/src/account_locks.rs#L118 */
999 7641 : err = fd_executor_validate_account_locks( txn_ctx );
1000 7641 : if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
1001 393 : task_info->txn->flags = 0U;
1002 393 : task_info->exec_res = err;
1003 393 : return;
1004 393 : }
1005 :
1006 : /* `load_and_execute_transactions()` -> `check_transactions()`
1007 : https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/runtime/src/bank.rs#L3667-L3672 */
1008 7248 : err = fd_executor_check_transactions( txn_ctx );
1009 7248 : if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
1010 1038 : task_info->txn->flags = 0U;
1011 1038 : task_info->exec_res = err;
1012 1038 : return;
1013 1038 : }
1014 :
1015 : /* `load_and_execute_sanitized_transactions()` -> `validate_fees()` -> `validate_transaction_fee_payer()`
1016 : https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/svm/src/transaction_processor.rs#L236-L249 */
1017 6210 : err = fd_executor_validate_transaction_fee_payer( txn_ctx );
1018 6210 : if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
1019 153 : task_info->txn->flags = 0U;
1020 153 : task_info->exec_res = err;
1021 153 : return;
1022 153 : }
1023 :
1024 : /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/svm/src/transaction_processor.rs#L284-L296 */
1025 6057 : err = fd_executor_load_transaction_accounts( txn_ctx );
1026 6057 : if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {
1027 702 : task_info->txn->flags = 0U;
1028 702 : task_info->exec_res = err;
1029 702 : return;
1030 702 : }
1031 6057 : }
1032 :
1033 : void
1034 0 : fd_runtime_execute_txn( fd_execute_txn_task_info_t * task_info ) {
1035 :
1036 : /* Transaction sanitization is complete at this point. Now finish account
1037 : setup, execute the transaction, and reclaim dead accounts. */
1038 0 : if( FD_UNLIKELY( task_info->exec_res ) ) {
1039 0 : return;
1040 0 : }
1041 :
1042 0 : task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
1043 0 : task_info->exec_res = fd_execute_txn( task_info->txn_ctx );
1044 0 : fd_txn_reclaim_accounts( task_info->txn_ctx );
1045 0 : }
1046 :
1047 : static void FD_FN_UNUSED
1048 : fd_txn_prep_and_exec_task( void *tpool,
1049 : ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
1050 : void *args FD_PARAM_UNUSED,
1051 : void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
1052 : ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
1053 : ulong m0, ulong m1 FD_PARAM_UNUSED,
1054 0 : ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED ) {
1055 :
1056 0 : fd_execute_txn_task_info_t * task_info = (fd_execute_txn_task_info_t *)tpool + m0;
1057 0 : fd_exec_slot_ctx_t * slot_ctx = (fd_exec_slot_ctx_t *)args;
1058 : // fd_capture_ctx_t * capture_ctx = (fd_capture_ctx_t *)reduce;
1059 :
1060 : /* It is important to note that there is currently a 1-1 mapping between the
1061 : tiles and tpool threads at the time of this comment. Eventually, this will
1062 : change and the transaction context's spad will not be queried by tile
1063 : index as every tile will correspond to one CPU core. */
1064 0 : ulong tile_idx = fd_tile_idx();
1065 0 : task_info->txn_ctx->spad = task_info->spads[ tile_idx ];
1066 0 : if( FD_UNLIKELY( !task_info->txn_ctx->spad ) ) {
1067 0 : FD_LOG_ERR(("spad is NULL"));
1068 0 : }
1069 :
1070 0 : fd_runtime_pre_execute_check( task_info );
1071 0 : fd_runtime_execute_txn( task_info );
1072 :
1073 0 : ulong curr = slot_ctx->slot_bank.collected_execution_fees;
1074 0 : FD_COMPILER_MFENCE();
1075 0 : while( FD_UNLIKELY( FD_ATOMIC_CAS( &slot_ctx->slot_bank.collected_execution_fees, curr, curr + task_info->txn_ctx->execution_fee ) != curr ) ) {
1076 0 : FD_SPIN_PAUSE();
1077 0 : curr = slot_ctx->slot_bank.collected_execution_fees;
1078 0 : FD_COMPILER_MFENCE();
1079 0 : }
1080 :
1081 0 : curr = slot_ctx->slot_bank.collected_priority_fees;
1082 0 : FD_COMPILER_MFENCE();
1083 0 : while( FD_UNLIKELY( FD_ATOMIC_CAS( &slot_ctx->slot_bank.collected_priority_fees, curr, curr + task_info->txn_ctx->priority_fee ) != curr ) ) {
1084 0 : FD_SPIN_PAUSE();
1085 0 : curr = slot_ctx->slot_bank.collected_priority_fees;
1086 0 : FD_COMPILER_MFENCE();
1087 0 : }
1088 :
1089 0 : curr = slot_ctx->slot_bank.collected_rent;
1090 0 : FD_COMPILER_MFENCE();
1091 0 : while( FD_UNLIKELY( FD_ATOMIC_CAS( &slot_ctx->slot_bank.collected_rent, curr, curr + task_info->txn_ctx->collected_rent ) != curr ) ) {
1092 0 : FD_SPIN_PAUSE();
1093 0 : curr = slot_ctx->slot_bank.collected_rent;
1094 0 : FD_COMPILER_MFENCE();
1095 0 : }
1096 :
1097 : // fd_runtime_finalize_txn( slot_ctx, capture_ctx, task_info );
1098 :
1099 0 : }
1100 :
1101 : /* This task could be combined with the rest of the transaction checks that
1102 : exist in fd_runtime_prepare_txns_phase2_tpool, but creates a lot more
1103 : complexity to make the transaction fuzzer work. */
1104 : int
1105 : fd_runtime_verify_txn_signatures_tpool( fd_execute_txn_task_info_t * task_info,
1106 : ulong txn_cnt,
1107 0 : fd_tpool_t * tpool ) {
1108 0 : int res = 0;
1109 0 : fd_tpool_exec_all_rrobin( tpool, 0, fd_tpool_worker_cnt( tpool ), fd_txn_sigverify_task, task_info, NULL, NULL, 1, 0, txn_cnt );
1110 0 : for( ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++ ) {
1111 0 : if( FD_UNLIKELY(!( task_info[txn_idx].txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS )) ) {
1112 0 : task_info->exec_res = FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE;
1113 0 : res |= FD_RUNTIME_TXN_ERR_SIGNATURE_FAILURE;
1114 0 : break;
1115 0 : }
1116 0 : }
1117 0 : return res;
1118 0 : }
1119 :
1120 : int
1121 : fd_runtime_prepare_execute_finalize_txn( fd_exec_slot_ctx_t * slot_ctx,
1122 : fd_spad_t * spad,
1123 : fd_capture_ctx_t * capture_ctx,
1124 : fd_txn_p_t * txn,
1125 0 : fd_execute_txn_task_info_t * task_info ) {
1126 :
1127 0 : FD_SCRATCH_SCOPE_BEGIN {
1128 :
1129 0 : int res = 0;
1130 :
1131 0 : task_info->txn_ctx = fd_valloc_malloc( fd_scratch_virtual(), FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
1132 0 : fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
1133 0 : task_info->exec_res = -1;
1134 0 : task_info->txn = txn;
1135 0 : fd_txn_t const * txn_descriptor = (fd_txn_t const *) txn->_;
1136 :
1137 0 : task_info->txn_ctx->spad = spad;
1138 :
1139 0 : fd_rawtxn_b_t raw_txn = { .raw = txn->payload, .txn_sz = (ushort)txn->payload_sz };
1140 :
1141 0 : res = fd_execute_txn_prepare_start( slot_ctx, txn_ctx, txn_descriptor, &raw_txn );
1142 0 : if( FD_UNLIKELY( res ) ) {
1143 0 : txn->flags = 0U;
1144 0 : return -1;
1145 0 : }
1146 :
1147 0 : txn_ctx->valloc = fd_scratch_virtual();
1148 :
1149 : /* NOTE: This intentionally does not have sigverify */
1150 :
1151 0 : fd_runtime_pre_execute_check( task_info );
1152 0 : if( FD_UNLIKELY( !( task_info->txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
1153 0 : res = task_info->exec_res;
1154 0 : return -1;
1155 0 : }
1156 :
1157 : /* execute */
1158 0 : task_info->txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
1159 0 : task_info->exec_res = fd_execute_txn( task_info->txn_ctx );
1160 :
1161 0 : if( task_info->exec_res==0 ) {
1162 0 : fd_txn_reclaim_accounts( task_info->txn_ctx );
1163 0 : }
1164 :
1165 0 : ulong curr = slot_ctx->slot_bank.collected_execution_fees;
1166 0 : FD_COMPILER_MFENCE();
1167 0 : while( FD_UNLIKELY( FD_ATOMIC_CAS( &slot_ctx->slot_bank.collected_execution_fees, curr, curr + task_info->txn_ctx->execution_fee ) != curr ) ) {
1168 0 : FD_SPIN_PAUSE();
1169 0 : curr = slot_ctx->slot_bank.collected_execution_fees;
1170 0 : FD_COMPILER_MFENCE();
1171 0 : }
1172 :
1173 0 : curr = slot_ctx->slot_bank.collected_priority_fees;
1174 0 : FD_COMPILER_MFENCE();
1175 0 : while( FD_UNLIKELY( FD_ATOMIC_CAS( &slot_ctx->slot_bank.collected_priority_fees, curr, curr + task_info->txn_ctx->priority_fee ) != curr ) ) {
1176 0 : FD_SPIN_PAUSE();
1177 0 : curr = slot_ctx->slot_bank.collected_priority_fees;
1178 0 : FD_COMPILER_MFENCE();
1179 0 : }
1180 :
1181 0 : curr = slot_ctx->slot_bank.collected_rent;
1182 0 : FD_COMPILER_MFENCE();
1183 0 : while( FD_UNLIKELY( FD_ATOMIC_CAS( &slot_ctx->slot_bank.collected_rent, curr, curr + task_info->txn_ctx->collected_rent ) != curr ) ) {
1184 0 : FD_SPIN_PAUSE();
1185 0 : curr = slot_ctx->slot_bank.collected_rent;
1186 0 : FD_COMPILER_MFENCE();
1187 0 : }
1188 :
1189 0 : fd_runtime_finalize_txn( slot_ctx, capture_ctx, task_info );
1190 :
1191 0 : return res;
1192 :
1193 0 : } FD_SCRATCH_SCOPE_END;
1194 0 : }
1195 :
1196 :
1197 : /* This setup phase sets up the borrowed accounts in each transaction and
1198 : performs a series of checks on each of the transactions. */
1199 : int
1200 : fd_runtime_prep_and_exec_txns_tpool( fd_exec_slot_ctx_t * slot_ctx,
1201 : fd_execute_txn_task_info_t * task_info,
1202 : ulong txn_cnt,
1203 0 : fd_tpool_t * tpool ) {
1204 0 : int res = 0;
1205 0 : FD_SCRATCH_SCOPE_BEGIN {
1206 :
1207 0 : fd_tpool_exec_all_rrobin( tpool, 0, fd_tpool_worker_cnt( tpool ), fd_txn_prep_and_exec_task, task_info, slot_ctx, task_info->txn_ctx->capture_ctx, 1, 0, txn_cnt );
1208 :
1209 0 : for( ulong txn_idx=0UL; txn_idx<txn_cnt; txn_idx++ ) {
1210 0 : if( FD_UNLIKELY( !( task_info[txn_idx].txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
1211 0 : res |= task_info[txn_idx].exec_res;
1212 0 : continue;
1213 0 : }
1214 0 : }
1215 :
1216 0 : } FD_SCRATCH_SCOPE_END;
1217 0 : return res;
1218 0 : }
1219 :
1220 : int
1221 : fd_runtime_prepare_txns_phase3( fd_exec_slot_ctx_t * slot_ctx,
1222 : fd_execute_txn_task_info_t * task_info,
1223 0 : ulong txn_cnt ) {
1224 :
1225 0 : int result = 0;
1226 : /* Loop across transactions */
1227 0 : for (ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++) {
1228 0 : fd_exec_txn_ctx_t * txn_ctx = task_info[txn_idx].txn_ctx;
1229 :
1230 0 : if( !( task_info[txn_idx].txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) {
1231 0 : continue;
1232 0 : }
1233 :
1234 0 : int res = fd_execute_txn_prepare_phase3( slot_ctx, txn_ctx, task_info[txn_idx].txn );
1235 0 : if( res != 0 ) {
1236 0 : FD_LOG_DEBUG(("could not prepare txn phase 3"));
1237 0 : task_info[txn_idx].txn->flags = 0;
1238 0 : result = res;
1239 0 : }
1240 :
1241 0 : }
1242 :
1243 0 : return result;
1244 0 : }
1245 :
1246 : void
1247 : fd_runtime_copy_program_data_acc_to_pruned_funk( fd_funk_t * pruned_funk,
1248 : fd_funk_txn_t * prune_txn,
1249 : fd_exec_slot_ctx_t * slot_ctx,
1250 0 : fd_pubkey_t const * program_pubkey ) {
1251 : /* If account corresponds to bpf_upgradeable, copy over the programdata as well.
1252 : This is necessary for executing any bpf upgradeable program. */
1253 :
1254 0 : fd_account_meta_t const * program_acc = fd_acc_mgr_view_raw( slot_ctx->acc_mgr, NULL,
1255 0 : program_pubkey, NULL, NULL, NULL );
1256 :
1257 0 : if( memcmp( program_acc->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
1258 0 : return;
1259 0 : }
1260 :
1261 0 : fd_bincode_decode_ctx_t ctx = {
1262 0 : .data = (uchar *)program_acc + program_acc->hlen,
1263 0 : .dataend = (char *) ctx.data + program_acc->dlen,
1264 0 : .valloc = slot_ctx->valloc,
1265 0 : };
1266 :
1267 0 : fd_bpf_upgradeable_loader_state_t loader_state;
1268 0 : if ( fd_bpf_upgradeable_loader_state_decode( &loader_state, &ctx ) ) {
1269 0 : FD_LOG_ERR(( "fd_bpf_upgradeable_loader_state_decode failed" ));
1270 0 : }
1271 :
1272 0 : if( !fd_bpf_upgradeable_loader_state_is_program( &loader_state ) ) {
1273 0 : FD_LOG_ERR(( "fd_bpf_upgradeable_loader_state_is_program failed" ));
1274 0 : }
1275 :
1276 0 : fd_pubkey_t * programdata_pubkey = (fd_pubkey_t *)&loader_state.inner.program.programdata_address;
1277 0 : fd_funk_rec_key_t programdata_reckey = fd_acc_funk_key( programdata_pubkey );
1278 :
1279 : /* Copy over programdata record */
1280 0 : fd_funk_rec_t * new_rec_pd = fd_funk_rec_write_prepare( pruned_funk, prune_txn, &programdata_reckey,
1281 0 : 0, 1, NULL, NULL );
1282 0 : FD_TEST(( !!new_rec_pd ));
1283 0 : }
1284 :
1285 : void
1286 : fd_runtime_copy_accounts_to_pruned_funk( fd_funk_t * pruned_funk,
1287 : fd_funk_txn_t * prune_txn,
1288 : fd_exec_slot_ctx_t * slot_ctx,
1289 0 : fd_exec_txn_ctx_t * txn_ctx ) {
1290 : /* This function is only responsible for copying over the account ids that are
1291 : modified. The account data is copied over after execution is complete. */
1292 :
1293 : /* Copy over ALUTs */
1294 0 : fd_txn_acct_addr_lut_t * addr_luts = fd_txn_get_address_tables( (fd_txn_t *) txn_ctx->txn_descriptor );
1295 0 : for( ulong i = 0; i < txn_ctx->txn_descriptor->addr_table_lookup_cnt; i++ ) {
1296 0 : fd_txn_acct_addr_lut_t * addr_lut = &addr_luts[i];
1297 0 : fd_pubkey_t const * addr_lut_acc = (fd_pubkey_t *)((uchar *)txn_ctx->_txn_raw->raw + addr_lut->addr_off);
1298 0 : if ( addr_lut_acc ) {
1299 0 : fd_funk_rec_key_t acc_lut_rec_key = fd_acc_funk_key( addr_lut_acc );
1300 0 : fd_funk_rec_write_prepare( pruned_funk, prune_txn, &acc_lut_rec_key, 0, 1, NULL, NULL );
1301 0 : }
1302 0 : }
1303 :
1304 : /* Get program id from top level instructions and copy over programdata */
1305 0 : fd_instr_info_t instrs[txn_ctx->txn_descriptor->instr_cnt];
1306 0 : for ( ushort i = 0; i < txn_ctx->txn_descriptor->instr_cnt; i++ ) {
1307 0 : fd_txn_instr_t const * txn_instr = &txn_ctx->txn_descriptor->instr[i];
1308 0 : fd_convert_txn_instr_to_instr( txn_ctx, txn_instr, txn_ctx->borrowed_accounts, &instrs[i] );
1309 0 : fd_pubkey_t program_pubkey = instrs[i].program_id_pubkey;
1310 0 : fd_funk_rec_key_t program_rec_key = fd_acc_funk_key( &program_pubkey );
1311 0 : fd_funk_rec_t *new_rec = fd_funk_rec_write_prepare(pruned_funk, prune_txn, &program_rec_key, 0, 1, NULL, NULL);
1312 0 : if ( !new_rec ) {
1313 0 : FD_LOG_NOTICE(("fd_funk_rec_write_prepare failed %s", FD_BASE58_ENC_32_ALLOCA( &program_pubkey ) ));
1314 0 : continue;
1315 0 : }
1316 :
1317 : /* If account corresponds to bpf_upgradeable, copy over the programdata as well */
1318 0 : fd_runtime_copy_program_data_acc_to_pruned_funk( pruned_funk, prune_txn, slot_ctx, &program_pubkey );
1319 0 : }
1320 :
1321 : /* Write out all accounts touched during the transaction, copy over all program data accounts for
1322 : any BPF upgradeable accounts in case they are a CPI's program account. */
1323 0 : for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) {
1324 0 : fd_pubkey_t * acc_pubkey = (fd_pubkey_t *)&txn_ctx->accounts[i].key;
1325 0 : fd_funk_rec_key_t rec_key = fd_acc_funk_key( acc_pubkey );
1326 0 : fd_funk_rec_t * rec = fd_funk_rec_write_prepare( pruned_funk, prune_txn, &rec_key, 0, 1, NULL, NULL );
1327 0 : FD_TEST(( !!rec ));
1328 0 : fd_runtime_copy_program_data_acc_to_pruned_funk( pruned_funk, prune_txn, slot_ctx, acc_pubkey );
1329 0 : }
1330 0 : }
1331 :
1332 : void
1333 : fd_runtime_write_transaction_status( fd_capture_ctx_t * capture_ctx,
1334 : fd_exec_slot_ctx_t * slot_ctx,
1335 : fd_exec_txn_ctx_t * txn_ctx,
1336 0 : int exec_txn_err) {
1337 : /* Look up solana-side transaction status details */
1338 0 : fd_blockstore_t * blockstore = txn_ctx->slot_ctx->blockstore;
1339 0 : uchar * sig = (uchar *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->signature_off;
1340 0 : fd_blockstore_txn_map_t * txn_map_entry = fd_blockstore_txn_query( blockstore, sig );
1341 0 : if ( txn_map_entry != NULL ) {
1342 0 : void * meta = fd_wksp_laddr_fast( fd_blockstore_wksp( blockstore ), txn_map_entry->meta_gaddr );
1343 :
1344 0 : fd_solblock_TransactionStatusMeta txn_status = {0};
1345 : /* Need to handle case for ledgers where transaction status is not available.
1346 : This case will be handled in fd_solcap_diff. */
1347 0 : ulong fd_cus_consumed = txn_ctx->compute_unit_limit - txn_ctx->compute_meter;
1348 0 : ulong solana_cus_consumed = ULONG_MAX;
1349 0 : ulong solana_txn_err = ULONG_MAX;
1350 0 : if ( meta != NULL ) {
1351 0 : pb_istream_t stream = pb_istream_from_buffer( meta, txn_map_entry->meta_sz );
1352 0 : if ( pb_decode( &stream, fd_solblock_TransactionStatusMeta_fields, &txn_status ) == false ) {
1353 0 : FD_LOG_WARNING(("no txn_status decoding found sig=%s (%s)", FD_BASE58_ENC_64_ALLOCA( sig ), PB_GET_ERROR(&stream)));
1354 0 : }
1355 0 : if ( txn_status.has_compute_units_consumed ) {
1356 0 : solana_cus_consumed = txn_status.compute_units_consumed;
1357 0 : }
1358 0 : if ( txn_status.has_err ) {
1359 0 : solana_txn_err = txn_status.err.err->bytes[0];
1360 0 : }
1361 :
1362 0 : fd_solcap_Transaction txn = {
1363 0 : .slot = slot_ctx->slot_bank.slot,
1364 0 : .fd_txn_err = exec_txn_err,
1365 0 : .fd_custom_err = txn_ctx->custom_err,
1366 0 : .solana_txn_err = solana_txn_err,
1367 0 : .fd_cus_used = fd_cus_consumed,
1368 0 : .solana_cus_used = solana_cus_consumed,
1369 0 : .instr_err_idx = txn_ctx->instr_err_idx == INT_MAX ? -1 : txn_ctx->instr_err_idx,
1370 0 : };
1371 0 : memcpy( txn.txn_sig, sig, sizeof(fd_signature_t) );
1372 :
1373 0 : fd_exec_instr_ctx_t const * failed_instr = txn_ctx->failed_instr;
1374 0 : if( failed_instr ) {
1375 0 : assert( failed_instr->depth < 4 );
1376 0 : txn.instr_err = failed_instr->instr_err;
1377 0 : txn.failed_instr_path_count = failed_instr->depth + 1;
1378 0 : for( long j = failed_instr->depth; j>=0L; j-- ) {
1379 0 : txn.failed_instr_path[j] = failed_instr->index;
1380 0 : failed_instr = failed_instr->parent;
1381 0 : }
1382 0 : }
1383 :
1384 0 : fd_solcap_write_transaction2( capture_ctx->capture, &txn );
1385 0 : }
1386 0 : }
1387 0 : }
1388 :
1389 : int
1390 : fd_runtime_finalize_txn( fd_exec_slot_ctx_t * slot_ctx,
1391 : fd_capture_ctx_t * capture_ctx,
1392 0 : fd_execute_txn_task_info_t * task_info ) {
1393 :
1394 0 : fd_exec_txn_ctx_t * txn_ctx = task_info->txn_ctx;
1395 0 : int exec_txn_err = task_info->exec_res;
1396 :
1397 0 : fd_funk_txn_t * prune_txn = NULL;
1398 0 : if( capture_ctx != NULL && capture_ctx->pruned_funk != NULL ) {
1399 0 : fd_funk_txn_xid_t prune_xid;
1400 0 : fd_memset( &prune_xid, 0x42, sizeof(fd_funk_txn_xid_t) );
1401 0 : fd_funk_txn_t * txn_map = fd_funk_txn_map( capture_ctx->pruned_funk, fd_funk_wksp( capture_ctx->pruned_funk ) );
1402 0 : prune_txn = fd_funk_txn_query( &prune_xid, txn_map );
1403 0 : }
1404 :
1405 : /* Add all involved records to pruned funk */
1406 0 : if( capture_ctx != NULL && capture_ctx->pruned_funk != NULL ) {
1407 0 : fd_funk_start_write( capture_ctx->pruned_funk );
1408 0 : fd_runtime_copy_accounts_to_pruned_funk( capture_ctx->pruned_funk, prune_txn, slot_ctx, txn_ctx );
1409 0 : fd_funk_end_write( capture_ctx->pruned_funk );
1410 0 : }
1411 :
1412 : /* For ledgers that contain txn status, decode and write out for solcap */
1413 0 : if( capture_ctx != NULL && capture_ctx->capture && capture_ctx->capture_txns ) {
1414 : // TODO: probably need to get rid of this lock or special case it to not use funk's lock.
1415 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
1416 0 : fd_runtime_write_transaction_status( capture_ctx, slot_ctx, txn_ctx, exec_txn_err );
1417 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
1418 0 : }
1419 :
1420 0 : if( slot_ctx->status_cache ) {
1421 0 : fd_txncache_insert_t * status_insert = fd_scratch_alloc( alignof(fd_txncache_insert_t), sizeof(fd_txncache_insert_t) );
1422 0 : uchar * results = fd_scratch_alloc( alignof(uchar), sizeof(uchar) );
1423 :
1424 0 : results[0] = exec_txn_err == 0 ? 1 : 0;
1425 0 : fd_txncache_insert_t * curr_insert = &status_insert[0];
1426 0 : curr_insert->blockhash = ((uchar *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->recent_blockhash_off);
1427 0 : curr_insert->slot = slot_ctx->slot_bank.slot;
1428 0 : fd_hash_t * hash = &txn_ctx->blake_txn_msg_hash;
1429 0 : curr_insert->txnhash = hash->uc;
1430 0 : curr_insert->result = &results[0];
1431 0 : if( !fd_txncache_insert_batch( slot_ctx->status_cache, status_insert, 1UL ) ) {
1432 0 : FD_LOG_DEBUG(("Status cache is full, this should not be possible"));
1433 0 : }
1434 0 : }
1435 :
1436 0 : if( FD_UNLIKELY( exec_txn_err ) ) {
1437 :
1438 : /* Save the fee_payer. Everything but the fee balance should be reset.
1439 : TODO: an optimization here could be to use a dirty flag in the
1440 : borrowed account. If the borrowed account data has been changed in
1441 : any way, then the full account can be rolled back as it is done now.
1442 : However, most of the time the account data is not changed, and only
1443 : the lamport balance has to change. */
1444 0 : fd_borrowed_account_t * borrowed_account = fd_borrowed_account_init( &txn_ctx->borrowed_accounts[0] );
1445 :
1446 0 : fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, &txn_ctx->accounts[0], borrowed_account );
1447 0 : memcpy( borrowed_account->pubkey->key, &txn_ctx->accounts[0], sizeof(fd_pubkey_t) );
1448 :
1449 0 : void * borrowed_account_data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
1450 0 : fd_borrowed_account_make_modifiable( borrowed_account, borrowed_account_data );
1451 0 : borrowed_account->meta->info.lamports -= (txn_ctx->execution_fee + txn_ctx->priority_fee);
1452 :
1453 0 : fd_acc_mgr_save_non_tpool( slot_ctx->acc_mgr, slot_ctx->funk_txn, &txn_ctx->borrowed_accounts[0] );
1454 :
1455 0 : for( ulong i=1UL; i<txn_ctx->accounts_cnt; i++ ) {
1456 0 : if( txn_ctx->nonce_accounts[i] ) {
1457 0 : ushort recent_blockhash_off = txn_ctx->txn_descriptor->recent_blockhash_off;
1458 0 : fd_hash_t * recent_blockhash = (fd_hash_t *)((uchar *)txn_ctx->_txn_raw->raw + recent_blockhash_off);
1459 0 : fd_block_hash_queue_t queue = slot_ctx->slot_bank.block_hash_queue;
1460 0 : ulong queue_sz = fd_hash_hash_age_pair_t_map_size( queue.ages_pool, queue.ages_root );
1461 0 : if( FD_UNLIKELY( !queue_sz ) ) {
1462 0 : FD_LOG_ERR(( "Blockhash queue is empty" ));
1463 0 : }
1464 :
1465 0 : if( !fd_executor_is_blockhash_valid_for_age( &queue, recent_blockhash, FD_RECENT_BLOCKHASHES_MAX_ENTRIES ) ) {
1466 0 : fd_acc_mgr_save_non_tpool( slot_ctx->acc_mgr, slot_ctx->funk_txn, &txn_ctx->borrowed_accounts[i] );
1467 0 : }
1468 0 : }
1469 0 : }
1470 0 : } else {
1471 :
1472 0 : int dirty_vote_acc = txn_ctx->dirty_vote_acc;
1473 0 : int dirty_stake_acc = txn_ctx->dirty_stake_acc;
1474 :
1475 0 : for( ulong i=0UL; i<txn_ctx->accounts_cnt; i++ ) {
1476 0 : if( !fd_txn_account_is_writable_idx( txn_ctx, (int)i ) ) {
1477 0 : continue;
1478 0 : }
1479 :
1480 0 : fd_borrowed_account_t * acc_rec = &txn_ctx->borrowed_accounts[i];
1481 :
1482 0 : if( dirty_vote_acc && 0==memcmp( acc_rec->const_meta->info.owner, &fd_solana_vote_program_id, sizeof(fd_pubkey_t) ) ) {
1483 : /* lock for inserting/modifying vote accounts in slot ctx. */
1484 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
1485 0 : fd_vote_store_account( slot_ctx, acc_rec );
1486 0 : FD_SCRATCH_SCOPE_BEGIN {
1487 0 : fd_vote_state_versioned_t vsv[1];
1488 0 : fd_bincode_decode_ctx_t decode_vsv =
1489 0 : { .data = acc_rec->const_data,
1490 0 : .dataend = acc_rec->const_data + acc_rec->const_meta->dlen,
1491 0 : .valloc = fd_scratch_virtual() };
1492 :
1493 0 : int err = fd_vote_state_versioned_decode( vsv, &decode_vsv );
1494 0 : if( err ) break; /* out of scratch scope */
1495 :
1496 0 : fd_vote_block_timestamp_t const * ts = NULL;
1497 0 : switch( vsv->discriminant ) {
1498 0 : case fd_vote_state_versioned_enum_v0_23_5:
1499 0 : ts = &vsv->inner.v0_23_5.last_timestamp;
1500 0 : break;
1501 0 : case fd_vote_state_versioned_enum_v1_14_11:
1502 0 : ts = &vsv->inner.v1_14_11.last_timestamp;
1503 0 : break;
1504 0 : case fd_vote_state_versioned_enum_current:
1505 0 : ts = &vsv->inner.current.last_timestamp;
1506 0 : break;
1507 0 : default:
1508 0 : __builtin_unreachable();
1509 0 : }
1510 :
1511 0 : fd_vote_record_timestamp_vote_with_slot( slot_ctx, acc_rec->pubkey, ts->timestamp, ts->slot );
1512 0 : }
1513 0 : FD_SCRATCH_SCOPE_END;
1514 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
1515 0 : }
1516 :
1517 0 : if( dirty_stake_acc && 0==memcmp( acc_rec->const_meta->info.owner, &fd_solana_stake_program_id, sizeof(fd_pubkey_t) ) ) {
1518 : // TODO: does this correctly handle stake account close?
1519 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
1520 0 : fd_store_stake_delegation( slot_ctx, acc_rec );
1521 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
1522 0 : }
1523 :
1524 0 : fd_acc_mgr_save_non_tpool( slot_ctx->acc_mgr, slot_ctx->funk_txn, &txn_ctx->borrowed_accounts[i] );
1525 0 : }
1526 0 : }
1527 0 : ulong curr = slot_ctx->signature_cnt;
1528 0 : FD_COMPILER_MFENCE();
1529 0 : while( FD_UNLIKELY( FD_ATOMIC_CAS( &slot_ctx->signature_cnt, curr, curr + txn_ctx->txn_descriptor->signature_cnt ) != curr ) ) {
1530 0 : FD_SPIN_PAUSE();
1531 0 : curr = slot_ctx->signature_cnt;
1532 0 : FD_COMPILER_MFENCE();
1533 0 : }
1534 :
1535 0 : return 0;
1536 0 : }
1537 :
1538 : static bool
1539 0 : encode_return_data( pb_ostream_t *stream, const pb_field_t *field, void * const *arg ) {
1540 0 : fd_exec_txn_ctx_t * txn_ctx = (fd_exec_txn_ctx_t *)(*arg);
1541 0 : pb_encode_tag_for_field(stream, field);
1542 0 : pb_encode_string(stream, txn_ctx->return_data.data, txn_ctx->return_data.len );
1543 0 : return 1;
1544 0 : }
1545 :
1546 : static ulong
1547 0 : fd_txn_copy_meta( fd_exec_txn_ctx_t * txn_ctx, uchar * dest, ulong dest_sz ) {
1548 0 : fd_solblock_TransactionStatusMeta txn_status = {0};
1549 :
1550 0 : txn_status.has_fee = 1;
1551 0 : txn_status.fee = txn_ctx->execution_fee + txn_ctx->priority_fee;
1552 :
1553 0 : txn_status.has_compute_units_consumed = 1;
1554 0 : txn_status.compute_units_consumed = txn_ctx->compute_unit_limit - txn_ctx->compute_meter;
1555 :
1556 0 : ulong readonly_cnt = 0;
1557 0 : ulong writable_cnt = 0;
1558 0 : if( txn_ctx->txn_descriptor->transaction_version == FD_TXN_V0 ) {
1559 0 : fd_txn_acct_addr_lut_t const * addr_luts = fd_txn_get_address_tables_const( txn_ctx->txn_descriptor );
1560 0 : for( ulong i = 0; i < txn_ctx->txn_descriptor->addr_table_lookup_cnt; i++ ) {
1561 0 : fd_txn_acct_addr_lut_t const * addr_lut = &addr_luts[i];
1562 0 : readonly_cnt += addr_lut->readonly_cnt;
1563 0 : writable_cnt += addr_lut->writable_cnt;
1564 0 : }
1565 0 : }
1566 :
1567 0 : typedef PB_BYTES_ARRAY_T(32) my_ba_t;
1568 0 : typedef union { my_ba_t my; pb_bytes_array_t normal; } union_ba_t;
1569 0 : union_ba_t writable_ba[writable_cnt];
1570 0 : pb_bytes_array_t * writable_baptr[writable_cnt];
1571 0 : txn_status.loaded_writable_addresses_count = (uint)writable_cnt;
1572 0 : txn_status.loaded_writable_addresses = writable_baptr;
1573 0 : ulong idx2 = txn_ctx->txn_descriptor->acct_addr_cnt;
1574 0 : for (ulong idx = 0; idx < writable_cnt; idx++) {
1575 0 : pb_bytes_array_t * ba = writable_baptr[ idx ] = &writable_ba[ idx ].normal;
1576 0 : ba->size = 32;
1577 0 : fd_memcpy(ba->bytes, &txn_ctx->accounts[idx2++], 32);
1578 0 : }
1579 :
1580 0 : union_ba_t readonly_ba[readonly_cnt];
1581 0 : pb_bytes_array_t * readonly_baptr[readonly_cnt];
1582 0 : txn_status.loaded_readonly_addresses_count = (uint)readonly_cnt;
1583 0 : txn_status.loaded_readonly_addresses = readonly_baptr;
1584 0 : for (ulong idx = 0; idx < readonly_cnt; idx++) {
1585 0 : pb_bytes_array_t * ba = readonly_baptr[ idx ] = &readonly_ba[ idx ].normal;
1586 0 : ba->size = 32;
1587 0 : fd_memcpy(ba->bytes, &txn_ctx->accounts[idx2++], 32);
1588 0 : }
1589 0 : ulong acct_cnt = txn_ctx->accounts_cnt;
1590 0 : FD_TEST(acct_cnt == idx2);
1591 :
1592 0 : txn_status.pre_balances_count = txn_status.post_balances_count = (pb_size_t)acct_cnt;
1593 0 : uint64_t pre_balances[acct_cnt];
1594 0 : txn_status.pre_balances = pre_balances;
1595 0 : uint64_t post_balances[acct_cnt];
1596 0 : txn_status.post_balances = post_balances;
1597 :
1598 0 : for (ulong idx = 0; idx < acct_cnt; idx++) {
1599 0 : fd_borrowed_account_t const * acct = &txn_ctx->borrowed_accounts[idx];
1600 0 : ulong pre = ( acct->starting_lamports == ULONG_MAX ? 0UL : acct->starting_lamports );
1601 0 : pre_balances[idx] = pre;
1602 0 : post_balances[idx] = ( acct->meta ? acct->meta->info.lamports :
1603 0 : ( acct->orig_meta ? acct->orig_meta->info.lamports : pre ) );
1604 0 : }
1605 :
1606 0 : if( txn_ctx->return_data.len ) {
1607 0 : txn_status.has_return_data = 1;
1608 0 : txn_status.return_data.has_program_id = 1;
1609 0 : fd_memcpy( txn_status.return_data.program_id, txn_ctx->return_data.program_id.uc, 32U );
1610 0 : pb_callback_t data = { .funcs.encode = encode_return_data, .arg = txn_ctx };
1611 0 : txn_status.return_data.data = data;
1612 0 : }
1613 :
1614 0 : union {
1615 0 : pb_bytes_array_t arr;
1616 0 : uchar space[64];
1617 0 : } errarr;
1618 0 : pb_byte_t * errptr = errarr.arr.bytes;
1619 0 : if( txn_ctx->custom_err != UINT_MAX ) {
1620 0 : *(uint*)errptr = 8 /* Instruction error */;
1621 0 : errptr += sizeof(uint);
1622 0 : *errptr = (uchar)txn_ctx->instr_err_idx;
1623 0 : errptr += 1;
1624 0 : *(int*)errptr = FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
1625 0 : errptr += sizeof(int);
1626 0 : *(uint*)errptr = txn_ctx->custom_err;
1627 0 : errptr += sizeof(uint);
1628 0 : errarr.arr.size = (uint)(errptr - errarr.arr.bytes);
1629 0 : txn_status.has_err = 1;
1630 0 : txn_status.err.err = &errarr.arr;
1631 0 : } else if( txn_ctx->exec_err ) {
1632 0 : switch( txn_ctx->exec_err_kind ) {
1633 0 : case FD_EXECUTOR_ERR_KIND_SYSCALL:
1634 0 : break;
1635 0 : case FD_EXECUTOR_ERR_KIND_INSTR:
1636 0 : *(uint*)errptr = 8 /* Instruction error */;
1637 0 : errptr += sizeof(uint);
1638 0 : *errptr = (uchar)txn_ctx->instr_err_idx;
1639 0 : errptr += 1;
1640 0 : *(int*)errptr = txn_ctx->exec_err;
1641 0 : errptr += sizeof(int);
1642 0 : errarr.arr.size = (uint)(errptr - errarr.arr.bytes);
1643 0 : txn_status.has_err = 1;
1644 0 : txn_status.err.err = &errarr.arr;
1645 0 : break;
1646 0 : case FD_EXECUTOR_ERR_KIND_EBPF:
1647 0 : break;
1648 0 : }
1649 0 : }
1650 :
1651 0 : if( dest == NULL ) {
1652 0 : size_t sz = 0;
1653 0 : bool r = pb_get_encoded_size( &sz, fd_solblock_TransactionStatusMeta_fields, &txn_status );
1654 0 : if( !r ) {
1655 0 : FD_LOG_WARNING(( "pb_get_encoded_size failed" ));
1656 0 : return 0;
1657 0 : }
1658 0 : return sz + txn_ctx->log_collector.buf_sz;
1659 0 : }
1660 :
1661 0 : pb_ostream_t stream = pb_ostream_from_buffer( dest, dest_sz );
1662 0 : bool r = pb_encode( &stream, fd_solblock_TransactionStatusMeta_fields, &txn_status );
1663 0 : if( !r ) {
1664 0 : FD_LOG_WARNING(( "pb_encode failed" ));
1665 0 : return 0;
1666 0 : }
1667 0 : pb_write( &stream, txn_ctx->log_collector.buf, txn_ctx->log_collector.buf_sz );
1668 0 : return stream.bytes_written;
1669 0 : }
1670 :
1671 : /* fd_runtime_finalize_txns_update_blockstore_meta() updates transaction metadata
1672 : after execution.
1673 :
1674 : Execution recording is controlled by slot_ctx->enable_exec_recording, and this
1675 : function does nothing if execution recording is off. The following comments
1676 : only apply when execution recording is on.
1677 :
1678 : Transaction metadata includes execution result (success/error), balance changes,
1679 : transaction logs, ... All this info is not part of consensus but can be retrieved,
1680 : for instace, via RPC getTransaction. Firedancer stores txn meta in the blockstore,
1681 : in the same binary format as Agave, protobuf TransactionStatusMeta. */
1682 : void
1683 : fd_runtime_finalize_txns_update_blockstore_meta( fd_exec_slot_ctx_t * slot_ctx,
1684 : fd_execute_txn_task_info_t * task_info,
1685 0 : ulong txn_cnt ) {
1686 : /* Nothing to do if execution recording is off */
1687 0 : if( !slot_ctx->enable_exec_recording ) {
1688 0 : return;
1689 0 : }
1690 :
1691 0 : fd_blockstore_t * blockstore = slot_ctx->blockstore;
1692 0 : fd_wksp_t * blockstore_wksp = fd_blockstore_wksp( blockstore );
1693 0 : fd_alloc_t * blockstore_alloc = fd_wksp_laddr_fast( blockstore_wksp, blockstore->alloc_gaddr );
1694 0 : fd_blockstore_txn_map_t * txn_map = fd_wksp_laddr_fast( blockstore_wksp, blockstore->txn_map_gaddr );
1695 :
1696 : /* Get the total size of all logs */
1697 0 : ulong tot_meta_sz = 2*sizeof(ulong);
1698 0 : for( ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++ ) {
1699 : /* Prebalance compensation */
1700 0 : fd_exec_txn_ctx_t * txn_ctx = task_info[txn_idx].txn_ctx;
1701 0 : txn_ctx->borrowed_accounts[0].starting_lamports += (txn_ctx->execution_fee + txn_ctx->priority_fee);
1702 : /* Get the size without the copy */
1703 0 : tot_meta_sz += fd_txn_copy_meta( txn_ctx, NULL, 0 );
1704 0 : }
1705 0 : uchar * cur_laddr = fd_alloc_malloc( blockstore_alloc, 1, tot_meta_sz );
1706 0 : if( cur_laddr == NULL ) {
1707 0 : return;
1708 0 : }
1709 0 : uchar * const end_laddr = cur_laddr + tot_meta_sz;
1710 :
1711 0 : fd_blockstore_start_write( blockstore );
1712 0 : fd_block_t * blk = slot_ctx->block;
1713 : /* Link to previous allocation */
1714 0 : ((ulong*)cur_laddr)[0] = blk->txns_meta_gaddr;
1715 0 : ((ulong*)cur_laddr)[1] = blk->txns_meta_sz;
1716 0 : blk->txns_meta_gaddr = fd_wksp_gaddr_fast( blockstore_wksp, cur_laddr );
1717 0 : blk->txns_meta_sz = tot_meta_sz;
1718 0 : cur_laddr += 2*sizeof(ulong);
1719 :
1720 0 : for( ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++ ) {
1721 0 : fd_exec_txn_ctx_t * txn_ctx = task_info[txn_idx].txn_ctx;
1722 0 : ulong meta_sz = fd_txn_copy_meta( txn_ctx, cur_laddr, (size_t)(end_laddr - cur_laddr) );
1723 0 : if( meta_sz ) {
1724 0 : ulong meta_gaddr = fd_wksp_gaddr_fast( blockstore_wksp, cur_laddr );
1725 :
1726 : /* Update all the signatures */
1727 0 : char const * sig_p = (char const *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->signature_off;
1728 0 : fd_blockstore_txn_key_t sig;
1729 0 : for( uchar i=0U; i<txn_ctx->txn_descriptor->signature_cnt; i++ ) {
1730 0 : fd_memcpy( &sig, sig_p, sizeof(fd_blockstore_txn_key_t) );
1731 0 : fd_blockstore_txn_map_t * txn_map_entry = fd_blockstore_txn_map_query( txn_map, &sig, NULL );
1732 0 : if( FD_LIKELY( txn_map_entry ) ) {
1733 0 : txn_map_entry->meta_gaddr = meta_gaddr;
1734 0 : txn_map_entry->meta_sz = meta_sz;
1735 0 : }
1736 0 : sig_p += FD_ED25519_SIG_SZ;
1737 0 : }
1738 :
1739 0 : cur_laddr += meta_sz;
1740 0 : }
1741 0 : fd_log_collector_delete( &txn_ctx->log_collector );
1742 0 : }
1743 :
1744 0 : FD_TEST( cur_laddr == end_laddr );
1745 :
1746 0 : fd_blockstore_end_write( blockstore );
1747 0 : }
1748 :
1749 : /* https://github.com/anza-xyz/agave/blob/16de8b75ebcd57022409b422de557dd37b1de8db/accounts-db/src/accounts.rs#L700 */
1750 : int
1751 : fd_runtime_finalize_txns_tpool( fd_exec_slot_ctx_t * slot_ctx,
1752 : fd_capture_ctx_t * capture_ctx,
1753 : fd_execute_txn_task_info_t * task_info,
1754 : ulong txn_cnt,
1755 0 : fd_tpool_t * tpool ) {
1756 0 : FD_SCRATCH_SCOPE_BEGIN {
1757 :
1758 0 : fd_funk_txn_t * prune_txn = NULL;
1759 0 : if( capture_ctx != NULL && capture_ctx->pruned_funk != NULL ) {
1760 0 : fd_funk_txn_xid_t prune_xid;
1761 0 : fd_memset( &prune_xid, 0x42, sizeof(fd_funk_txn_xid_t) );
1762 0 : fd_funk_txn_t * txn_map = fd_funk_txn_map( capture_ctx->pruned_funk, fd_funk_wksp( capture_ctx->pruned_funk ) );
1763 0 : prune_txn = fd_funk_txn_query( &prune_xid, txn_map );
1764 0 : }
1765 :
1766 : /* Store transaction metadata, including logs */
1767 0 : fd_runtime_finalize_txns_update_blockstore_meta( slot_ctx, task_info, txn_cnt );
1768 :
1769 0 : fd_txncache_insert_t * status_insert = NULL;
1770 0 : uchar * results = NULL;
1771 0 : ulong num_cache_txns = 0UL;
1772 :
1773 :
1774 0 : if( FD_LIKELY( slot_ctx->status_cache ) ) {
1775 0 : status_insert = fd_scratch_alloc( alignof(fd_txncache_insert_t), txn_cnt * sizeof(fd_txncache_insert_t) );
1776 0 : results = fd_scratch_alloc( alignof(uchar), txn_cnt * sizeof(uchar) );
1777 0 : }
1778 :
1779 0 : fd_borrowed_account_t * * accounts_to_save = fd_scratch_alloc( 8UL, 128UL * txn_cnt * sizeof(fd_borrowed_account_t *) );
1780 0 : ulong acc_idx = 0UL;
1781 0 : for( ulong txn_idx=0UL; txn_idx<txn_cnt; txn_idx++ ) {
1782 : /* Transaction was skipped due to preparation failure. */
1783 0 : if( FD_UNLIKELY( !( task_info[txn_idx].txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS ) ) ) {
1784 0 : continue;
1785 0 : }
1786 0 : fd_exec_txn_ctx_t * txn_ctx = task_info[txn_idx].txn_ctx;
1787 0 : int exec_txn_err = task_info[txn_idx].exec_res;
1788 :
1789 : /* Add all involved records to pruned funk */
1790 0 : if( FD_UNLIKELY( capture_ctx != NULL && capture_ctx->pruned_funk != NULL ) ) {
1791 0 : fd_funk_start_write( capture_ctx->pruned_funk );
1792 0 : fd_runtime_copy_accounts_to_pruned_funk( capture_ctx->pruned_funk, prune_txn, slot_ctx, txn_ctx );
1793 0 : fd_funk_end_write( capture_ctx->pruned_funk );
1794 0 : }
1795 :
1796 : /* For ledgers that contain txn status, decode and write out for solcap */
1797 0 : if( FD_UNLIKELY( capture_ctx != NULL && capture_ctx->capture && capture_ctx->capture_txns ) ) {
1798 0 : fd_runtime_write_transaction_status( capture_ctx, slot_ctx, txn_ctx, exec_txn_err );
1799 0 : }
1800 :
1801 0 : slot_ctx->signature_cnt += txn_ctx->txn_descriptor->signature_cnt;
1802 :
1803 0 : if( FD_LIKELY( slot_ctx->status_cache ) ) {
1804 0 : results[num_cache_txns] = exec_txn_err == 0 ? 1 : 0;
1805 0 : fd_txncache_insert_t * curr_insert = &status_insert[num_cache_txns];
1806 0 : curr_insert->blockhash = ((uchar *)txn_ctx->_txn_raw->raw + txn_ctx->txn_descriptor->recent_blockhash_off);
1807 0 : curr_insert->slot = slot_ctx->slot_bank.slot;
1808 0 : fd_hash_t * hash = &txn_ctx->blake_txn_msg_hash;
1809 0 : curr_insert->txnhash = hash->uc;
1810 0 : curr_insert->result = &results[num_cache_txns];
1811 0 : num_cache_txns++;
1812 0 : }
1813 :
1814 0 : if( FD_UNLIKELY( exec_txn_err ) ) {
1815 : /* Save the fee_payer. Everything but the fee balance should be reset.
1816 : TODO: an optimization here could be to use a dirty flag in the
1817 : borrowed account. If the borrowed account data has been changed in
1818 : any way, then the full account can be rolled back as it is done now.
1819 : However, most of the time the account data is not changed, and only
1820 : the lamport balance has to change. */
1821 0 : fd_borrowed_account_t * borrowed_account = fd_borrowed_account_init( &txn_ctx->borrowed_accounts[0] );
1822 :
1823 0 : fd_acc_mgr_view( txn_ctx->acc_mgr, txn_ctx->funk_txn, &txn_ctx->accounts[0], borrowed_account );
1824 0 : memcpy( borrowed_account->pubkey->key, &txn_ctx->accounts[0], sizeof(fd_pubkey_t) );
1825 :
1826 0 : void * borrowed_account_data = fd_spad_alloc( txn_ctx->spad, FD_ACCOUNT_REC_ALIGN, FD_ACC_TOT_SZ_MAX );
1827 0 : fd_borrowed_account_make_modifiable( borrowed_account, borrowed_account_data );
1828 0 : borrowed_account->meta->info.lamports -= (txn_ctx->execution_fee + txn_ctx->priority_fee);
1829 :
1830 0 : accounts_to_save[acc_idx++] = &txn_ctx->borrowed_accounts[0];
1831 0 : for( ulong i=1UL; i<txn_ctx->accounts_cnt; i++ ) {
1832 0 : if( txn_ctx->nonce_accounts[i] ) {
1833 0 : ushort recent_blockhash_off = txn_ctx->txn_descriptor->recent_blockhash_off;
1834 0 : fd_hash_t * recent_blockhash = (fd_hash_t *)((uchar *)txn_ctx->_txn_raw->raw + recent_blockhash_off);
1835 0 : fd_block_hash_queue_t queue = slot_ctx->slot_bank.block_hash_queue;
1836 0 : ulong queue_sz = fd_hash_hash_age_pair_t_map_size( queue.ages_pool, queue.ages_root );
1837 0 : if( FD_UNLIKELY( !queue_sz ) ) {
1838 0 : FD_LOG_ERR(( "Blockhash queue is empty" ));
1839 0 : }
1840 :
1841 0 : if( !fd_executor_is_blockhash_valid_for_age( &queue, recent_blockhash, FD_RECENT_BLOCKHASHES_MAX_ENTRIES ) ) {
1842 0 : accounts_to_save[acc_idx++] = &txn_ctx->borrowed_accounts[i];
1843 0 : }
1844 0 : break;
1845 0 : }
1846 0 : }
1847 0 : } else {
1848 0 : int dirty_vote_acc = txn_ctx->dirty_vote_acc;
1849 0 : int dirty_stake_acc = txn_ctx->dirty_stake_acc;
1850 :
1851 0 : for( ulong i=0UL; i<txn_ctx->accounts_cnt; i++ ) {
1852 0 : if( !fd_txn_account_is_writable_idx( txn_ctx, (int)i ) ) {
1853 0 : continue;
1854 0 : }
1855 :
1856 0 : fd_borrowed_account_t * acc_rec = &txn_ctx->borrowed_accounts[i];
1857 :
1858 0 : if( dirty_vote_acc && !memcmp( acc_rec->const_meta->info.owner, &fd_solana_vote_program_id, sizeof(fd_pubkey_t) ) ) {
1859 0 : fd_vote_store_account( slot_ctx, acc_rec );
1860 0 : FD_SCRATCH_SCOPE_BEGIN {
1861 0 : fd_vote_state_versioned_t vsv[1];
1862 0 : fd_bincode_decode_ctx_t decode_vsv =
1863 0 : { .data = acc_rec->const_data,
1864 0 : .dataend = acc_rec->const_data + acc_rec->const_meta->dlen,
1865 0 : .valloc = fd_scratch_virtual() };
1866 :
1867 0 : int err = fd_vote_state_versioned_decode( vsv, &decode_vsv );
1868 0 : if( err ) break; /* out of scratch scope */
1869 :
1870 0 : fd_vote_block_timestamp_t const * ts = NULL;
1871 0 : switch( vsv->discriminant ) {
1872 0 : case fd_vote_state_versioned_enum_v0_23_5:
1873 0 : ts = &vsv->inner.v0_23_5.last_timestamp;
1874 0 : break;
1875 0 : case fd_vote_state_versioned_enum_v1_14_11:
1876 0 : ts = &vsv->inner.v1_14_11.last_timestamp;
1877 0 : break;
1878 0 : case fd_vote_state_versioned_enum_current:
1879 0 : ts = &vsv->inner.current.last_timestamp;
1880 0 : break;
1881 0 : default:
1882 0 : __builtin_unreachable();
1883 0 : }
1884 :
1885 0 : fd_vote_record_timestamp_vote_with_slot( slot_ctx, acc_rec->pubkey, ts->timestamp, ts->slot );
1886 0 : }
1887 0 : FD_SCRATCH_SCOPE_END;
1888 0 : }
1889 :
1890 0 : if( dirty_stake_acc && !memcmp( acc_rec->const_meta->info.owner, &fd_solana_stake_program_id, sizeof(fd_pubkey_t) ) ) {
1891 : // TODO: does this correctly handle stake account close?
1892 0 : fd_store_stake_delegation( slot_ctx, acc_rec );
1893 0 : }
1894 :
1895 0 : accounts_to_save[acc_idx++] = acc_rec;
1896 0 : }
1897 0 : }
1898 0 : }
1899 :
1900 : /* All the accounts have been accumulated and can be saved */
1901 :
1902 : // TODO: we need to use the txn ctx funk_txn, valloc, etc.
1903 0 : int err = fd_acc_mgr_save_many_tpool( slot_ctx->acc_mgr, slot_ctx->funk_txn, accounts_to_save, acc_idx, tpool );
1904 0 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
1905 0 : FD_LOG_ERR(( "failed to save edits to accounts" ));
1906 0 : return -1;
1907 0 : }
1908 :
1909 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
1910 0 : int ret = fd_funk_txn_merge_all_children( slot_ctx->acc_mgr->funk, slot_ctx->funk_txn, 0 );
1911 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
1912 :
1913 0 : if( FD_UNLIKELY( ret!=FD_FUNK_SUCCESS ) ) {
1914 0 : FD_LOG_ERR(( "failed merging funk transaction: (%i-%s) ", ret, fd_funk_strerror(ret) ));
1915 0 : }
1916 :
1917 0 : if( FD_LIKELY( slot_ctx->status_cache ) ) {
1918 0 : if( FD_UNLIKELY( !fd_txncache_insert_batch( slot_ctx->status_cache, status_insert, num_cache_txns ) ) ) {
1919 0 : FD_LOG_WARNING(("Status cache is full, this should not be possible"));
1920 0 : }
1921 0 : }
1922 :
1923 0 : return 0;
1924 0 : } FD_SCRATCH_SCOPE_END;
1925 0 : }
1926 :
1927 : struct fd_pubkey_map_node {
1928 : ulong pubkey;
1929 : uint hash;
1930 : };
1931 : typedef struct fd_pubkey_map_node fd_pubkey_map_node_t;
1932 :
1933 : #define MAP_NAME fd_pubkey_map
1934 0 : #define MAP_T fd_pubkey_map_node_t
1935 0 : #define MAP_KEY pubkey
1936 : // #define MAP_KEY_T fd_pubkey_t
1937 0 : #define MAP_KEY_T ulong
1938 : // #define MAP_KEY_NULL pubkey_null
1939 0 : #define MAP_KEY_NULL 0
1940 : // #define MAP_KEY_INVAL( k ) !( memcmp( &k, &pubkey_null, sizeof( fd_pubkey_t ) ) )
1941 0 : #define MAP_KEY_INVAL( k ) k==0
1942 : // #define MAP_KEY_EQUAL( k0, k1 ) !( memcmp( ( &k0 ), ( &k1 ), sizeof( fd_pubkey_t ) ) )
1943 0 : #define MAP_KEY_EQUAL( k0, k1 ) k0==k1
1944 : #define MAP_KEY_EQUAL_IS_SLOW 1
1945 : // #define MAP_KEY_HASH( key ) ( (uint)( fd_hash( 0UL, &key, sizeof( fd_pubkey_t ) ) ) )
1946 0 : #define MAP_KEY_HASH( key ) ( (uint)key )
1947 : #define MAP_MEMOIZE 1
1948 : #include "../../util/tmpl/fd_map_dynamic.c"
1949 :
1950 : /* return 0 on failure, 1 if exists, 2 if inserted */
1951 : static uint
1952 : fd_pubkey_map_insert_if_not_in( fd_pubkey_map_node_t * map,
1953 0 : fd_pubkey_t pubkey ) {
1954 : /* Check if entry already exists */
1955 0 : ulong h = fd_hash( 0UL, &pubkey, sizeof( fd_pubkey_t ) );
1956 0 : fd_pubkey_map_node_t * entry = fd_pubkey_map_query( map, h, NULL );
1957 0 : if( entry )
1958 0 : return 1;
1959 :
1960 : /* Insert new */
1961 0 : entry = fd_pubkey_map_insert( map, h );
1962 0 : if( FD_UNLIKELY( !entry ) ) return 0; /* check for internal map collision */
1963 :
1964 0 : return 2;
1965 0 : }
1966 :
1967 : void
1968 : fd_runtime_generate_wave( fd_execute_txn_task_info_t * task_infos,
1969 : ulong * prev_incomplete_txn_idxs,
1970 : ulong prev_incomplete_txn_idxs_cnt,
1971 : ulong prev_accounts_cnt,
1972 : ulong * incomplete_txn_idxs,
1973 : ulong * _incomplete_txn_idxs_cnt,
1974 : ulong * _incomplete_accounts_cnt,
1975 : fd_execute_txn_task_info_t * wave_task_infos,
1976 0 : ulong * _wave_task_infos_cnt ) {
1977 0 : FD_SCRATCH_SCOPE_BEGIN {
1978 0 : int lg_slot_cnt = fd_ulong_find_msb( prev_accounts_cnt ) + 1;
1979 0 : void * read_map_mem = fd_scratch_alloc( fd_pubkey_map_align(), fd_pubkey_map_footprint( lg_slot_cnt ) );
1980 0 : fd_pubkey_map_node_t * read_map = fd_pubkey_map_join( fd_pubkey_map_new( read_map_mem, lg_slot_cnt ) );
1981 :
1982 0 : void * write_map_mem = fd_scratch_alloc( fd_pubkey_map_align(), fd_pubkey_map_footprint( lg_slot_cnt ) );
1983 0 : fd_pubkey_map_node_t * write_map = fd_pubkey_map_join( fd_pubkey_map_new( write_map_mem, lg_slot_cnt ) );
1984 :
1985 0 : ulong incomplete_txn_idxs_cnt = 0;
1986 0 : ulong wave_task_infos_cnt = 0;
1987 0 : ulong accounts_in_wave = 0;
1988 0 : for( ulong i = 0; i < prev_incomplete_txn_idxs_cnt; i++ ) {
1989 0 : ulong txn_idx = prev_incomplete_txn_idxs[i];
1990 0 : uint is_executable_now = 1;
1991 0 : fd_execute_txn_task_info_t * task_info = &task_infos[txn_idx];
1992 : // if( FD_UNLIKELY( accounts_in_wave >= (() - FD_TXN_ACCT_ADDR_MAX) ) ) {
1993 : // incomplete_txn_idxs[incomplete_txn_idxs_cnt++] = txn_idx;
1994 : // continue;
1995 : // }
1996 :
1997 0 : for( ulong j = 0; j < task_info->txn_ctx->accounts_cnt; j++ ) {
1998 0 : ulong h = fd_hash( 0UL, &task_info->txn_ctx->accounts[j], sizeof( fd_pubkey_t ) );
1999 0 : if( fd_pubkey_map_query( write_map, h, NULL ) != NULL ) {
2000 0 : is_executable_now = 0;
2001 0 : break;
2002 0 : }
2003 0 : if( fd_txn_account_is_writable_idx( task_info->txn_ctx, (int)j ) ) {
2004 0 : if( fd_pubkey_map_query( read_map, h, NULL ) != NULL ) {
2005 0 : is_executable_now = 0;
2006 0 : break;
2007 0 : }
2008 0 : }
2009 0 : }
2010 :
2011 0 : if( !is_executable_now ) {
2012 0 : incomplete_txn_idxs[incomplete_txn_idxs_cnt++] = txn_idx;
2013 0 : } else {
2014 0 : wave_task_infos[wave_task_infos_cnt++] = *task_info;
2015 0 : }
2016 :
2017 : /* Include txn in wave */
2018 0 : for( ulong j = 0; j < task_info->txn_ctx->accounts_cnt; j++ ) {
2019 0 : if( fd_txn_account_is_writable_idx( task_info->txn_ctx, (int)j ) ) {
2020 0 : uint ins_res = fd_pubkey_map_insert_if_not_in( write_map, task_info->txn_ctx->accounts[j] );
2021 0 : if( ins_res == 2 ) {
2022 0 : accounts_in_wave++;
2023 0 : }
2024 0 : } else {
2025 0 : uint ins_res = fd_pubkey_map_insert_if_not_in( read_map, task_info->txn_ctx->accounts[j] );
2026 0 : if( ins_res == 2 ) {
2027 0 : accounts_in_wave++;
2028 0 : }
2029 0 : }
2030 0 : }
2031 :
2032 0 : }
2033 :
2034 0 : *_incomplete_txn_idxs_cnt = incomplete_txn_idxs_cnt;
2035 0 : *_incomplete_accounts_cnt = prev_accounts_cnt - accounts_in_wave;
2036 0 : *_wave_task_infos_cnt = wave_task_infos_cnt;
2037 0 : } FD_SCRATCH_SCOPE_END;
2038 0 : }
2039 :
2040 : int
2041 : fd_runtime_execute_pack_txns( fd_exec_slot_ctx_t * slot_ctx,
2042 : fd_spad_t * spad,
2043 : fd_capture_ctx_t * capture_ctx,
2044 : fd_txn_p_t * txns,
2045 0 : ulong txn_cnt ) {
2046 :
2047 0 : FD_SCRATCH_SCOPE_BEGIN {
2048 :
2049 0 : fd_execute_txn_task_info_t * task_infos = fd_scratch_alloc( 8, txn_cnt * sizeof(fd_execute_txn_task_info_t));
2050 :
2051 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
2052 0 : txns[i].flags = FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
2053 0 : }
2054 :
2055 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
2056 0 : fd_runtime_prepare_execute_finalize_txn( slot_ctx, spad, capture_ctx, &txns[i], &task_infos[i] );
2057 0 : }
2058 :
2059 0 : ulong curr_cnt = slot_ctx->slot_bank.transaction_count;
2060 0 : FD_COMPILER_MFENCE();
2061 0 : while( FD_UNLIKELY( FD_ATOMIC_CAS( &slot_ctx->slot_bank.transaction_count, curr_cnt, curr_cnt + txn_cnt ) != curr_cnt ) ) {
2062 0 : FD_SPIN_PAUSE();
2063 0 : curr_cnt = slot_ctx->slot_bank.transaction_count;
2064 0 : FD_COMPILER_MFENCE();
2065 0 : }
2066 :
2067 0 : return 0;
2068 0 : } FD_SCRATCH_SCOPE_END;
2069 :
2070 0 : }
2071 :
2072 : /* NOTE: Don't mess with this call without updating the transaction fuzzing harness appropriately!
2073 : fd_exec_instr_test.c:_txn_context_create_and_exec */
2074 : int
2075 : fd_runtime_execute_txns_in_waves_tpool( fd_exec_slot_ctx_t * slot_ctx,
2076 : fd_capture_ctx_t * capture_ctx,
2077 : fd_txn_p_t * all_txns,
2078 : ulong total_txn_cnt,
2079 : fd_tpool_t * tpool,
2080 : fd_spad_t * * spads,
2081 0 : ulong spad_cnt ) {
2082 0 : int dump_txn = capture_ctx && slot_ctx->slot_bank.slot >= capture_ctx->dump_proto_start_slot && capture_ctx->dump_txn_to_pb;
2083 :
2084 : /* As a note, the batch size of 128 is a relatively arbitrary number. The
2085 : notion of batching here will change as the transaction execution model
2086 : changes with respect to transaction execution. */
2087 0 : #define BATCH_SIZE (128UL)
2088 0 : ulong batch_size = fd_ulong_min( fd_tile_cnt(), BATCH_SIZE );
2089 :
2090 0 : for( ulong i=0UL; i<total_txn_cnt; i++ ) {
2091 0 : all_txns[i].flags = FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
2092 0 : }
2093 :
2094 0 : ulong num_batches = total_txn_cnt/batch_size;
2095 0 : ulong rem = total_txn_cnt%batch_size;
2096 0 : num_batches += rem ? 1UL : 0UL;
2097 :
2098 0 : int res = 0;
2099 0 : for( ulong i=0UL; i<num_batches; i++ ) {
2100 0 : FD_SCRATCH_SCOPE_BEGIN {
2101 :
2102 0 : fd_txn_p_t * txns = all_txns + (batch_size * i);
2103 0 : ulong txn_cnt = ((i+1UL==num_batches) && rem) ? rem : batch_size;
2104 :
2105 0 : fd_execute_txn_task_info_t * task_infos = fd_scratch_alloc( 8, txn_cnt * sizeof(fd_execute_txn_task_info_t));
2106 0 : fd_execute_txn_task_info_t * wave_task_infos = fd_scratch_alloc( 8, txn_cnt * sizeof(fd_execute_txn_task_info_t));
2107 0 : ulong wave_task_infos_cnt = 0;
2108 :
2109 0 : res = fd_runtime_prepare_txns_start( slot_ctx, task_infos, txns, txn_cnt );
2110 0 : if( res != 0 ) {
2111 0 : FD_LOG_DEBUG(("Fail prep 1"));
2112 0 : }
2113 :
2114 0 : ulong * incomplete_txn_idxs = fd_scratch_alloc( 8UL, txn_cnt * sizeof(ulong) );
2115 0 : ulong incomplete_txn_idxs_cnt = 0;
2116 0 : ulong incomplete_accounts_cnt = 0;
2117 :
2118 : /* Setup sanitized txns as incomplete and set the capture context */
2119 0 : for( ulong i = 0; i < txn_cnt; i++ ) {
2120 0 : if( FD_UNLIKELY( !( task_infos[i].txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
2121 0 : continue;
2122 0 : }
2123 0 : incomplete_txn_idxs[incomplete_txn_idxs_cnt++] = i;
2124 0 : incomplete_accounts_cnt += task_infos[i].txn_ctx->accounts_cnt;
2125 0 : task_infos[i].txn_ctx->capture_ctx = capture_ctx;
2126 0 : }
2127 :
2128 0 : ulong * next_incomplete_txn_idxs = fd_scratch_alloc( 8UL, txn_cnt * sizeof(ulong) );
2129 0 : ulong next_incomplete_txn_idxs_cnt = 0;
2130 0 : ulong next_incomplete_accounts_cnt = 0;
2131 :
2132 0 : while( incomplete_txn_idxs_cnt > 0 ) {
2133 0 : fd_runtime_generate_wave( task_infos, incomplete_txn_idxs, incomplete_txn_idxs_cnt, incomplete_accounts_cnt,
2134 0 : next_incomplete_txn_idxs, &next_incomplete_txn_idxs_cnt, &next_incomplete_accounts_cnt,
2135 0 : wave_task_infos, &wave_task_infos_cnt );
2136 0 : ulong * temp_incomplete_txn_idxs = incomplete_txn_idxs;
2137 0 : incomplete_txn_idxs = next_incomplete_txn_idxs;
2138 0 : next_incomplete_txn_idxs = temp_incomplete_txn_idxs;
2139 0 : incomplete_txn_idxs_cnt = next_incomplete_txn_idxs_cnt;
2140 :
2141 : // Dump txns in waves
2142 0 : if( dump_txn ) {
2143 0 : for( ulong i = 0; i < wave_task_infos_cnt; ++i ) {
2144 0 : dump_txn_to_protobuf( wave_task_infos[i].txn_ctx, spads[0] );
2145 0 : }
2146 0 : }
2147 :
2148 : /* Assign out spads to the transaction contexts */
2149 0 : for( ulong i=0UL; i<wave_task_infos_cnt; i++ ) {
2150 0 : wave_task_infos[i].spads = spads;
2151 0 : }
2152 :
2153 0 : res |= fd_runtime_verify_txn_signatures_tpool( wave_task_infos, wave_task_infos_cnt, tpool );
2154 0 : if( res != 0 ) {
2155 0 : FD_LOG_WARNING(("Fail signature verification"));
2156 0 : }
2157 :
2158 0 : res |= fd_runtime_prep_and_exec_txns_tpool( slot_ctx, wave_task_infos, wave_task_infos_cnt, tpool );
2159 0 : if( res != 0 ) {
2160 0 : FD_LOG_DEBUG(("Fail prep 2"));
2161 0 : }
2162 :
2163 0 : int finalize_res = fd_runtime_finalize_txns_tpool( slot_ctx, capture_ctx, wave_task_infos, wave_task_infos_cnt, tpool );
2164 0 : if( finalize_res != 0 ) {
2165 0 : FD_LOG_ERR(("Fail finalize"));
2166 0 : }
2167 :
2168 : /* Resetting the spad is a O(1) operation */
2169 0 : for( ulong i=0UL; i<spad_cnt; i++ ) {
2170 0 : fd_spad_reset( spads[i] );
2171 0 : }
2172 :
2173 : // wave_time += fd_log_wallclock();
2174 : // double wave_time_ms = (double)wave_time * 1e-6;
2175 : // cum_wave_time_ms += wave_time_ms;
2176 : // (void)cum_wave_time_ms;
2177 : // FD_LOG_INFO(( "wave executed - sz: %lu, accounts: %lu, elapsed: %6.6f ms, cum: %6.6f ms", wave_task_infos_cnt, incomplete_accounts_cnt - next_incomplete_accounts_cnt, wave_time_ms, cum_wave_time_ms ));
2178 0 : }
2179 0 : } FD_SCRATCH_SCOPE_END;
2180 0 : }
2181 0 : slot_ctx->slot_bank.transaction_count += total_txn_cnt;
2182 :
2183 0 : #undef BATCH_SIZE
2184 :
2185 0 : return res;
2186 0 : }
2187 :
2188 : // TODO: add tracking account_state hashes so that we can verify our
2189 : // banks hash... this has interesting threading implications since we
2190 : // could execute the cryptography in another thread for tracking this
2191 : // but we don't actually have anything to compare it to until we hit
2192 : // another snapshot... Probably we should just store the results into
2193 : // the slot_ctx state (a slot/hash map)?
2194 : //
2195 : // What slots exactly do cache'd account_updates go into? how are
2196 : // they hashed (which slot?)?
2197 :
2198 : int
2199 0 : fd_runtime_block_sysvar_update_pre_execute( fd_exec_slot_ctx_t * slot_ctx ) {
2200 : // let (fee_rate_governor, fee_components_time_us) = measure_us!(
2201 : // FeeRateGovernor::new_derived(&parent.fee_rate_governor, parent.signature_count())
2202 : // );
2203 : /* https://github.com/firedancer-io/solana/blob/dab3da8e7b667d7527565bddbdbecf7ec1fb868e/runtime/src/bank.rs#L1312-L1314 */
2204 0 : fd_sysvar_fees_new_derived(slot_ctx, slot_ctx->slot_bank.fee_rate_governor, slot_ctx->parent_signature_cnt);
2205 :
2206 : // TODO: move all these out to a fd_sysvar_update() call...
2207 0 : long clock_update_time = -fd_log_wallclock();
2208 0 : fd_sysvar_clock_update(slot_ctx);
2209 0 : clock_update_time += fd_log_wallclock();
2210 0 : double clock_update_time_ms = (double)clock_update_time * 1e-6;
2211 0 : FD_LOG_INFO(( "clock updated - slot: %lu, elapsed: %6.6f ms", slot_ctx->slot_bank.slot, clock_update_time_ms ));
2212 0 : if (!FD_FEATURE_ACTIVE(slot_ctx, disable_fees_sysvar))
2213 0 : fd_sysvar_fees_update(slot_ctx);
2214 : // It has to go into the current txn previous info but is not in slot 0
2215 0 : if (slot_ctx->slot_bank.slot != 0)
2216 0 : fd_sysvar_slot_hashes_update(slot_ctx);
2217 0 : fd_sysvar_last_restart_slot_update(slot_ctx);
2218 :
2219 0 : return 0;
2220 0 : }
2221 :
2222 : int
2223 0 : fd_runtime_block_update_current_leader( fd_exec_slot_ctx_t * slot_ctx ) {
2224 0 : ulong slot_rel;
2225 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
2226 0 : fd_slot_to_epoch(&epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot, &slot_rel);
2227 0 : slot_ctx->leader = fd_epoch_leaders_get( fd_exec_epoch_ctx_leaders( slot_ctx->epoch_ctx ), slot_ctx->slot_bank.slot );
2228 0 : if( slot_ctx->leader == NULL ) {
2229 0 : return -1;
2230 0 : }
2231 :
2232 0 : return 0;
2233 0 : }
2234 :
2235 : int
2236 0 : fd_runtime_block_execute_prepare( fd_exec_slot_ctx_t * slot_ctx ) {
2237 : /* Update block height */
2238 0 : slot_ctx->slot_bank.block_height += 1UL;
2239 0 : fd_blockstore_block_height_update(
2240 0 : slot_ctx->blockstore,
2241 0 : slot_ctx->slot_bank.slot,
2242 0 : slot_ctx->slot_bank.block_height );
2243 :
2244 : // TODO: this is not part of block execution, move it.
2245 0 : if( slot_ctx->slot_bank.slot != 0 ) {
2246 0 : slot_ctx->block = fd_blockstore_block_query( slot_ctx->blockstore, slot_ctx->slot_bank.slot );
2247 :
2248 0 : ulong slot_idx;
2249 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
2250 0 : ulong prev_epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_ctx->slot_bank.prev_slot, &slot_idx );
2251 0 : ulong new_epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot, &slot_idx );
2252 0 : if (slot_idx==1UL && new_epoch==0UL) {
2253 : /* the block after genesis has a height of 1*/
2254 0 : slot_ctx->slot_bank.block_height = 1UL;
2255 0 : }
2256 :
2257 0 : if( prev_epoch < new_epoch || slot_idx == 0 ) {
2258 0 : FD_LOG_DEBUG(("Epoch boundary"));
2259 : /* Epoch boundary! */
2260 0 : fd_funk_start_write(slot_ctx->acc_mgr->funk);
2261 0 : fd_process_new_epoch(slot_ctx, new_epoch - 1UL);
2262 0 : fd_funk_end_write(slot_ctx->acc_mgr->funk);
2263 0 : }
2264 0 : }
2265 :
2266 0 : slot_ctx->slot_bank.collected_execution_fees = 0;
2267 0 : slot_ctx->slot_bank.collected_priority_fees = 0;
2268 0 : slot_ctx->slot_bank.collected_rent = 0;
2269 0 : slot_ctx->signature_cnt = 0;
2270 :
2271 0 : if( slot_ctx->slot_bank.slot != 0 && (
2272 0 : FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ||
2273 0 : FD_FEATURE_ACTIVE( slot_ctx, partitioned_epoch_rewards_superfeature ) ) ) {
2274 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
2275 0 : fd_distribute_partitioned_epoch_rewards( slot_ctx );
2276 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
2277 0 : }
2278 :
2279 0 : int result = fd_runtime_block_update_current_leader( slot_ctx );
2280 0 : if (result != 0) {
2281 0 : FD_LOG_WARNING(("updating current leader"));
2282 0 : return result;
2283 0 : }
2284 :
2285 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
2286 0 : result = fd_runtime_block_sysvar_update_pre_execute( slot_ctx );
2287 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
2288 0 : if (result != 0) {
2289 0 : FD_LOG_WARNING(("updating sysvars failed"));
2290 0 : return result;
2291 0 : }
2292 :
2293 : /* Load sysvars into cache */
2294 0 : if( FD_UNLIKELY( result = fd_runtime_sysvar_cache_load( slot_ctx ) ) ) {
2295 : /* non-zero error */
2296 0 : return result;
2297 0 : }
2298 :
2299 0 : return FD_RUNTIME_EXECUTE_SUCCESS;
2300 0 : }
2301 :
2302 : int fd_runtime_block_execute_finalize(fd_exec_slot_ctx_t *slot_ctx,
2303 : fd_capture_ctx_t *capture_ctx,
2304 0 : fd_block_info_t const *block_info) {
2305 0 : fd_sysvar_slot_history_update(slot_ctx);
2306 :
2307 : // this slot is frozen... and cannot change anymore...
2308 0 : fd_runtime_freeze(slot_ctx);
2309 :
2310 0 : int result = fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, slot_ctx->funk_txn );
2311 0 : if( result != 0 ) {
2312 0 : FD_LOG_WARNING(("update bpf program cache failed"));
2313 0 : return result;
2314 0 : }
2315 :
2316 0 : result = fd_update_hash_bank(slot_ctx, capture_ctx, &slot_ctx->slot_bank.banks_hash, block_info->signature_cnt);
2317 0 : if( result != FD_EXECUTOR_INSTR_SUCCESS ) {
2318 0 : FD_LOG_WARNING(("hashing bank failed"));
2319 0 : return result;
2320 0 : }
2321 :
2322 : // result = fd_runtime_save_epoch_bank( slot_ctx );
2323 : // if (result != FD_EXECUTOR_INSTR_SUCCESS) {
2324 : // FD_LOG_WARNING(( "save epoch bank failed" ));
2325 : // return result;
2326 : // }
2327 :
2328 0 : result = fd_runtime_save_slot_bank(slot_ctx);
2329 0 : if( result != FD_RUNTIME_EXECUTE_SUCCESS ) {
2330 0 : FD_LOG_WARNING(("failed to save slot bank"));
2331 0 : return result;
2332 0 : }
2333 :
2334 0 : return FD_RUNTIME_EXECUTE_SUCCESS;
2335 0 : }
2336 :
2337 : int
2338 : fd_runtime_block_execute_finalize_tpool( fd_exec_slot_ctx_t * slot_ctx,
2339 : fd_capture_ctx_t * capture_ctx,
2340 : fd_block_info_t const * block_info,
2341 0 : fd_tpool_t * tpool ) {
2342 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
2343 :
2344 0 : fd_sysvar_slot_history_update(slot_ctx);
2345 :
2346 : // this slot is frozen... and cannot change anymore...
2347 0 : fd_runtime_freeze(slot_ctx);
2348 :
2349 0 : int result = fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, slot_ctx->funk_txn );
2350 0 : if( result != 0 ) {
2351 0 : FD_LOG_WARNING(("update bpf program cache failed"));
2352 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
2353 0 : return result;
2354 0 : }
2355 :
2356 0 : result = fd_update_hash_bank_tpool(slot_ctx, capture_ctx, &slot_ctx->slot_bank.banks_hash, block_info->signature_cnt, tpool );
2357 0 : if( result != FD_EXECUTOR_INSTR_SUCCESS ) {
2358 0 : FD_LOG_WARNING(("hashing bank failed"));
2359 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
2360 0 : return result;
2361 0 : }
2362 :
2363 : // result = fd_runtime_save_epoch_bank( slot_ctx );
2364 : // if (result != FD_EXECUTOR_INSTR_SUCCESS) {
2365 : // FD_LOG_WARNING(( "save epoch bank failed" ));
2366 : // return result;
2367 : // }
2368 :
2369 0 : result = fd_runtime_save_slot_bank(slot_ctx);
2370 0 : if( result != FD_RUNTIME_EXECUTE_SUCCESS ) {
2371 0 : FD_LOG_WARNING(("failed to save slot bank"));
2372 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
2373 0 : return result;
2374 0 : }
2375 :
2376 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
2377 :
2378 0 : slot_ctx->total_compute_units_requested = 0;
2379 0 : for ( fd_account_compute_table_iter_t iter = fd_account_compute_table_iter_init( slot_ctx->account_compute_table );
2380 0 : !fd_account_compute_table_iter_done( slot_ctx->account_compute_table, iter );
2381 0 : iter = fd_account_compute_table_iter_next( slot_ctx->account_compute_table, iter ) ) {
2382 0 : fd_account_compute_elem_t * e = fd_account_compute_table_iter_ele( slot_ctx->account_compute_table, iter );
2383 0 : fd_account_compute_table_remove( slot_ctx->account_compute_table, &e->key );
2384 0 : }
2385 0 : return FD_RUNTIME_EXECUTE_SUCCESS;
2386 0 : }
2387 :
2388 : int
2389 : fd_runtime_block_execute_tpool_v2( fd_exec_slot_ctx_t * slot_ctx,
2390 : fd_capture_ctx_t * capture_ctx,
2391 : fd_block_info_t const * block_info,
2392 : fd_tpool_t * tpool,
2393 : fd_spad_t * * spads,
2394 0 : ulong spad_cnt ) {
2395 0 : FD_SCRATCH_SCOPE_BEGIN {
2396 0 : if ( capture_ctx != NULL && capture_ctx->capture ) {
2397 0 : fd_solcap_writer_set_slot( capture_ctx->capture, slot_ctx->slot_bank.slot );
2398 0 : }
2399 :
2400 0 : long block_execute_time = -fd_log_wallclock();
2401 :
2402 0 : int res = fd_runtime_block_execute_prepare( slot_ctx );
2403 0 : if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
2404 0 : return res;
2405 0 : }
2406 :
2407 0 : ulong txn_cnt = block_info->txn_cnt;
2408 0 : fd_txn_p_t * txn_ptrs = fd_scratch_alloc( alignof(fd_txn_p_t), txn_cnt * sizeof(fd_txn_p_t) );
2409 :
2410 0 : fd_runtime_block_collect_txns( block_info, txn_ptrs );
2411 :
2412 0 : res = fd_runtime_execute_txns_in_waves_tpool( slot_ctx, capture_ctx, txn_ptrs, txn_cnt, tpool, spads, spad_cnt );
2413 0 : if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
2414 0 : return res;
2415 0 : }
2416 :
2417 0 : long block_finalize_time = -fd_log_wallclock();
2418 0 : res = fd_runtime_block_execute_finalize_tpool( slot_ctx, capture_ctx, block_info, tpool );
2419 0 : if( res != FD_RUNTIME_EXECUTE_SUCCESS ) {
2420 0 : return res;
2421 0 : }
2422 :
2423 0 : slot_ctx->slot_bank.transaction_count += txn_cnt;
2424 :
2425 0 : block_finalize_time += fd_log_wallclock();
2426 0 : double block_finalize_time_ms = (double)block_finalize_time * 1e-6;
2427 0 : FD_LOG_INFO(( "finalized block successfully - slot: %lu, elapsed: %6.6f ms", slot_ctx->slot_bank.slot, block_finalize_time_ms ));
2428 :
2429 0 : block_execute_time += fd_log_wallclock();
2430 0 : double block_execute_time_ms = (double)block_execute_time * 1e-6;
2431 :
2432 0 : FD_LOG_INFO(( "executed block successfully - slot: %lu, elapsed: %6.6f ms", slot_ctx->slot_bank.slot, block_execute_time_ms ));
2433 :
2434 0 : return FD_RUNTIME_EXECUTE_SUCCESS;
2435 0 : } FD_SCRATCH_SCOPE_END;
2436 0 : }
2437 :
2438 :
2439 : struct fd_poh_verification_info {
2440 : fd_microblock_info_t const *microblock_info;
2441 : fd_hash_t const *in_poh_hash;
2442 : int success;
2443 : };
2444 : typedef struct fd_poh_verification_info fd_poh_verification_info_t;
2445 :
2446 : void fd_runtime_microblock_verify_info_collect( fd_microblock_info_t const *microblock_info,
2447 : fd_hash_t const *in_poh_hash,
2448 0 : fd_poh_verification_info_t *poh_verification_info ) {
2449 0 : poh_verification_info->microblock_info = microblock_info;
2450 0 : poh_verification_info->in_poh_hash = in_poh_hash;
2451 0 : poh_verification_info->success = 0;
2452 0 : }
2453 :
2454 : void fd_runtime_microblock_batch_verify_info_collect(fd_microblock_batch_info_t const *microblock_batch_info,
2455 : fd_hash_t const *in_poh_hash,
2456 : fd_poh_verification_info_t *poh_verification_info)
2457 0 : {
2458 0 : for (ulong i = 0; i < microblock_batch_info->microblock_cnt; i++)
2459 0 : {
2460 0 : fd_microblock_info_t const *microblock_info = µblock_batch_info->microblock_infos[i];
2461 0 : fd_runtime_microblock_verify_info_collect(microblock_info, in_poh_hash, &poh_verification_info[i]);
2462 0 : in_poh_hash = (fd_hash_t const *)µblock_info->microblock_hdr.hash;
2463 0 : }
2464 0 : }
2465 :
2466 : void fd_runtime_block_verify_info_collect(fd_block_info_t const *block_info,
2467 : fd_hash_t const *in_poh_hash,
2468 : fd_poh_verification_info_t *poh_verification_info)
2469 0 : {
2470 0 : for (ulong i = 0; i < block_info->microblock_batch_cnt; i++)
2471 0 : {
2472 0 : fd_microblock_batch_info_t const *microblock_batch_info = &block_info->microblock_batch_infos[i];
2473 :
2474 0 : fd_runtime_microblock_batch_verify_info_collect(microblock_batch_info, in_poh_hash, poh_verification_info);
2475 0 : in_poh_hash = (fd_hash_t const *)poh_verification_info[microblock_batch_info->microblock_cnt - 1].microblock_info->microblock_hdr.hash;
2476 0 : poh_verification_info += microblock_batch_info->microblock_cnt;
2477 0 : }
2478 0 : }
2479 :
2480 : static void FD_FN_UNUSED
2481 : fd_runtime_poh_verify_task( void *tpool,
2482 : ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
2483 : void *args FD_PARAM_UNUSED,
2484 : void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
2485 : ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
2486 : ulong m0, ulong m1 FD_PARAM_UNUSED,
2487 0 : ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED ) {
2488 0 : fd_poh_verification_info_t *poh_info = (fd_poh_verification_info_t *)tpool + m0;
2489 0 : fd_bmtree_commit_t commit_mem[1];
2490 0 :
2491 0 : fd_hash_t out_poh_hash = *poh_info->in_poh_hash;
2492 0 :
2493 0 : fd_microblock_info_t const *microblock_info = poh_info->microblock_info;
2494 0 : ulong hash_cnt = microblock_info->microblock_hdr.hash_cnt;
2495 0 : ulong txn_cnt = microblock_info->microblock_hdr.txn_cnt;
2496 0 :
2497 0 : if (txn_cnt == 0) {
2498 0 : fd_poh_append(&out_poh_hash, hash_cnt);
2499 0 : } else {
2500 0 : if (hash_cnt > 0) {
2501 0 : fd_poh_append(&out_poh_hash, hash_cnt - 1);
2502 0 : }
2503 0 :
2504 0 : fd_bmtree_commit_t *tree = fd_bmtree_commit_init(commit_mem, 32UL, 1UL, 0UL);
2505 0 :
2506 0 : /* Loop across transactions */
2507 0 : for (ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++) {
2508 0 : fd_txn_p_t * txn_p = µblock_info->txns[txn_idx];
2509 0 :
2510 0 : fd_txn_t const *txn = (fd_txn_t const *) txn_p->_;
2511 0 : fd_rawtxn_b_t const raw_txn[1] = {{.raw = txn_p->payload, .txn_sz = (ushort)txn_p->payload_sz}};
2512 0 :
2513 0 : /* Loop across signatures */
2514 0 : fd_ed25519_sig_t const *sigs = (fd_ed25519_sig_t const *)((ulong)raw_txn->raw + (ulong)txn->signature_off);
2515 0 : for (ulong j = 0; j < txn->signature_cnt; j++) {
2516 0 : fd_bmtree_node_t leaf;
2517 0 : fd_bmtree_hash_leaf(&leaf, &sigs[j], sizeof(fd_ed25519_sig_t), 1);
2518 0 : fd_bmtree_commit_append(tree, (fd_bmtree_node_t const *)&leaf, 1);
2519 0 : }
2520 0 : }
2521 0 :
2522 0 : uchar *root = fd_bmtree_commit_fini(tree);
2523 0 : fd_poh_mixin(&out_poh_hash, root);
2524 0 : }
2525 0 :
2526 0 : if (FD_UNLIKELY(0 != memcmp(microblock_info->microblock_hdr.hash, out_poh_hash.hash, sizeof(fd_hash_t)))) {
2527 0 : FD_LOG_WARNING(( "poh mismatch (bank: %s, entry: %s)", FD_BASE58_ENC_32_ALLOCA( out_poh_hash.hash ), FD_BASE58_ENC_32_ALLOCA( microblock_info->microblock_hdr.hash ) ));
2528 0 : poh_info->success = -1;
2529 0 : }
2530 0 : }
2531 :
2532 : static void
2533 : fd_runtime_poh_verify_wide_task( void *tpool,
2534 : ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
2535 : void *args FD_PARAM_UNUSED,
2536 : void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
2537 : ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
2538 : ulong m0, ulong m1 FD_PARAM_UNUSED,
2539 0 : ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED ) {
2540 0 : fd_poh_verification_info_t * poh_info = (fd_poh_verification_info_t *)tpool + m0;
2541 :
2542 0 : fd_hash_t out_poh_hash = *poh_info->in_poh_hash;
2543 :
2544 0 : fd_microblock_info_t const *microblock_info = poh_info->microblock_info;
2545 0 : ulong hash_cnt = microblock_info->microblock_hdr.hash_cnt;
2546 0 : ulong txn_cnt = microblock_info->microblock_hdr.txn_cnt;
2547 :
2548 0 : if( txn_cnt == 0 ) {
2549 0 : fd_poh_append( &out_poh_hash, hash_cnt );
2550 0 : } else {
2551 0 : if( hash_cnt > 0 ) {
2552 0 : fd_poh_append(&out_poh_hash, hash_cnt - 1);
2553 0 : }
2554 :
2555 0 : ulong leaf_cnt = microblock_info->signature_cnt;
2556 0 : unsigned char * commit = fd_alloca_check( FD_WBMTREE32_ALIGN, fd_wbmtree32_footprint(leaf_cnt));
2557 0 : fd_wbmtree32_leaf_t * leafs = fd_alloca_check(alignof(fd_wbmtree32_leaf_t), sizeof(fd_wbmtree32_leaf_t) * leaf_cnt);
2558 0 : unsigned char * mbuf = fd_alloca_check(1UL, leaf_cnt * (sizeof(fd_ed25519_sig_t) + 1));
2559 :
2560 0 : fd_wbmtree32_t *tree = fd_wbmtree32_init(commit, leaf_cnt);
2561 0 : fd_wbmtree32_leaf_t *l = &leafs[0];
2562 :
2563 : /* Loop across transactions */
2564 0 : for (ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++) {
2565 0 : fd_txn_p_t * txn_p = µblock_info->txns[txn_idx];
2566 :
2567 0 : fd_txn_t const *txn = (fd_txn_t const *) txn_p->_;
2568 0 : fd_rawtxn_b_t const raw_txn[1] = {{.raw = txn_p->payload, .txn_sz = (ushort)txn_p->payload_sz}};
2569 :
2570 : /* Loop across signatures */
2571 0 : fd_ed25519_sig_t const *sigs = (fd_ed25519_sig_t const *)((ulong)raw_txn->raw + (ulong)txn->signature_off);
2572 0 : for( ulong j = 0; j < txn->signature_cnt; j++ ) {
2573 0 : l->data = (uchar *)&sigs[j];
2574 0 : l->data_len = sizeof(fd_ed25519_sig_t);
2575 0 : l++;
2576 0 : }
2577 0 : }
2578 :
2579 0 : fd_wbmtree32_append(tree, leafs, leaf_cnt, mbuf);
2580 0 : uchar *root = fd_wbmtree32_fini(tree);
2581 0 : fd_poh_mixin(&out_poh_hash, root);
2582 0 : }
2583 :
2584 0 : if (FD_UNLIKELY(0 != memcmp(microblock_info->microblock_hdr.hash, out_poh_hash.hash, sizeof(fd_hash_t)))) {
2585 0 : FD_LOG_WARNING(( "poh mismatch (bank: %s, entry: %s)", FD_BASE58_ENC_32_ALLOCA( out_poh_hash.hash ), FD_BASE58_ENC_32_ALLOCA( microblock_info->microblock_hdr.hash ) ));
2586 0 : poh_info->success = -1;
2587 0 : }
2588 0 : }
2589 :
2590 : int
2591 : fd_runtime_poh_verify_tpool( fd_poh_verification_info_t *poh_verification_info,
2592 : ulong poh_verification_info_cnt,
2593 0 : fd_tpool_t * tpool ) {
2594 0 : fd_tpool_exec_all_rrobin(tpool, 0, fd_tpool_worker_cnt( tpool ), fd_runtime_poh_verify_wide_task, poh_verification_info, NULL, NULL, 1, 0, poh_verification_info_cnt);
2595 :
2596 0 : for (ulong i = 0; i < poh_verification_info_cnt; i++) {
2597 0 : if (poh_verification_info[i].success != 0)
2598 0 : {
2599 0 : return -1;
2600 0 : }
2601 0 : }
2602 :
2603 0 : return 0;
2604 0 : }
2605 :
2606 : int fd_runtime_block_verify_tpool(fd_block_info_t const *block_info,
2607 : fd_hash_t const *in_poh_hash,
2608 : fd_hash_t *out_poh_hash,
2609 : fd_valloc_t valloc,
2610 0 : fd_tpool_t *tpool) {
2611 0 : long block_verify_time = -fd_log_wallclock();
2612 :
2613 0 : fd_hash_t tmp_in_poh_hash = *in_poh_hash;
2614 0 : ulong poh_verification_info_cnt = block_info->microblock_cnt;
2615 0 : fd_poh_verification_info_t *poh_verification_info = fd_valloc_malloc(valloc,
2616 0 : alignof(fd_poh_verification_info_t),
2617 0 : poh_verification_info_cnt * sizeof(fd_poh_verification_info_t));
2618 0 : fd_runtime_block_verify_info_collect(block_info, &tmp_in_poh_hash, poh_verification_info);
2619 0 : int result = fd_runtime_poh_verify_tpool(poh_verification_info, poh_verification_info_cnt, tpool );
2620 0 : fd_memcpy(out_poh_hash->hash, poh_verification_info[poh_verification_info_cnt - 1].microblock_info->microblock_hdr.hash, sizeof(fd_hash_t));
2621 0 : fd_valloc_free(valloc, poh_verification_info);
2622 :
2623 0 : block_verify_time += fd_log_wallclock();
2624 0 : double block_verify_time_ms = (double)block_verify_time * 1e-6;
2625 :
2626 0 : FD_LOG_INFO(("verified block successfully - elapsed: %6.6f ms", block_verify_time_ms));
2627 :
2628 0 : return result;
2629 0 : }
2630 :
2631 : int fd_runtime_microblock_wide_verify(fd_microblock_info_t const *microblock_info,
2632 : fd_hash_t const *in_poh_hash,
2633 0 : fd_hash_t *out_poh_hash) {
2634 0 : ulong hash_cnt = microblock_info->microblock_hdr.hash_cnt;
2635 0 : ulong txn_cnt = microblock_info->microblock_hdr.txn_cnt;
2636 0 : FD_LOG_WARNING(( "poh input %lu %lu %s %s", hash_cnt, txn_cnt, FD_BASE58_ENC_32_ALLOCA( in_poh_hash->hash ), FD_BASE58_ENC_32_ALLOCA( microblock_info->microblock_hdr.hash ) ));
2637 :
2638 0 : *out_poh_hash = *in_poh_hash;
2639 :
2640 0 : if (txn_cnt == 0)
2641 0 : fd_poh_append(out_poh_hash, hash_cnt);
2642 0 : else
2643 0 : {
2644 0 : if (hash_cnt > 0)
2645 0 : fd_poh_append(out_poh_hash, hash_cnt - 1);
2646 :
2647 0 : ulong leaf_cnt = 0;
2648 0 : for (ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++)
2649 0 : {
2650 0 : fd_txn_p_t * txn_p = µblock_info->txns[txn_idx];
2651 0 : fd_txn_t const *txn = (fd_txn_t const *) txn_p->_;
2652 0 : leaf_cnt += txn->signature_cnt;
2653 0 : }
2654 :
2655 : // TODO: optimize this... .. and, we cannot use alloca...
2656 0 : unsigned char *commit = fd_alloca_check(FD_WBMTREE32_ALIGN, fd_wbmtree32_footprint(leaf_cnt));
2657 0 : fd_wbmtree32_leaf_t *leafs = fd_alloca_check(alignof(fd_wbmtree32_leaf_t), sizeof(fd_wbmtree32_leaf_t) * leaf_cnt);
2658 0 : unsigned char *mbuf = fd_alloca_check(1UL, leaf_cnt * (sizeof(fd_ed25519_sig_t) + 1));
2659 :
2660 0 : fd_wbmtree32_t *tree = fd_wbmtree32_init(commit, leaf_cnt);
2661 0 : fd_wbmtree32_leaf_t *l = &leafs[0];
2662 :
2663 : /* Loop acro ss transactions */
2664 0 : for( ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++ ) {
2665 0 : fd_txn_p_t * txn_p = µblock_info->txns[txn_idx];
2666 :
2667 0 : fd_txn_t const *txn = (fd_txn_t const *) txn_p->_;
2668 0 : fd_rawtxn_b_t const raw_txn[1] = {{.raw = txn_p->payload, .txn_sz = (ushort)txn_p->payload_sz}};
2669 :
2670 : /* Loop across signatures */
2671 0 : fd_ed25519_sig_t const *sigs = (fd_ed25519_sig_t const *)((ulong)raw_txn->raw + (ulong)txn->signature_off);
2672 0 : for( ulong j = 0; j < txn->signature_cnt; j++ ) {
2673 0 : l->data = (uchar *)&sigs[j];
2674 0 : l->data_len = sizeof(fd_ed25519_sig_t);
2675 0 : l++;
2676 0 : }
2677 0 : }
2678 :
2679 0 : fd_wbmtree32_append(tree, leafs, leaf_cnt, mbuf);
2680 0 : uchar *root = fd_wbmtree32_fini(tree);
2681 0 : fd_poh_mixin(out_poh_hash, root);
2682 0 : }
2683 :
2684 0 : if (FD_UNLIKELY(0 != memcmp(microblock_info->microblock_hdr.hash, out_poh_hash->hash, sizeof(fd_hash_t))))
2685 0 : {
2686 0 : FD_LOG_WARNING(( "poh mismatch (bank: %s, entry: %s)", FD_BASE58_ENC_32_ALLOCA( out_poh_hash->hash ), FD_BASE58_ENC_32_ALLOCA( microblock_info->microblock_hdr.hash ) ));
2687 0 : return -1;
2688 0 : }
2689 :
2690 0 : return 0;
2691 0 : }
2692 :
2693 : int fd_runtime_microblock_verify(fd_microblock_info_t const *microblock_info,
2694 : fd_hash_t const * in_poh_hash,
2695 0 : fd_hash_t * out_poh_hash) {
2696 0 : fd_bmtree_commit_t commit_mem[1];
2697 :
2698 0 : *out_poh_hash = *in_poh_hash;
2699 :
2700 0 : ulong hash_cnt = microblock_info->microblock_hdr.hash_cnt;
2701 0 : ulong txn_cnt = microblock_info->microblock_hdr.txn_cnt;
2702 :
2703 0 : if (txn_cnt == 0) {
2704 0 : fd_poh_append(out_poh_hash, hash_cnt);
2705 0 : } else {
2706 0 : if (hash_cnt > 0) {
2707 0 : fd_poh_append(out_poh_hash, hash_cnt - 1);
2708 0 : }
2709 :
2710 0 : fd_bmtree_commit_t *tree = fd_bmtree_commit_init(commit_mem, 32UL, 1UL, 0UL);
2711 :
2712 : /* Loop across transactions */
2713 0 : for (ulong txn_idx = 0; txn_idx < txn_cnt; txn_idx++) {
2714 0 : fd_txn_p_t * txn_p = µblock_info->txns[txn_idx];
2715 :
2716 0 : fd_txn_t const *txn = (fd_txn_t const *) txn_p->_;
2717 0 : fd_rawtxn_b_t const raw_txn[1] = {{.raw = txn_p->payload, .txn_sz = (ushort)txn_p->payload_sz}};
2718 :
2719 : /* Loop across signatures */
2720 0 : fd_ed25519_sig_t const *sigs = (fd_ed25519_sig_t const *)((ulong)raw_txn->raw + (ulong)txn->signature_off);
2721 0 : for (ulong j = 0; j < txn->signature_cnt; j++) {
2722 0 : fd_bmtree_node_t leaf;
2723 0 : fd_bmtree_hash_leaf(&leaf, &sigs[j], sizeof(fd_ed25519_sig_t), 1);
2724 0 : fd_bmtree_commit_append(tree, (fd_bmtree_node_t const *)&leaf, 1);
2725 0 : }
2726 0 : }
2727 :
2728 0 : uchar *root = fd_bmtree_commit_fini(tree);
2729 0 : fd_poh_mixin(out_poh_hash, root);
2730 0 : }
2731 :
2732 0 : if (FD_UNLIKELY(0 != memcmp(microblock_info->microblock_hdr.hash, out_poh_hash->hash, sizeof(fd_hash_t)))) {
2733 0 : FD_LOG_WARNING(("poh mismatch (bank: %s, entry: %s)", FD_BASE58_ENC_32_ALLOCA( out_poh_hash->hash ), FD_BASE58_ENC_32_ALLOCA( microblock_info->microblock_hdr.hash) ));
2734 0 : return -1;
2735 0 : }
2736 :
2737 0 : return 0;
2738 0 : }
2739 :
2740 : int fd_runtime_microblock_batch_verify(fd_microblock_batch_info_t const *microblock_batch_info,
2741 : fd_hash_t const *in_poh_hash,
2742 0 : fd_hash_t *out_poh_hash) {
2743 0 : fd_hash_t tmp_poh_hash = *in_poh_hash;
2744 0 : for (ulong i = 0; i < microblock_batch_info->microblock_cnt; i++)
2745 0 : {
2746 0 : if (fd_runtime_microblock_wide_verify(µblock_batch_info->microblock_infos[i], &tmp_poh_hash, out_poh_hash) != 0)
2747 0 : {
2748 0 : FD_LOG_WARNING(("poh mismatch in microblock - idx: %lu", i));
2749 0 : return -1;
2750 0 : }
2751 :
2752 0 : tmp_poh_hash = *out_poh_hash;
2753 0 : }
2754 :
2755 0 : return 0;
2756 0 : }
2757 :
2758 : // TODO: add back in the total_hashes == bank.hashes_per_slot
2759 : // TODO: add solana txn verify to this as well since, again, it can be
2760 : // done in parallel...
2761 : int
2762 : fd_runtime_block_verify(fd_block_info_t const *block_info,
2763 : fd_hash_t const *in_poh_hash,
2764 0 : fd_hash_t *out_poh_hash) {
2765 0 : fd_hash_t tmp_poh_hash = *in_poh_hash;
2766 0 : for( ulong i = 0; i < block_info->microblock_batch_cnt; i++ ) {
2767 0 : if( fd_runtime_microblock_batch_verify(&block_info->microblock_batch_infos[i], &tmp_poh_hash, out_poh_hash) != 0 ) {
2768 0 : FD_LOG_WARNING(("poh mismatch in microblock batch - idx: %lu", i));
2769 0 : return -1;
2770 0 : }
2771 :
2772 0 : tmp_poh_hash = *out_poh_hash;
2773 0 : }
2774 :
2775 0 : return 0;
2776 0 : }
2777 :
2778 : // int
2779 : // fd_runtime_slot_bank_from_parent(child_slot_ctx ) {
2780 : // child_slot_bank.collected_fees = 0;
2781 : // child_slot_ctx->slot_bank.collected_rent = 0;
2782 : // child_slot_ctx->slot_bank.max_tick_height = (slot + 1) * slot_ctx->epoch_ctx->epoch_bank->ticks_per_slot;
2783 : // }
2784 :
2785 : void
2786 : fd_runtime_checkpt( fd_capture_ctx_t * capture_ctx,
2787 : fd_exec_slot_ctx_t * slot_ctx,
2788 0 : ulong slot ) {
2789 0 : int is_checkpt_freq = capture_ctx != NULL && slot % capture_ctx->checkpt_freq == 0;
2790 0 : int is_abort_slot = slot == ULONG_MAX;
2791 0 : if( !is_checkpt_freq && !is_abort_slot ) {
2792 0 : return;
2793 0 : }
2794 :
2795 0 : if( capture_ctx->checkpt_path != NULL ) {
2796 0 : if( !is_abort_slot ) {
2797 0 : FD_LOG_NOTICE(( "checkpointing at slot=%lu to file=%s", slot, capture_ctx->checkpt_path ));
2798 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
2799 0 : } else {
2800 0 : FD_LOG_NOTICE(( "checkpointing after mismatch to file=%s", capture_ctx->checkpt_path ));
2801 0 : }
2802 :
2803 0 : unlink( capture_ctx->checkpt_path );
2804 0 : int err = fd_wksp_checkpt( fd_funk_wksp( slot_ctx->acc_mgr->funk ), capture_ctx->checkpt_path, 0666, 0, NULL );
2805 0 : if ( err ) {
2806 0 : FD_LOG_ERR(( "backup failed: error %d", err ));
2807 0 : }
2808 :
2809 0 : if( !is_abort_slot ) {
2810 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
2811 0 : }
2812 0 : }
2813 :
2814 0 : if( capture_ctx->checkpt_archive != NULL ) {
2815 0 : if( !is_abort_slot ) {
2816 0 : FD_LOG_NOTICE(( "archiving at slot=%lu to file=%s", slot, capture_ctx->checkpt_archive ));
2817 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
2818 0 : } else {
2819 0 : FD_LOG_NOTICE(( "archiving after mismatch to file=%s", capture_ctx->checkpt_archive ));
2820 0 : }
2821 :
2822 0 : int err = fd_funk_archive( slot_ctx->acc_mgr->funk, capture_ctx->checkpt_archive );
2823 0 : if ( err ) {
2824 0 : FD_LOG_ERR(( "archive failed: error %d", err ));
2825 0 : }
2826 :
2827 0 : if( !is_abort_slot ) {
2828 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
2829 0 : }
2830 0 : }
2831 0 : }
2832 :
2833 : static int
2834 : fd_runtime_publish_old_txns( fd_exec_slot_ctx_t * slot_ctx,
2835 : fd_capture_ctx_t * capture_ctx,
2836 0 : fd_tpool_t * tpool ) {
2837 : /* Publish any transaction older than 31 slots */
2838 0 : fd_funk_t * funk = slot_ctx->acc_mgr->funk;
2839 0 : fd_funk_txn_t * txnmap = fd_funk_txn_map(funk, fd_funk_wksp(funk));
2840 0 : uint depth = 0;
2841 0 : for( fd_funk_txn_t * txn = slot_ctx->funk_txn; txn; txn = fd_funk_txn_parent(txn, txnmap) ) {
2842 : /* TODO: tmp change */
2843 0 : if (++depth == (FD_RUNTIME_NUM_ROOT_BLOCKS - 1) ) {
2844 0 : FD_LOG_DEBUG(("publishing %s (slot %lu)", FD_BASE58_ENC_32_ALLOCA( &txn->xid ), txn->xid.ul[0]));
2845 :
2846 0 : fd_funk_start_write(funk);
2847 0 : ulong publish_err = fd_funk_txn_publish(funk, txn, 1);
2848 0 : if (publish_err == 0) {
2849 0 : FD_LOG_ERR(("publish err"));
2850 0 : return -1;
2851 0 : }
2852 0 : if( slot_ctx->status_cache ) {
2853 0 : fd_txncache_register_root_slot( slot_ctx->status_cache, txn->xid.ul[0] );
2854 0 : }
2855 :
2856 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
2857 0 : if( txn->xid.ul[0] >= epoch_bank->eah_start_slot ) {
2858 0 : fd_accounts_hash( slot_ctx, tpool, &slot_ctx->slot_bank.epoch_account_hash );
2859 0 : epoch_bank->eah_start_slot = ULONG_MAX;
2860 0 : }
2861 :
2862 0 : if( capture_ctx != NULL ) {
2863 0 : fd_runtime_checkpt( capture_ctx, slot_ctx, txn->xid.ul[0] );
2864 0 : }
2865 :
2866 0 : fd_funk_end_write(funk);
2867 :
2868 0 : break;
2869 0 : }
2870 0 : }
2871 :
2872 0 : return 0;
2873 0 : }
2874 :
2875 : int
2876 : fd_runtime_block_eval_tpool( fd_exec_slot_ctx_t * slot_ctx,
2877 : fd_capture_ctx_t * capture_ctx,
2878 : void const * block,
2879 : ulong blocklen,
2880 : fd_tpool_t * tpool,
2881 : ulong scheduler,
2882 : ulong * txn_cnt,
2883 : fd_spad_t * * spads,
2884 0 : ulong spad_cnt ) {
2885 0 : (void)scheduler;
2886 :
2887 0 : int err = fd_runtime_publish_old_txns( slot_ctx, capture_ctx, tpool );
2888 0 : if( err != 0 ) {
2889 0 : return err;
2890 0 : }
2891 :
2892 0 : fd_funk_t * funk = slot_ctx->acc_mgr->funk;
2893 :
2894 0 : long block_eval_time = -fd_log_wallclock();
2895 0 : fd_block_info_t block_info;
2896 0 : int ret = fd_runtime_block_prepare(block, blocklen, slot_ctx->valloc, &block_info);
2897 0 : *txn_cnt = block_info.txn_cnt;
2898 :
2899 : /* Use the blockhash as the funk xid */
2900 0 : fd_funk_txn_xid_t xid;
2901 :
2902 0 : fd_blockstore_start_read(slot_ctx->blockstore);
2903 0 : ulong slot = slot_ctx->slot_bank.slot;
2904 0 : fd_hash_t const * hash = fd_blockstore_block_hash_query(slot_ctx->blockstore, slot);
2905 0 : if( hash == NULL ) {
2906 0 : ret = FD_RUNTIME_EXECUTE_GENERIC_ERR;
2907 0 : FD_LOG_WARNING(("missing blockhash for %lu", slot));
2908 0 : } else {
2909 0 : fd_memcpy(xid.uc, hash->uc, sizeof(fd_funk_txn_xid_t));
2910 0 : xid.ul[0] = slot_ctx->slot_bank.slot;
2911 : /* push a new transaction on the stack */
2912 0 : fd_funk_start_write( funk );
2913 0 : slot_ctx->funk_txn = fd_funk_txn_prepare(funk, slot_ctx->funk_txn, &xid, 1);
2914 0 : fd_funk_end_write( funk );
2915 0 : }
2916 0 : fd_blockstore_end_read(slot_ctx->blockstore);
2917 :
2918 0 : if( FD_RUNTIME_EXECUTE_SUCCESS == ret ) {
2919 0 : ret = fd_runtime_block_verify_tpool(&block_info, &slot_ctx->slot_bank.poh, &slot_ctx->slot_bank.poh, slot_ctx->valloc, tpool );
2920 0 : }
2921 0 : if( FD_RUNTIME_EXECUTE_SUCCESS == ret ) {
2922 0 : ret = fd_runtime_block_execute_tpool_v2( slot_ctx, capture_ctx, &block_info, tpool, spads, spad_cnt );
2923 0 : }
2924 :
2925 0 : fd_runtime_block_destroy( slot_ctx->valloc, &block_info );
2926 :
2927 : // FIXME: better way of using starting slot
2928 0 : if( FD_UNLIKELY( FD_RUNTIME_EXECUTE_SUCCESS != ret ) ) {
2929 0 : FD_LOG_WARNING(("execution failure, code %d", ret));
2930 : /* Skip over slot next time */
2931 0 : slot_ctx->slot_bank.slot = slot+1;
2932 0 : return 0;
2933 0 : }
2934 :
2935 0 : block_eval_time += fd_log_wallclock();
2936 0 : double block_eval_time_ms = (double)block_eval_time * 1e-6;
2937 0 : double tps = (double) block_info.txn_cnt / ((double)block_eval_time * 1e-9);
2938 0 : FD_LOG_INFO(( "evaluated block successfully - slot: %lu, elapsed: %6.6f ms, signatures: %lu, txns: %lu, tps: %6.6f, bank_hash: %s, leader: %s",
2939 0 : slot_ctx->slot_bank.slot,
2940 0 : block_eval_time_ms,
2941 0 : block_info.signature_cnt,
2942 0 : block_info.txn_cnt,
2943 0 : tps,
2944 0 : FD_BASE58_ENC_32_ALLOCA( slot_ctx->slot_bank.banks_hash.hash ),
2945 0 : FD_BASE58_ENC_32_ALLOCA( slot_ctx->leader->key ) ));
2946 :
2947 0 : slot_ctx->slot_bank.transaction_count += block_info.txn_cnt;
2948 :
2949 : /* progress to next slot next time */
2950 0 : slot_ctx->blockstore->smr++;
2951 :
2952 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
2953 0 : fd_runtime_save_slot_bank( slot_ctx );
2954 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
2955 :
2956 0 : slot_ctx->slot_bank.prev_slot = slot;
2957 : // FIXME: this shouldn't be doing this, it doesn't work with forking. punting changing it though
2958 0 : slot_ctx->slot_bank.slot = slot+1;
2959 :
2960 0 : return 0;
2961 0 : }
2962 :
2963 : /* rollback to the state where the given slot just FINISHED executing */
2964 : int
2965 0 : fd_runtime_rollback_to( fd_exec_slot_ctx_t * slot_ctx, ulong slot ) {
2966 0 : FD_LOG_NOTICE(( "rolling back to %lu", slot ));
2967 0 : fd_funk_t * funk = slot_ctx->acc_mgr->funk;
2968 0 : fd_funk_txn_t * txnmap = fd_funk_txn_map(funk, fd_funk_wksp(funk));
2969 0 : fd_blockstore_t * blockstore = slot_ctx->blockstore;
2970 0 : FD_LOG_WARNING(("rolling back to slot %lu", slot));
2971 : /* Get the blockhash, which is used as the funk transaction id */
2972 0 : fd_blockstore_start_read(blockstore);
2973 0 : fd_hash_t const * hash = fd_blockstore_block_hash_query(blockstore, slot);
2974 0 : if( !hash ) {
2975 0 : fd_blockstore_end_read(blockstore);
2976 0 : return -1;
2977 0 : }
2978 0 : fd_funk_txn_xid_t xid;
2979 0 : fd_memcpy(xid.uc, hash, sizeof(fd_funk_txn_xid_t));
2980 0 : xid.ul[0] = slot;
2981 0 : fd_blockstore_end_read(blockstore);
2982 : /* Switch to the funk transaction */
2983 0 : fd_funk_txn_t * txn = fd_funk_txn_query(&xid, txnmap);
2984 0 : if( !txn) return -1;
2985 0 : slot_ctx->funk_txn = txn;
2986 : /* Recover the old bank state */
2987 0 : fd_runtime_recover_banks(slot_ctx, 1, 1);
2988 0 : return 0;
2989 0 : }
2990 :
2991 : ulong
2992 6111 : fd_runtime_lamports_per_signature( fd_slot_bank_t const *slot_bank ) {
2993 : // https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/fee_calculator.rs#L110
2994 6111 : return slot_bank->fee_rate_governor.target_lamports_per_signature / 2;
2995 6111 : }
2996 :
2997 : ulong
2998 : fd_runtime_lamports_per_signature_for_blockhash( fd_exec_slot_ctx_t const * slot_ctx,
2999 0 : fd_hash_t const * blockhash ) {
3000 :
3001 : // https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/fee_calculator.rs#L110
3002 :
3003 : // https://github.com/firedancer-io/solana/blob/53a4e5d6c58b2ffe89b09304e4437f8ca198dadd/runtime/src/blockhash_queue.rs#L55
3004 0 : ulong default_fee = slot_ctx->slot_bank.fee_rate_governor.target_lamports_per_signature / 2;
3005 :
3006 0 : if( blockhash == NULL ) {
3007 0 : return default_fee;
3008 0 : }
3009 :
3010 0 : fd_block_block_hash_entry_t *hashes = slot_ctx->slot_bank.recent_block_hashes.hashes;
3011 0 : for( deq_fd_block_block_hash_entry_t_iter_t iter = deq_fd_block_block_hash_entry_t_iter_init(hashes);
3012 0 : !deq_fd_block_block_hash_entry_t_iter_done(hashes, iter);
3013 0 : iter = deq_fd_block_block_hash_entry_t_iter_next(hashes, iter) ) {
3014 0 : fd_block_block_hash_entry_t *curr_elem = deq_fd_block_block_hash_entry_t_iter_ele(hashes, iter);
3015 0 : if (memcmp(&curr_elem->blockhash, blockhash, sizeof(fd_hash_t)) == 0) {
3016 0 : return curr_elem->fee_calculator.lamports_per_signature;
3017 0 : }
3018 0 : }
3019 :
3020 0 : return default_fee;
3021 0 : }
3022 :
3023 : ulong
3024 : fd_runtime_txn_lamports_per_signature( fd_exec_txn_ctx_t * txn_ctx,
3025 : fd_txn_t const * txn_descriptor,
3026 0 : fd_rawtxn_b_t const * txn_raw ) {
3027 : // why is asan not detecting access to uninitialized memory here?!
3028 0 : fd_nonce_state_versions_t state;
3029 0 : int err;
3030 0 : if ((NULL != txn_descriptor) && fd_load_nonce_account(txn_ctx, &state, txn_ctx->valloc, &err)) {
3031 0 : if (state.inner.current.discriminant == fd_nonce_state_enum_initialized)
3032 0 : return state.inner.current.inner.initialized.fee_calculator.lamports_per_signature;
3033 0 : }
3034 :
3035 : // lamports_per_signature = (transaction has a DurableNonce, use the lamports_per_signature from that nonce instead of looking up the recent_block_hash and using the lamports_per_signature associated with that hash
3036 : // let TransactionExecutionDetails {
3037 : // status,
3038 : // log_messages,
3039 : // inner_instructions,
3040 : // durable_nonce_fee,
3041 : // ..
3042 : // } = details;
3043 : // let lamports_per_signature = match durable_nonce_fee {
3044 : // Some(DurableNonceFee::Valid(lamports_per_signature)) => {
3045 : // Some(lamports_per_signature)
3046 : // }
3047 : // Some(DurableNonceFee::Invalid) => None,
3048 : // None => bank.get_lamports_per_signature_for_blockhash(
3049 : // transaction.message().recent_blockhash(),
3050 : // ),
3051 : // }
3052 :
3053 0 : return (txn_raw == NULL) ? fd_runtime_lamports_per_signature_for_blockhash(txn_ctx->slot_ctx, NULL) : fd_runtime_lamports_per_signature_for_blockhash(txn_ctx->slot_ctx, (fd_hash_t *)((uchar *)txn_raw->raw + txn_descriptor->recent_blockhash_off));
3054 0 : }
3055 :
3056 : void compute_priority_fee(fd_exec_txn_ctx_t const *txn_ctx, ulong *fee, ulong *priority)
3057 6111 : {
3058 6111 : switch (txn_ctx->prioritization_fee_type)
3059 6111 : {
3060 6054 : case FD_COMPUTE_BUDGET_PRIORITIZATION_FEE_TYPE_DEPRECATED:
3061 6054 : {
3062 6054 : if (txn_ctx->compute_unit_limit == 0)
3063 12 : {
3064 12 : *priority = 0;
3065 12 : }
3066 6042 : else
3067 6042 : {
3068 6042 : uint128 micro_lamport_fee = (uint128)txn_ctx->compute_unit_price * (uint128)MICRO_LAMPORTS_PER_LAMPORT;
3069 6042 : uint128 _priority = micro_lamport_fee / (uint128)txn_ctx->compute_unit_limit;
3070 6042 : *priority = _priority > (uint128)ULONG_MAX ? ULONG_MAX : (ulong)_priority;
3071 6042 : }
3072 :
3073 6054 : *fee = txn_ctx->compute_unit_price;
3074 6054 : return;
3075 0 : }
3076 57 : case FD_COMPUTE_BUDGET_PRIORITIZATION_FEE_TYPE_COMPUTE_UNIT_PRICE:
3077 57 : {
3078 :
3079 57 : uint128 micro_lamport_fee = (uint128)txn_ctx->compute_unit_price * (uint128)txn_ctx->compute_unit_limit;
3080 :
3081 57 : *priority = txn_ctx->compute_unit_price;
3082 57 : uint128 _fee = (micro_lamport_fee + (uint128)(MICRO_LAMPORTS_PER_LAMPORT - 1)) / (uint128)(MICRO_LAMPORTS_PER_LAMPORT);
3083 57 : *fee = _fee > (uint128)ULONG_MAX ? ULONG_MAX : (ulong)_fee;
3084 57 : return;
3085 0 : }
3086 0 : default:
3087 0 : __builtin_unreachable();
3088 6111 : }
3089 6111 : }
3090 :
3091 : // https://github.com/anza-xyz/agave/blob/2e6ca8c1f62db62c1db7f19c9962d4db43d0d550/sdk/src/fee.rs#L82
3092 12222 : #define ACCOUNT_DATA_COST_PAGE_SIZE fd_ulong_sat_mul(32, 1024)
3093 :
3094 : void
3095 : fd_runtime_calculate_fee(fd_exec_txn_ctx_t *txn_ctx,
3096 : fd_txn_t const *txn_descriptor,
3097 : fd_rawtxn_b_t const *txn_raw,
3098 : ulong *ret_execution_fee,
3099 : ulong *ret_priority_fee)
3100 6111 : {
3101 : // https://github.com/firedancer-io/solana/blob/08a1ef5d785fe58af442b791df6c4e83fe2e7c74/runtime/src/bank.rs#L4443
3102 : // TODO: implement fee distribution to the collector ... and then charge us the correct amount
3103 6111 : ulong priority = 0;
3104 6111 : ulong priority_fee = 0;
3105 6111 : compute_priority_fee(txn_ctx, &priority_fee, &priority);
3106 :
3107 : // let signature_fee = Self::get_num_signatures_in_message(message) .saturating_mul(fee_structure.lamports_per_signature);
3108 6111 : ulong num_signatures = txn_descriptor->signature_cnt;
3109 16905 : for (ushort i = 0; i < txn_descriptor->instr_cnt; ++i)
3110 10794 : {
3111 10794 : fd_txn_instr_t const *txn_instr = &txn_descriptor->instr[i];
3112 10794 : fd_pubkey_t *program_id = &txn_ctx->accounts[txn_instr->program_id];
3113 10794 : if (memcmp(program_id->uc, fd_solana_keccak_secp_256k_program_id.key, sizeof(fd_pubkey_t)) == 0 ||
3114 10794 : memcmp(program_id->uc, fd_solana_ed25519_sig_verify_program_id.key, sizeof(fd_pubkey_t)) == 0 ||
3115 10794 : memcmp(program_id->uc, fd_solana_secp256r1_program_id.key, sizeof(fd_pubkey_t)) == 0)
3116 510 : {
3117 510 : if (txn_instr->data_sz == 0)
3118 0 : {
3119 0 : continue;
3120 0 : }
3121 510 : uchar *data = (uchar *)txn_raw->raw + txn_instr->data_off;
3122 510 : num_signatures = fd_ulong_sat_add(num_signatures, (ulong)(data[0]));
3123 510 : }
3124 10794 : }
3125 :
3126 6111 : ulong signature_fee = fd_runtime_lamports_per_signature(&txn_ctx->slot_ctx->slot_bank) * num_signatures;
3127 :
3128 : // TODO: as far as I can tell, this is always 0
3129 : //
3130 : // let write_lock_fee = Self::get_num_write_locks_in_message(message)
3131 : // .saturating_mul(fee_structure.lamports_per_write_lock);
3132 6111 : ulong lamports_per_write_lock = 0;
3133 6111 : ulong write_lock_fee = fd_ulong_sat_mul(fd_txn_account_cnt(txn_descriptor, FD_TXN_ACCT_CAT_WRITABLE), lamports_per_write_lock);
3134 :
3135 : // TODO: the fee_structure bin is static and default..
3136 : // let loaded_accounts_data_size_cost = if include_loaded_account_data_size_in_fee {
3137 : // FeeStructure::calculate_memory_usage_cost(
3138 : // budget_limits.loaded_accounts_data_size_limit,
3139 : // budget_limits.heap_cost,
3140 : // )
3141 : // } else {
3142 : // 0_u64
3143 : // };
3144 : // let total_compute_units =
3145 : // loaded_accounts_data_size_cost.saturating_add(budget_limits.compute_unit_limit);
3146 : // let compute_fee = self
3147 : // .compute_fee_bins
3148 : // .iter()
3149 : // .find(|bin| total_compute_units <= bin.limit)
3150 : // .map(|bin| bin.fee)
3151 : // .unwrap_or_else(|| {
3152 : // self.compute_fee_bins
3153 : // .last()
3154 : // .map(|bin| bin.fee)
3155 : // .unwrap_or_default()
3156 : // });
3157 :
3158 : // https://github.com/anza-xyz/agave/blob/2e6ca8c1f62db62c1db7f19c9962d4db43d0d550/sdk/src/fee.rs#L116
3159 6111 : ulong MEMORY_USAGE_COST = (((txn_ctx->loaded_accounts_data_size_limit + (ACCOUNT_DATA_COST_PAGE_SIZE - 1)) / ACCOUNT_DATA_COST_PAGE_SIZE) * FD_VM_HEAP_COST);
3160 : // https://github.com/anza-xyz/agave/blob/2e6ca8c1f62db62c1db7f19c9962d4db43d0d550/sdk/src/fee.rs#L180
3161 6111 : ulong loaded_accounts_data_size_cost = FD_FEATURE_ACTIVE(txn_ctx->slot_ctx, include_loaded_accounts_data_size_in_fee_calculation) ? MEMORY_USAGE_COST : 0;
3162 6111 : ulong total_compute_units = loaded_accounts_data_size_cost + txn_ctx->compute_unit_limit;
3163 : /* unused */
3164 6111 : (void)total_compute_units;
3165 6111 : ulong compute_fee = 0;
3166 :
3167 : // https://github.com/anza-xyz/agave/blob/2e6ca8c1f62db62c1db7f19c9962d4db43d0d550/sdk/src/fee.rs#L203-L206
3168 6111 : ulong execution_fee = (signature_fee + write_lock_fee + compute_fee);
3169 :
3170 : // FD_LOG_DEBUG(("fd_runtime_calculate_fee_compare: slot=%ld fee(%lf) = (prioritization_fee(%f) + signature_fee(%f) + write_lock_fee(%f) + compute_fee(%f)) * congestion_multiplier(%f)", txn_ctx->slot_ctx->slot_bank.slot, fee, prioritization_fee, signature_fee, write_lock_fee, compute_fee, congestion_multiplier));
3171 :
3172 6111 : if (execution_fee >= ULONG_MAX)
3173 0 : *ret_execution_fee = ULONG_MAX;
3174 6111 : else
3175 6111 : *ret_execution_fee = execution_fee;
3176 :
3177 6111 : if (priority_fee >= ULONG_MAX)
3178 3 : *ret_priority_fee = ULONG_MAX;
3179 6108 : else
3180 6108 : *ret_priority_fee = priority_fee;
3181 6111 : }
3182 :
3183 : /* sadness */
3184 :
3185 : // static double
3186 : // fd_slots_per_year( ulong ticks_per_slot,
3187 : // ulong ns_per_tick ) {
3188 : // return 365.242199 * 24.0 * 60.0 * 60.0
3189 : // * (1000000000.0 / (double)ns_per_tick)
3190 : // / (double)ticks_per_slot;
3191 : // }
3192 :
3193 2007 : #define FD_RENT_EXEMPT (-1L)
3194 :
3195 : static long
3196 : fd_runtime_get_rent_due( fd_exec_slot_ctx_t const * slot_ctx,
3197 : fd_account_meta_t * acc,
3198 1623 : ulong epoch ) {
3199 :
3200 1623 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
3201 1623 : fd_epoch_schedule_t * schedule = &epoch_bank->rent_epoch_schedule;
3202 1623 : fd_rent_t * rent = &epoch_bank->rent;
3203 1623 : double slots_per_year = epoch_bank->slots_per_year;
3204 :
3205 1623 : fd_solana_account_meta_t *info = &acc->info;
3206 :
3207 : /* Nothing due if account is rent-exempt
3208 : https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L90 */
3209 1623 : ulong min_balance = fd_rent_exempt_minimum_balance( rent, acc->dlen );
3210 1623 : if( info->lamports>=min_balance ) {
3211 1590 : return FD_RENT_EXEMPT;
3212 1590 : }
3213 :
3214 : /* Count the number of slots that have passed since last collection. This
3215 : inlines the agave function get_slots_in_peohc
3216 : https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L93-L98 */
3217 33 : ulong slots_elapsed = 0UL;
3218 33 : if( FD_UNLIKELY( info->rent_epoch<schedule->first_normal_epoch ) ) {
3219 : /* Count the slots before the first normal epoch separately */
3220 423 : for( ulong i=info->rent_epoch; i<schedule->first_normal_epoch && i<=epoch; i++ ) {
3221 393 : slots_elapsed += fd_epoch_slot_cnt( schedule, i+1UL );
3222 393 : }
3223 30 : slots_elapsed += fd_ulong_sat_sub( epoch+1UL, schedule->first_normal_epoch ) * schedule->slots_per_epoch;
3224 30 : }
3225 3 : else {
3226 3 : slots_elapsed = (epoch - info->rent_epoch + 1UL) * schedule->slots_per_epoch;
3227 3 : }
3228 : /* Consensus-critical use of doubles :( */
3229 :
3230 33 : double years_elapsed;
3231 33 : if( FD_LIKELY( slots_per_year!=0.0 ) ) {
3232 33 : years_elapsed = (double)slots_elapsed / slots_per_year;
3233 33 : } else {
3234 0 : years_elapsed = 0.0;
3235 0 : }
3236 :
3237 33 : ulong lamports_per_year = rent->lamports_per_uint8_year * (acc->dlen + 128UL);
3238 : /* https://github.com/anza-xyz/agave/blob/d2124a995f89e33c54f41da76bfd5b0bd5820898/sdk/src/rent_collector.rs#L108 */
3239 : /* https://github.com/anza-xyz/agave/blob/d2124a995f89e33c54f41da76bfd5b0bd5820898/sdk/program/src/rent.rs#L95 */
3240 33 : return (long)fd_rust_cast_double_to_ulong(years_elapsed * (double)lamports_per_year);
3241 1623 : }
3242 :
3243 : /* https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L117-149 */
3244 : /* Collect rent from an account. Returns the amount of rent collected. */
3245 : ulong
3246 : fd_runtime_collect_from_existing_account( fd_exec_slot_ctx_t const * slot_ctx,
3247 : fd_account_meta_t * acc,
3248 : fd_pubkey_t const * pubkey,
3249 4926 : ulong epoch ) {
3250 4926 : ulong collected_rent = 0UL;
3251 9012 : #define NO_RENT_COLLECTION_NOW (-1)
3252 4926 : #define EXEMPT (-2)
3253 4944 : #define COLLECT_RENT (-3)
3254 :
3255 : /* An account must be hashed regardless of if rent is collected from it. */
3256 4926 : acc->slot = slot_ctx->slot_bank.slot;
3257 :
3258 : /* Inlining calculate_rent_result
3259 : https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L153-184 */
3260 4926 : int calculate_rent_result = COLLECT_RENT;
3261 :
3262 : /* RentResult::NoRentCollectionNow */
3263 4926 : if( FD_LIKELY( acc->info.rent_epoch==FD_RENT_EXEMPT_RENT_EPOCH || acc->info.rent_epoch>epoch ) ) {
3264 4506 : calculate_rent_result = NO_RENT_COLLECTION_NOW;
3265 4506 : goto rent_calculation;
3266 4506 : }
3267 : /* RentResult::Exempt */
3268 : /* Inlining should_collect_rent() */
3269 420 : int should_collect_rent = !( acc->info.executable ||
3270 420 : !memcmp( pubkey, &fd_sysvar_incinerator_id, sizeof(fd_pubkey_t) ) );
3271 420 : if( !should_collect_rent ) {
3272 3 : calculate_rent_result = EXEMPT;
3273 3 : goto rent_calculation;
3274 3 : }
3275 :
3276 : /* https://github.com/anza-xyz/agave/blob/v2.0.10/sdk/src/rent_collector.rs#L167-180 */
3277 417 : long rent_due = fd_runtime_get_rent_due( slot_ctx, acc, epoch );
3278 417 : if( rent_due==FD_RENT_EXEMPT ) {
3279 408 : calculate_rent_result = EXEMPT;
3280 408 : } else if( rent_due==0L ) {
3281 0 : calculate_rent_result = NO_RENT_COLLECTION_NOW;
3282 9 : } else {
3283 9 : calculate_rent_result = COLLECT_RENT;
3284 9 : }
3285 :
3286 4926 : rent_calculation:
3287 4926 : switch( calculate_rent_result ) {
3288 411 : case EXEMPT:
3289 411 : acc->info.rent_epoch = FD_RENT_EXEMPT_RENT_EPOCH;
3290 411 : break;
3291 4506 : case NO_RENT_COLLECTION_NOW:
3292 4506 : break;
3293 9 : case COLLECT_RENT:
3294 9 : if( FD_UNLIKELY( (ulong)rent_due>=acc->info.lamports ) ) {
3295 : /* Reclaim account */
3296 9 : collected_rent += (ulong)acc->info.lamports;
3297 9 : acc->info.lamports = 0UL;
3298 9 : acc->dlen = 0UL;
3299 9 : fd_memset( acc->info.owner, 0, sizeof(acc->info.owner) );
3300 9 : } else {
3301 0 : collected_rent += (ulong)rent_due;
3302 0 : acc->info.lamports -= (ulong)rent_due;
3303 0 : acc->info.rent_epoch = epoch+1UL;
3304 0 : }
3305 4926 : }
3306 :
3307 4926 : return collected_rent;
3308 :
3309 4926 : #undef NO_RENT_COLLECTION_NOW
3310 4926 : #undef EXEMPT
3311 4926 : #undef COLLECT_RENT
3312 4926 : }
3313 :
3314 : /* fd_runtime_collect_rent_from_account performs rent collection duties.
3315 : Although the Solana runtime prevents the creation of new accounts
3316 : that are subject to rent, some older accounts are still undergo the
3317 : rent collection process. Updates the account's 'rent_epoch' if
3318 : needed. Returns the amount of rent collected. */
3319 : /* https://github.com/anza-xyz/agave/blob/v2.0.10/svm/src/account_loader.rs#L71-96 */
3320 : ulong
3321 : fd_runtime_collect_rent_from_account( fd_exec_slot_ctx_t const * slot_ctx,
3322 : fd_account_meta_t * acc,
3323 : fd_pubkey_t const * key,
3324 6135 : ulong epoch ) {
3325 :
3326 6135 : if( !FD_FEATURE_ACTIVE( slot_ctx, disable_rent_fees_collection ) ) {
3327 4926 : return fd_runtime_collect_from_existing_account( slot_ctx, acc, key, epoch );
3328 4926 : } else {
3329 1209 : if( FD_UNLIKELY( acc->info.rent_epoch!=FD_RENT_EXEMPT_RENT_EPOCH &&
3330 1209 : fd_runtime_get_rent_due( slot_ctx, acc, epoch )==FD_RENT_EXEMPT ) ) {
3331 1182 : acc->info.rent_epoch = ULONG_MAX;
3332 1182 : }
3333 1209 : }
3334 1209 : return 0UL;
3335 6135 : }
3336 :
3337 : static void
3338 0 : fd_runtime_collect_rent_for_slot( fd_exec_slot_ctx_t * slot_ctx, ulong off, ulong epoch ) {
3339 0 : fd_funk_txn_t * txn = slot_ctx->funk_txn;
3340 0 : fd_acc_mgr_t * acc_mgr = slot_ctx->acc_mgr;
3341 0 : fd_funk_t * funk = slot_ctx->acc_mgr->funk;
3342 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
3343 :
3344 0 : fd_funk_partvec_t * partvec = fd_funk_get_partvec( funk, wksp );
3345 :
3346 0 : fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
3347 :
3348 0 : for( fd_funk_rec_t const *rec_ro = fd_funk_part_head( partvec, (uint)off, rec_map );
3349 0 : rec_ro != NULL;
3350 0 : rec_ro = fd_funk_part_next( rec_ro, rec_map ) ) {
3351 :
3352 0 : if ( FD_UNLIKELY( !fd_funk_key_is_acc( rec_ro->pair.key ) ) ) {
3353 0 : continue;
3354 0 : }
3355 :
3356 0 : fd_pubkey_t const *key = fd_type_pun_const( rec_ro->pair.key[0].uc );
3357 0 : FD_BORROWED_ACCOUNT_DECL( rec );
3358 0 : int err = fd_acc_mgr_view( acc_mgr, txn, key, rec );
3359 :
3360 : /* Account might not exist anymore in the current world */
3361 0 : if( err==FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) {
3362 0 : continue;
3363 0 : }
3364 0 : if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
3365 0 : FD_LOG_WARNING(( "fd_runtime_collect_rent: fd_acc_mgr_view failed (%d)", err ));
3366 0 : continue;
3367 0 : }
3368 :
3369 : /* Check if latest version in this transaction */
3370 0 : if( rec_ro!=rec->const_rec ) {
3371 0 : continue;
3372 0 : }
3373 :
3374 : /* Upgrade read-only handle to writable */
3375 0 : err = fd_acc_mgr_modify(
3376 0 : acc_mgr, txn, key,
3377 0 : /* do_create */ 0,
3378 0 : /* min_data_sz */ 0UL,
3379 0 : rec);
3380 0 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
3381 0 : FD_LOG_WARNING(( "fd_runtime_collect_rent_range: fd_acc_mgr_modify failed (%d)", err ));
3382 0 : continue;
3383 0 : }
3384 :
3385 : /* Actually invoke rent collection */
3386 0 : slot_ctx->slot_bank.collected_rent += fd_runtime_collect_rent_from_account( slot_ctx, rec->meta, key, epoch );
3387 0 : }
3388 0 : }
3389 :
3390 : /* Yes, this is a real function that exists in Solana. Yes, I am ashamed I have had to replicate it. */
3391 : // https://github.com/firedancer-io/solana/blob/d8292b427adf8367d87068a3a88f6fd3ed8916a5/runtime/src/bank.rs#L5618
3392 : static ulong
3393 0 : fd_runtime_slot_count_in_two_day( ulong ticks_per_slot ) {
3394 0 : return 2UL * FD_SYSVAR_CLOCK_DEFAULT_TICKS_PER_SECOND * 86400UL /* seconds per day */ / ticks_per_slot;
3395 0 : }
3396 :
3397 : // https://github.com/firedancer-io/solana/blob/d8292b427adf8367d87068a3a88f6fd3ed8916a5/runtime/src/bank.rs#L5594
3398 : static int
3399 0 : fd_runtime_use_multi_epoch_collection( fd_exec_slot_ctx_t const * slot_ctx, ulong slot ) {
3400 0 : fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
3401 0 : fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
3402 :
3403 0 : ulong off;
3404 0 : ulong epoch = fd_slot_to_epoch( schedule, slot, &off );
3405 0 : ulong slots_per_normal_epoch = fd_epoch_slot_cnt( schedule, schedule->first_normal_epoch );
3406 :
3407 0 : ulong slot_count_in_two_day = fd_runtime_slot_count_in_two_day( epoch_bank->ticks_per_slot );
3408 :
3409 0 : int use_multi_epoch_collection = ( epoch >= schedule->first_normal_epoch )
3410 0 : && ( slots_per_normal_epoch < slot_count_in_two_day );
3411 :
3412 0 : return use_multi_epoch_collection;
3413 0 : }
3414 :
3415 : static ulong
3416 0 : fd_runtime_num_rent_partitions( fd_exec_slot_ctx_t const * slot_ctx, ulong slot ) {
3417 0 : fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
3418 0 : fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
3419 :
3420 0 : ulong off;
3421 0 : ulong epoch = fd_slot_to_epoch( schedule, slot, &off );
3422 0 : ulong slots_per_epoch = fd_epoch_slot_cnt( schedule, epoch );
3423 :
3424 0 : ulong slot_count_in_two_day = fd_runtime_slot_count_in_two_day( epoch_bank->ticks_per_slot );
3425 :
3426 0 : int use_multi_epoch_collection = fd_runtime_use_multi_epoch_collection( slot_ctx, slot );
3427 :
3428 0 : if( use_multi_epoch_collection ) {
3429 0 : ulong epochs_in_cycle = slot_count_in_two_day / slots_per_epoch;
3430 0 : return slots_per_epoch * epochs_in_cycle;
3431 0 : } else {
3432 0 : return slots_per_epoch;
3433 0 : }
3434 0 : }
3435 :
3436 : // https://github.com/anza-xyz/agave/blob/2bdcc838c18d262637524274cbb2275824eb97b8/accounts-db/src/accounts_partition.rs#L30
3437 : static ulong
3438 0 : fd_runtime_get_rent_partition( fd_exec_slot_ctx_t const * slot_ctx, ulong slot ) {
3439 0 : int use_multi_epoch_collection = fd_runtime_use_multi_epoch_collection( slot_ctx, slot );
3440 :
3441 0 : fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
3442 0 : fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
3443 :
3444 0 : ulong off;
3445 0 : ulong epoch = fd_slot_to_epoch( schedule, slot, &off );
3446 0 : ulong slot_count_per_epoch = fd_epoch_slot_cnt( schedule, epoch );
3447 0 : ulong slot_count_in_two_day = fd_runtime_slot_count_in_two_day( epoch_bank->ticks_per_slot );
3448 :
3449 0 : ulong base_epoch;
3450 0 : ulong epoch_count_in_cycle;
3451 0 : if( use_multi_epoch_collection ) {
3452 0 : base_epoch = schedule->first_normal_epoch;
3453 0 : epoch_count_in_cycle = slot_count_in_two_day / slot_count_per_epoch;
3454 0 : } else {
3455 0 : base_epoch = 0;
3456 0 : epoch_count_in_cycle = 1;
3457 0 : }
3458 :
3459 0 : ulong epoch_offset = epoch - base_epoch;
3460 0 : ulong epoch_index_in_cycle = epoch_offset % epoch_count_in_cycle;
3461 0 : return off + ( epoch_index_in_cycle * slot_count_per_epoch );
3462 0 : }
3463 :
3464 : static void
3465 0 : fd_runtime_collect_rent( fd_exec_slot_ctx_t * slot_ctx ) {
3466 : // Bank::collect_rent_eagerly (enter)
3467 :
3468 0 : fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
3469 0 : fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
3470 :
3471 : // Bank::rent_collection_partitions (enter)
3472 : // Bank::variable_cycle_partitions (enter)
3473 : // Bank::variable_cycle_partitions_between_slots (enter)
3474 :
3475 0 : ulong slot0 = slot_ctx->slot_bank.prev_slot;
3476 0 : ulong slot1 = slot_ctx->slot_bank.slot;
3477 :
3478 : /* For genesis, we collect rent for slot 0. */
3479 0 : if (slot1 == 0) {
3480 0 : ulong s = slot1;
3481 0 : ulong off;
3482 0 : ulong epoch = fd_slot_to_epoch(schedule, s, &off);
3483 :
3484 : /* FIXME: This will not necessarily support warmup_epochs */
3485 0 : ulong num_partitions = fd_runtime_num_rent_partitions( slot_ctx, s );
3486 : /* Reconstruct rent lists if the number of slots per epoch changes */
3487 0 : fd_acc_mgr_set_slots_per_epoch( slot_ctx, num_partitions );
3488 0 : fd_runtime_collect_rent_for_slot( slot_ctx, fd_runtime_get_rent_partition( slot_ctx, s ), epoch );
3489 0 : return;
3490 0 : }
3491 :
3492 0 : FD_TEST(slot0 <= slot1);
3493 :
3494 0 : for( ulong s = slot0 + 1; s <= slot1; ++s ) {
3495 0 : ulong off;
3496 0 : ulong epoch = fd_slot_to_epoch(schedule, s, &off);
3497 :
3498 : /* FIXME: This will not necessarily support warmup_epochs */
3499 0 : ulong num_partitions = fd_runtime_num_rent_partitions( slot_ctx, s );
3500 : /* Reconstruct rent lists if the number of slots per epoch changes */
3501 0 : fd_acc_mgr_set_slots_per_epoch( slot_ctx, num_partitions );
3502 0 : fd_runtime_collect_rent_for_slot( slot_ctx, fd_runtime_get_rent_partition( slot_ctx, s ), epoch );
3503 0 : }
3504 :
3505 : // FD_LOG_DEBUG(("rent collected - lamports: %lu", slot_ctx->slot_bank.collected_rent));
3506 0 : }
3507 :
3508 : void
3509 0 : fd_runtime_collect_rent_accounts_prune( ulong slot, fd_exec_slot_ctx_t * slot_ctx, fd_capture_ctx_t * capture_ctx ) {
3510 : /* TODO: Test if this works across epoch boundaries */
3511 :
3512 : /* As a note, the number of partitions are determined before execution begins.
3513 : The rent accounts for each slot are added to the pruned funk. The data in
3514 : the accounts is populated after execution is completed. */
3515 0 : fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank_const( slot_ctx->epoch_ctx );
3516 0 : fd_epoch_schedule_t const * schedule = &epoch_bank->epoch_schedule;
3517 :
3518 0 : ulong off;
3519 :
3520 0 : fd_slot_to_epoch( schedule, slot, &off );
3521 :
3522 0 : fd_funk_t * funk = slot_ctx->acc_mgr->funk;
3523 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
3524 0 : fd_funk_partvec_t * partvec = fd_funk_get_partvec( funk, wksp );
3525 0 : fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
3526 :
3527 0 : for (fd_funk_rec_t const *rec_ro = fd_funk_part_head(partvec, (uint)off, rec_map);
3528 0 : rec_ro != NULL;
3529 0 : rec_ro = fd_funk_part_next(rec_ro, rec_map)) {
3530 :
3531 0 : fd_pubkey_t const * key = fd_type_pun_const(rec_ro->pair.key[0].uc);
3532 0 : fd_funk_rec_key_t rec_key = fd_acc_funk_key( key );
3533 :
3534 0 : fd_funk_txn_xid_t prune_xid;
3535 0 : fd_memset( &prune_xid, 0x42, sizeof(fd_funk_txn_xid_t));
3536 :
3537 0 : fd_funk_txn_t * txn_map = fd_funk_txn_map( capture_ctx->pruned_funk, fd_funk_wksp( capture_ctx->pruned_funk ) );
3538 0 : fd_funk_txn_t * prune_txn = fd_funk_txn_query( &prune_xid, txn_map );
3539 :
3540 0 : fd_funk_rec_t * rec = fd_funk_rec_write_prepare( capture_ctx->pruned_funk, prune_txn, &rec_key, 0, 1, NULL, NULL );
3541 0 : FD_TEST(( !!rec ));
3542 :
3543 0 : int res = fd_funk_part_set( capture_ctx->pruned_funk, rec, (uint)off );
3544 0 : FD_TEST(( res == 0 ));
3545 0 : }
3546 0 : }
3547 :
3548 : ulong fd_runtime_calculate_rent_burn( ulong rent_collected,
3549 0 : fd_rent_t const * rent ) {
3550 0 : return ( rent_collected * rent->burn_percent ) / 100UL;
3551 0 : }
3552 :
3553 : struct fd_validator_stake_pair {
3554 : fd_pubkey_t pubkey;
3555 : ulong stake;
3556 : };
3557 : typedef struct fd_validator_stake_pair fd_validator_stake_pair_t;
3558 :
3559 : int fd_validator_stake_pair_compare_before( fd_validator_stake_pair_t const * a,
3560 0 : fd_validator_stake_pair_t const * b ) {
3561 0 : if( a->stake > b->stake ) {
3562 0 : return 1;
3563 0 : } else if (a->stake == b->stake) {
3564 0 : return memcmp(&a->pubkey, &b->pubkey, sizeof(fd_pubkey_t)) > 0;
3565 0 : }
3566 0 : else
3567 0 : { // a->stake < b->stake
3568 0 : return 0;
3569 0 : }
3570 0 : }
3571 :
3572 : #define SORT_NAME sort_validator_stake_pair
3573 0 : #define SORT_KEY_T fd_validator_stake_pair_t
3574 0 : #define SORT_BEFORE(a, b) (fd_validator_stake_pair_compare_before((fd_validator_stake_pair_t const *)&a, (fd_validator_stake_pair_t const *)&b))
3575 : #include "../../util/tmpl/fd_sort.c"
3576 : #undef SORT_NAME
3577 : #undef SORT_KEY_T
3578 : #undef SORT_BERFORE
3579 :
3580 : void fd_runtime_distribute_rent_to_validators( fd_exec_slot_ctx_t * slot_ctx,
3581 0 : ulong rent_to_be_distributed ) {
3582 :
3583 0 : FD_SCRATCH_SCOPE_BEGIN {
3584 0 : ulong total_staked = 0;
3585 :
3586 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
3587 0 : fd_vote_accounts_pair_t_mapnode_t *vote_accounts_pool = epoch_bank->stakes.vote_accounts.vote_accounts_pool;
3588 0 : fd_vote_accounts_pair_t_mapnode_t *vote_accounts_root = epoch_bank->stakes.vote_accounts.vote_accounts_root;
3589 :
3590 0 : ulong num_validator_stakes = fd_vote_accounts_pair_t_map_size( vote_accounts_pool, vote_accounts_root );
3591 0 : fd_validator_stake_pair_t * validator_stakes = fd_scratch_alloc( 8UL, sizeof(fd_validator_stake_pair_t) * num_validator_stakes );
3592 0 : ulong i = 0;
3593 :
3594 0 : for( fd_vote_accounts_pair_t_mapnode_t *n = fd_vote_accounts_pair_t_map_minimum( vote_accounts_pool, vote_accounts_root );
3595 0 : n;
3596 0 : n = fd_vote_accounts_pair_t_map_successor( vote_accounts_pool, n ), i++) {
3597 :
3598 0 : validator_stakes[i].pubkey = n->elem.value.node_pubkey;
3599 0 : validator_stakes[i].stake = n->elem.stake;
3600 :
3601 0 : total_staked += n->elem.stake;
3602 :
3603 0 : }
3604 :
3605 0 : sort_validator_stake_pair_inplace(validator_stakes, num_validator_stakes);
3606 :
3607 0 : ulong validate_fee_collector_account = FD_FEATURE_ACTIVE(slot_ctx, validate_fee_collector_account);
3608 :
3609 0 : ulong rent_distributed_in_initial_round = 0;
3610 :
3611 : // We now do distribution, reusing the validator stakes array for the rent stares
3612 0 : for( i = 0; i < num_validator_stakes; i++ ) {
3613 0 : ulong staked = validator_stakes[i].stake;
3614 0 : ulong rent_share = (ulong)(((uint128)staked * (uint128)rent_to_be_distributed) / (uint128)total_staked);
3615 :
3616 0 : validator_stakes[i].stake = rent_share;
3617 0 : rent_distributed_in_initial_round += rent_share;
3618 0 : }
3619 :
3620 0 : ulong leftover_lamports = rent_to_be_distributed - rent_distributed_in_initial_round;
3621 :
3622 0 : for( i = 0; i < num_validator_stakes; i++ ) {
3623 0 : if (leftover_lamports == 0) {
3624 0 : break;
3625 0 : }
3626 :
3627 0 : leftover_lamports--;
3628 0 : validator_stakes[i].stake++;
3629 0 : }
3630 :
3631 0 : for( i = 0; i < num_validator_stakes; i++ ) {
3632 0 : ulong rent_to_be_paid = validator_stakes[i].stake;
3633 :
3634 0 : if( rent_to_be_paid > 0 ) {
3635 0 : fd_pubkey_t pubkey = validator_stakes[i].pubkey;
3636 :
3637 0 : FD_BORROWED_ACCOUNT_DECL(rec);
3638 :
3639 0 : int err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &pubkey, rec );
3640 0 : if( FD_UNLIKELY(err) ) {
3641 0 : FD_LOG_WARNING(( "cannot view pubkey %s. fd_acc_mgr_view failed (%d)", FD_BASE58_ENC_32_ALLOCA( &pubkey ), err ));
3642 0 : leftover_lamports += rent_to_be_paid;
3643 0 : continue;
3644 0 : }
3645 :
3646 0 : if (validate_fee_collector_account) {
3647 0 : if (memcmp(rec->const_meta->info.owner, fd_solana_system_program_id.key, sizeof(rec->const_meta->info.owner)) != 0) {
3648 0 : FD_LOG_WARNING(( "cannot pay a non-system-program owned account (%s)", FD_BASE58_ENC_32_ALLOCA( &pubkey ) ));
3649 0 : leftover_lamports += rent_to_be_paid;
3650 0 : continue;
3651 0 : }
3652 0 : }
3653 :
3654 : // https://github.com/solana-labs/solana/blob/8c5b5f18be77737f0913355f17ddba81f14d5824/accounts-db/src/account_rent_state.rs#L39
3655 :
3656 0 : ulong minbal = fd_rent_exempt_minimum_balance( fd_sysvar_cache_rent( slot_ctx->sysvar_cache ), rec->const_meta->dlen );
3657 0 : if( rec->const_meta->info.lamports + rent_to_be_paid < minbal ) {
3658 0 : FD_LOG_WARNING(("cannot pay a rent paying account (%s)", FD_BASE58_ENC_32_ALLOCA( &pubkey ) ));
3659 0 : leftover_lamports += rent_to_be_paid;
3660 0 : continue;
3661 0 : }
3662 :
3663 0 : err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, &pubkey, 0, 0UL, rec );
3664 0 : if( FD_UNLIKELY(err) ) {
3665 0 : FD_LOG_WARNING(( "cannot modify pubkey %s. fd_acc_mgr_modify failed (%d)", FD_BASE58_ENC_32_ALLOCA( &pubkey ), err ));
3666 0 : leftover_lamports += rent_to_be_paid;
3667 0 : continue;
3668 0 : }
3669 0 : rec->meta->info.lamports += rent_to_be_paid;
3670 0 : }
3671 0 : } // end of iteration over validator_stakes
3672 :
3673 0 : ulong old = slot_ctx->slot_bank.capitalization;
3674 0 : slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub(slot_ctx->slot_bank.capitalization, leftover_lamports);
3675 0 : FD_LOG_DEBUG(( "fd_runtime_distribute_rent_to_validators: burn %lu, capitalization %lu->%lu ", leftover_lamports, old, slot_ctx->slot_bank.capitalization ));
3676 :
3677 0 : } FD_SCRATCH_SCOPE_END;
3678 0 : }
3679 :
3680 : void
3681 0 : fd_runtime_distribute_rent( fd_exec_slot_ctx_t * slot_ctx ) {
3682 0 : ulong total_rent_collected = slot_ctx->slot_bank.collected_rent;
3683 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
3684 0 : ulong burned_portion = fd_runtime_calculate_rent_burn( total_rent_collected, &epoch_bank->rent );
3685 0 : slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub( slot_ctx->slot_bank.capitalization, burned_portion );
3686 0 : ulong rent_to_be_distributed = total_rent_collected - burned_portion;
3687 :
3688 0 : FD_LOG_DEBUG(( "rent distribution - slot: %lu, burned_lamports: %lu, distributed_lamports: %lu, total_rent_collected: %lu", slot_ctx->slot_bank.slot, burned_portion, rent_to_be_distributed, total_rent_collected ));
3689 0 : if( rent_to_be_distributed == 0 ) {
3690 0 : return;
3691 0 : }
3692 :
3693 0 : fd_runtime_distribute_rent_to_validators( slot_ctx, rent_to_be_distributed );
3694 0 : }
3695 :
3696 : int
3697 0 : fd_runtime_run_incinerator( fd_exec_slot_ctx_t * slot_ctx ) {
3698 0 : FD_BORROWED_ACCOUNT_DECL(rec);
3699 :
3700 0 : int err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, &fd_sysvar_incinerator_id, 0, 0UL, rec );
3701 0 : if( FD_UNLIKELY(err != FD_ACC_MGR_SUCCESS) ) {
3702 : // TODO: not really an error! This is fine!
3703 0 : return -1;
3704 0 : }
3705 :
3706 0 : slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub( slot_ctx->slot_bank.capitalization, rec->const_meta->info.lamports );
3707 0 : rec->meta->info.lamports = 0;
3708 :
3709 0 : return 0;
3710 0 : }
3711 :
3712 : void
3713 0 : fd_runtime_cleanup_incinerator( fd_exec_slot_ctx_t * slot_ctx ) {
3714 0 : fd_funk_rec_key_t id = fd_acc_funk_key( &fd_sysvar_incinerator_id );
3715 0 : fd_funk_t * funk = slot_ctx->acc_mgr->funk;
3716 0 : fd_funk_rec_t const * rec = fd_funk_rec_query( funk, slot_ctx->funk_txn, &id );
3717 0 : if( rec )
3718 0 : fd_funk_rec_remove( funk, fd_funk_rec_modify( funk, rec ), 1 );
3719 0 : }
3720 :
3721 : void
3722 0 : fd_runtime_freeze( fd_exec_slot_ctx_t * slot_ctx ) {
3723 :
3724 : /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/runtime/src/bank.rs#L2820-L2821 */
3725 0 : fd_runtime_collect_rent( slot_ctx );
3726 : // self.collect_fees();
3727 :
3728 0 : fd_sysvar_recent_hashes_update( slot_ctx );
3729 :
3730 0 : if( !FD_FEATURE_ACTIVE(slot_ctx, disable_fees_sysvar) )
3731 0 : fd_sysvar_fees_update(slot_ctx);
3732 :
3733 0 : ulong fees = fd_ulong_sat_add (slot_ctx->slot_bank.collected_execution_fees, slot_ctx->slot_bank.collected_priority_fees );
3734 0 : if( FD_LIKELY( fees ) ) {
3735 : // Look at collect_fees... I think this was where I saw the fee payout..
3736 0 : FD_BORROWED_ACCOUNT_DECL(rec);
3737 :
3738 0 : int err = fd_acc_mgr_modify( slot_ctx->acc_mgr, slot_ctx->funk_txn, slot_ctx->leader, 0, 0UL, rec );
3739 0 : if( FD_UNLIKELY(err != FD_ACC_MGR_SUCCESS) ) {
3740 0 : FD_LOG_WARNING(("fd_runtime_freeze: fd_acc_mgr_modify for leader (%s) failed (%d)", FD_BASE58_ENC_32_ALLOCA( slot_ctx->leader ), err));
3741 0 : return;
3742 0 : }
3743 :
3744 0 : do {
3745 0 : if ( FD_FEATURE_ACTIVE( slot_ctx, validate_fee_collector_account ) ) {
3746 0 : if (memcmp(rec->meta->info.owner, fd_solana_system_program_id.key, sizeof(rec->meta->info.owner)) != 0) {
3747 0 : FD_LOG_WARNING(("fd_runtime_freeze: burn %lu due to invalid owner", fees ));
3748 0 : slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub(slot_ctx->slot_bank.capitalization, fees);
3749 0 : break;
3750 0 : }
3751 :
3752 0 : uchar not_exempt = fd_rent_exempt_minimum_balance( fd_sysvar_cache_rent( slot_ctx->sysvar_cache ), rec->meta->dlen ) > rec->meta->info.lamports;
3753 0 : if( not_exempt ) {
3754 0 : FD_LOG_WARNING(("fd_runtime_freeze: burn %lu due to non-rent-exempt account", fees ));
3755 0 : slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub(slot_ctx->slot_bank.capitalization, fees);
3756 0 : break;
3757 0 : }
3758 0 : }
3759 :
3760 0 : ulong fees = 0;
3761 0 : ulong burn = 0;
3762 :
3763 0 : if ( FD_FEATURE_ACTIVE( slot_ctx, reward_full_priority_fee ) ) {
3764 0 : ulong half_fee = slot_ctx->slot_bank.collected_execution_fees / 2;
3765 0 : fees = fd_ulong_sat_add(slot_ctx->slot_bank.collected_priority_fees, slot_ctx->slot_bank.collected_execution_fees - half_fee);
3766 0 : burn = half_fee;
3767 0 : } else {
3768 0 : ulong total_fees = fd_ulong_sat_add(slot_ctx->slot_bank.collected_execution_fees, slot_ctx->slot_bank.collected_priority_fees);
3769 0 : ulong half_fee = total_fees / 2;
3770 0 : fees = total_fees - half_fee;
3771 0 : burn = half_fee;
3772 0 : }
3773 :
3774 0 : rec->meta->info.lamports += fees;
3775 0 : rec->meta->slot = slot_ctx->slot_bank.slot;
3776 :
3777 0 : fd_blockstore_start_write( slot_ctx->blockstore );
3778 0 : fd_block_t * blk = slot_ctx->block;
3779 0 : blk->rewards.collected_fees = fees;
3780 0 : blk->rewards.post_balance = rec->meta->info.lamports;
3781 0 : memcpy( blk->rewards.leader.uc, slot_ctx->leader->uc, sizeof(fd_hash_t) );
3782 0 : fd_blockstore_end_write( slot_ctx->blockstore );
3783 :
3784 0 : ulong old = slot_ctx->slot_bank.capitalization;
3785 0 : slot_ctx->slot_bank.capitalization = fd_ulong_sat_sub( slot_ctx->slot_bank.capitalization, burn);
3786 0 : FD_LOG_DEBUG(( "fd_runtime_freeze: burn %lu, capitalization %lu->%lu ", burn, old, slot_ctx->slot_bank.capitalization));
3787 0 : } while (false);
3788 :
3789 0 : slot_ctx->slot_bank.collected_execution_fees = 0;
3790 0 : slot_ctx->slot_bank.collected_priority_fees = 0;
3791 0 : }
3792 :
3793 : // self.distribute_rent();
3794 : // self.update_slot_history();
3795 : // self.run_incinerator();
3796 :
3797 0 : fd_runtime_distribute_rent( slot_ctx );
3798 0 : fd_runtime_run_incinerator( slot_ctx );
3799 :
3800 0 : FD_LOG_DEBUG(( "fd_runtime_freeze: capitalization %lu ", slot_ctx->slot_bank.capitalization));
3801 0 : slot_ctx->slot_bank.collected_rent = 0;
3802 0 : }
3803 :
3804 : static void
3805 : fd_feature_activate( fd_exec_slot_ctx_t * slot_ctx,
3806 : fd_feature_id_t const * id,
3807 0 : uchar const acct[ static 32 ] ) {
3808 :
3809 : // Skip reverted features from being activated
3810 0 : if ( id->reverted==1 ) {
3811 0 : return;
3812 0 : }
3813 :
3814 0 : FD_BORROWED_ACCOUNT_DECL(acct_rec);
3815 0 : int err = fd_acc_mgr_view(slot_ctx->acc_mgr, slot_ctx->funk_txn, (fd_pubkey_t *)acct, acct_rec);
3816 0 : if (FD_UNLIKELY(err != FD_ACC_MGR_SUCCESS))
3817 0 : return;
3818 :
3819 0 : fd_feature_t feature[1];
3820 :
3821 0 : FD_SCRATCH_SCOPE_BEGIN
3822 0 : {
3823 :
3824 0 : fd_bincode_decode_ctx_t ctx = {
3825 0 : .data = acct_rec->const_data,
3826 0 : .dataend = acct_rec->const_data + acct_rec->const_meta->dlen,
3827 0 : .valloc = fd_scratch_virtual(),
3828 0 : };
3829 0 : int decode_err = fd_feature_decode(feature, &ctx);
3830 0 : if (FD_UNLIKELY(decode_err != FD_BINCODE_SUCCESS)) {
3831 0 : FD_LOG_ERR(( "Failed to decode feature account %s (%d)", FD_BASE58_ENC_32_ALLOCA( acct ), decode_err ));
3832 0 : }
3833 :
3834 0 : if( feature->has_activated_at ) {
3835 0 : FD_LOG_INFO(( "feature already activated - acc: %s, slot: %lu", FD_BASE58_ENC_32_ALLOCA( acct ), feature->activated_at ));
3836 0 : fd_features_set(&slot_ctx->epoch_ctx->features, id, feature->activated_at);
3837 0 : } else {
3838 0 : FD_LOG_INFO(( "Feature %s not activated at %lu, activating", FD_BASE58_ENC_32_ALLOCA( acct ), feature->activated_at ));
3839 :
3840 0 : FD_BORROWED_ACCOUNT_DECL(modify_acct_rec);
3841 0 : err = fd_acc_mgr_modify(slot_ctx->acc_mgr, slot_ctx->funk_txn, (fd_pubkey_t *)acct, 0, 0UL, modify_acct_rec);
3842 0 : if (FD_UNLIKELY(err != FD_ACC_MGR_SUCCESS)) {
3843 0 : return;
3844 0 : }
3845 :
3846 0 : feature->has_activated_at = 1;
3847 0 : feature->activated_at = slot_ctx->slot_bank.slot;
3848 0 : fd_bincode_encode_ctx_t encode_ctx = {
3849 0 : .data = modify_acct_rec->data,
3850 0 : .dataend = modify_acct_rec->data + modify_acct_rec->meta->dlen,
3851 0 : };
3852 0 : int encode_err = fd_feature_encode(feature, &encode_ctx);
3853 0 : if (FD_UNLIKELY(encode_err != FD_BINCODE_SUCCESS)) {
3854 0 : FD_LOG_ERR(( "Failed to encode feature account %s (%d)", FD_BASE58_ENC_32_ALLOCA( acct ), decode_err ));
3855 0 : }
3856 0 : }
3857 : /* No need to call destroy, since we are using fd_scratch allocator. */
3858 0 : } FD_SCRATCH_SCOPE_END;
3859 0 : }
3860 :
3861 :
3862 : void
3863 0 : fd_features_activate( fd_exec_slot_ctx_t * slot_ctx ) {
3864 0 : for( fd_feature_id_t const * id = fd_feature_iter_init();
3865 0 : !fd_feature_iter_done( id );
3866 0 : id = fd_feature_iter_next( id ) ) {
3867 0 : fd_feature_activate( slot_ctx, id, id->id.key );
3868 0 : }
3869 0 : }
3870 :
3871 : void fd_runtime_update_leaders(fd_exec_slot_ctx_t *slot_ctx, ulong slot)
3872 0 : {
3873 0 : FD_SCRATCH_SCOPE_BEGIN
3874 0 : {
3875 0 : fd_epoch_schedule_t schedule = slot_ctx->epoch_ctx->epoch_bank.epoch_schedule;
3876 :
3877 0 : FD_LOG_INFO(("schedule->slots_per_epoch = %lu", schedule.slots_per_epoch));
3878 0 : FD_LOG_INFO(("schedule->leader_schedule_slot_offset = %lu", schedule.leader_schedule_slot_offset));
3879 0 : FD_LOG_INFO(("schedule->warmup = %d", schedule.warmup));
3880 0 : FD_LOG_INFO(("schedule->first_normal_epoch = %lu", schedule.first_normal_epoch));
3881 0 : FD_LOG_INFO(("schedule->first_normal_slot = %lu", schedule.first_normal_slot));
3882 :
3883 0 : fd_vote_accounts_t const * epoch_vaccs = &slot_ctx->slot_bank.epoch_stakes;
3884 :
3885 0 : ulong epoch = fd_slot_to_epoch(&schedule, slot, NULL);
3886 0 : ulong slot0 = fd_epoch_slot0(&schedule, epoch);
3887 0 : ulong slot_cnt = fd_epoch_slot_cnt(&schedule, epoch);
3888 :
3889 0 : FD_LOG_INFO(("starting rent list init"));
3890 :
3891 0 : fd_acc_mgr_set_slots_per_epoch(slot_ctx, fd_epoch_slot_cnt(&schedule, epoch));
3892 0 : FD_LOG_INFO(("rent list init done"));
3893 :
3894 0 : ulong vote_acc_cnt = fd_vote_accounts_pair_t_map_size(epoch_vaccs->vote_accounts_pool, epoch_vaccs->vote_accounts_root);
3895 0 : fd_stake_weight_t *epoch_weights = fd_scratch_alloc(alignof(fd_stake_weight_t), vote_acc_cnt * sizeof(fd_stake_weight_t));
3896 0 : if (FD_UNLIKELY(!epoch_weights))
3897 0 : FD_LOG_ERR(("fd_scratch_alloc() failed"));
3898 :
3899 0 : ulong stake_weight_cnt = fd_stake_weights_by_node(epoch_vaccs, epoch_weights);
3900 :
3901 0 : if (FD_UNLIKELY(stake_weight_cnt == ULONG_MAX))
3902 0 : FD_LOG_ERR(("fd_stake_weights_by_node() failed"));
3903 :
3904 : /* Derive leader schedule */
3905 :
3906 0 : FD_LOG_INFO(("stake_weight_cnt=%lu slot_cnt=%lu", stake_weight_cnt, slot_cnt));
3907 0 : ulong epoch_leaders_footprint = fd_epoch_leaders_footprint(stake_weight_cnt, slot_cnt);
3908 0 : FD_LOG_INFO(("epoch_leaders_footprint=%lu", epoch_leaders_footprint));
3909 0 : if (FD_LIKELY(epoch_leaders_footprint))
3910 0 : {
3911 0 : FD_TEST( stake_weight_cnt <= MAX_PUB_CNT );
3912 0 : FD_TEST( slot_cnt <= MAX_SLOTS_CNT );
3913 0 : void *epoch_leaders_mem = fd_exec_epoch_ctx_leaders( slot_ctx->epoch_ctx );
3914 0 : fd_epoch_leaders_t * leaders = fd_epoch_leaders_join(fd_epoch_leaders_new(epoch_leaders_mem, epoch, slot0, slot_cnt, stake_weight_cnt, epoch_weights, 0UL));
3915 0 : FD_TEST(leaders);
3916 0 : }
3917 0 : }
3918 0 : FD_SCRATCH_SCOPE_END;
3919 0 : }
3920 :
3921 : /* Update the epoch bank stakes cache with the delegated stake values from the slot bank cache.
3922 : The slot bank cache will have been accumulating this epoch, and now we are at an epoch boundary
3923 : we can safely update the epoch stakes cache with the latest values.
3924 :
3925 : In Solana, the stakes cache is updated after every transaction
3926 : (https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/bank.rs#L7587).
3927 : As delegations have to warm up, the contents of the cache will not change inter-epoch. We can therefore update
3928 : the cache only at epoch boundaries.
3929 :
3930 : https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/stakes.rs#L65 */
3931 0 : void fd_update_stake_delegations(fd_exec_slot_ctx_t * slot_ctx ) {
3932 0 : FD_SCRATCH_SCOPE_BEGIN {
3933 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
3934 0 : fd_stakes_t * stakes = &epoch_bank->stakes;
3935 :
3936 : // TODO: is this size correct if the same stake account is in both the slot and epoch cache? Is this possible?
3937 0 : ulong stake_delegations_size = fd_delegation_pair_t_map_size(
3938 0 : stakes->stake_delegations_pool, stakes->stake_delegations_root );
3939 0 : stake_delegations_size += fd_stake_accounts_pair_t_map_size(
3940 0 : slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, slot_ctx->slot_bank.stake_account_keys.stake_accounts_root );
3941 :
3942 : // Create a new epoch stake delegations cache, which will hold the union of the slot and epoch caches.
3943 0 : fd_delegation_pair_t_mapnode_t * new_stake_root = NULL;
3944 0 : fd_delegation_pair_t_mapnode_t * new_stake_pool = fd_delegation_pair_t_map_alloc( fd_scratch_virtual(), stake_delegations_size );
3945 :
3946 : // Add the stake delegations from the epoch bank to the new epoch stake delegations cache.
3947 0 : for( fd_delegation_pair_t_mapnode_t const * n = fd_delegation_pair_t_map_minimum_const( stakes->stake_delegations_pool, stakes->stake_delegations_root );
3948 0 : n;
3949 0 : n = fd_delegation_pair_t_map_successor_const( stakes->stake_delegations_pool, n ) ) {
3950 0 : fd_pubkey_t const * stake_acc = &n->elem.account;
3951 0 : FD_BORROWED_ACCOUNT_DECL(stake_acc_rec);
3952 0 : if (fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec ) != FD_ACC_MGR_SUCCESS ) {
3953 0 : continue;
3954 0 : }
3955 :
3956 0 : fd_stake_state_v2_t stake_state;
3957 0 : if (fd_stake_get_state( stake_acc_rec, &slot_ctx->valloc, &stake_state) != 0) {
3958 0 : continue;
3959 0 : }
3960 0 : fd_delegation_pair_t_mapnode_t * entry = fd_delegation_pair_t_map_acquire( new_stake_pool );
3961 0 : fd_memcpy(&entry->elem.account, stake_acc, sizeof(fd_pubkey_t));
3962 0 : fd_memcpy(&entry->elem.delegation, &stake_state.inner.stake.stake.delegation, sizeof(fd_delegation_t));
3963 0 : fd_delegation_pair_t_map_insert( new_stake_pool, &new_stake_root, entry );
3964 0 : }
3965 :
3966 : // Add the stake delegations from the slot bank to the new epoch stake delegations cache.
3967 0 : for( fd_stake_accounts_pair_t_mapnode_t const * n = fd_stake_accounts_pair_t_map_minimum_const(
3968 0 : slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, slot_ctx->slot_bank.stake_account_keys.stake_accounts_root );
3969 0 : n;
3970 0 : n = fd_stake_accounts_pair_t_map_successor_const( slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool, n ) ) {
3971 0 : fd_pubkey_t const * stake_acc = &n->elem.key;
3972 0 : FD_BORROWED_ACCOUNT_DECL(stake_acc_rec);
3973 0 : if (fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, stake_acc, stake_acc_rec ) != FD_ACC_MGR_SUCCESS ) {
3974 0 : continue;
3975 0 : }
3976 :
3977 0 : fd_stake_state_v2_t stake_state;
3978 0 : if (fd_stake_get_state( stake_acc_rec, &slot_ctx->valloc, &stake_state) != 0) {
3979 0 : continue;
3980 0 : }
3981 0 : fd_delegation_pair_t_mapnode_t * entry = fd_delegation_pair_t_map_acquire( new_stake_pool );
3982 0 : fd_memcpy(&entry->elem.account, stake_acc, sizeof(fd_pubkey_t));
3983 0 : fd_memcpy(&entry->elem.delegation, &stake_state.inner.stake.stake.delegation, sizeof(fd_delegation_t));
3984 0 : fd_delegation_pair_t_map_insert( new_stake_pool, &new_stake_root, entry );
3985 0 : }
3986 :
3987 : // Update the epoch bank vote_accounts with the latest values from the slot bank
3988 : // FIXME: resize the vote_accounts_pool if necessary
3989 0 : for ( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum(
3990 0 : slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool,
3991 0 : slot_ctx->slot_bank.vote_account_keys.vote_accounts_root );
3992 0 : n;
3993 0 : n = fd_vote_accounts_pair_t_map_successor( slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, n ) ) {
3994 :
3995 : // If the vote account is not in the epoch stakes cache, insert it
3996 0 : fd_vote_accounts_pair_t_mapnode_t key;
3997 0 : fd_memcpy( &key.elem.key, &n->elem.key, FD_PUBKEY_FOOTPRINT );
3998 0 : fd_vote_accounts_pair_t_mapnode_t * epoch_cache_node = fd_vote_accounts_pair_t_map_find( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root, &key );
3999 0 : if( epoch_cache_node == NULL ) {
4000 0 : fd_vote_accounts_pair_t_mapnode_t * new_entry = fd_vote_accounts_pair_t_map_acquire( stakes->vote_accounts.vote_accounts_pool );
4001 :
4002 0 : fd_memcpy(&new_entry->elem.key, &n->elem.key, sizeof(fd_pubkey_t));
4003 0 : fd_memcpy(&new_entry->elem.stake, &n->elem.stake, sizeof(ulong));
4004 0 : fd_memcpy(&new_entry->elem.value, &n->elem.value, sizeof(fd_solana_account_t));
4005 :
4006 0 : fd_vote_accounts_pair_t_map_insert( stakes->vote_accounts.vote_accounts_pool, &stakes->vote_accounts.vote_accounts_root, new_entry );
4007 0 : } else {
4008 0 : epoch_cache_node->elem.stake = n->elem.stake;
4009 0 : }
4010 0 : }
4011 :
4012 0 : fd_bincode_destroy_ctx_t destroy_slot = {.valloc = slot_ctx->valloc};
4013 0 : fd_vote_accounts_destroy( &slot_ctx->slot_bank.vote_account_keys, &destroy_slot );
4014 0 : fd_stake_accounts_destroy(&slot_ctx->slot_bank.stake_account_keys, &destroy_slot );
4015 :
4016 : /* Release all nodes in tree.
4017 : FIXME sweep pool and ignore tree nodes might is probably faster
4018 : than recursive descent */
4019 0 : fd_delegation_pair_t_map_release_tree( stakes->stake_delegations_pool, stakes->stake_delegations_root );
4020 0 : stakes->stake_delegations_root = NULL;
4021 :
4022 0 : for( fd_delegation_pair_t_mapnode_t * n = fd_delegation_pair_t_map_minimum( new_stake_pool, new_stake_root ); n; n = fd_delegation_pair_t_map_successor( new_stake_pool, n ) ) {
4023 0 : fd_delegation_pair_t_mapnode_t * e = fd_delegation_pair_t_map_acquire( stakes->stake_delegations_pool );
4024 0 : if( FD_UNLIKELY( !e ) ) {
4025 0 : FD_LOG_CRIT(( "Stake delegation map overflowed! (capacity=%lu)", fd_delegation_pair_t_map_max( stakes->stake_delegations_pool ) ));
4026 0 : }
4027 0 : fd_memcpy( &e->elem.account, &n->elem.account, sizeof(fd_pubkey_t));
4028 0 : fd_memcpy( &e->elem.delegation, &n->elem.delegation, sizeof(fd_delegation_t));
4029 0 : fd_delegation_pair_t_map_insert( stakes->stake_delegations_pool, &stakes->stake_delegations_root, e );
4030 0 : }
4031 :
4032 0 : slot_ctx->slot_bank.stake_account_keys.stake_accounts_root = NULL;
4033 0 : slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool = fd_stake_accounts_pair_t_map_alloc( slot_ctx->valloc, 100000 );
4034 :
4035 0 : slot_ctx->slot_bank.vote_account_keys.vote_accounts_root = NULL;
4036 0 : slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool = fd_vote_accounts_pair_t_map_alloc( slot_ctx->valloc, 100000 );
4037 0 : } FD_SCRATCH_SCOPE_END;
4038 0 : }
4039 :
4040 : /* Replace the stakes in T-2 (slot_ctx->slot_bank.epoch_stakes) by the stakes at T-1 (epoch_bank->next_epoch_stakes) */
4041 : static
4042 0 : void fd_update_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) {
4043 0 : FD_SCRATCH_SCOPE_BEGIN
4044 0 : {
4045 0 : fd_epoch_bank_t * epoch_bank = &slot_ctx->epoch_ctx->epoch_bank;
4046 :
4047 : /* Copy epoch_bank->next_epoch_stakes into slot_ctx->slot_bank.epoch_stakes */
4048 0 : fd_vote_accounts_pair_t_map_release_tree(
4049 0 : slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool,
4050 0 : slot_ctx->slot_bank.epoch_stakes.vote_accounts_root );
4051 0 : slot_ctx->slot_bank.epoch_stakes.vote_accounts_root = NULL;
4052 :
4053 0 : for ( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum(
4054 0 : epoch_bank->next_epoch_stakes.vote_accounts_pool,
4055 0 : epoch_bank->next_epoch_stakes.vote_accounts_root );
4056 0 : n;
4057 0 : n = fd_vote_accounts_pair_t_map_successor( epoch_bank->next_epoch_stakes.vote_accounts_pool, n ) ) {
4058 :
4059 0 : const fd_pubkey_t null_pubkey = {{ 0 }};
4060 0 : if ( memcmp( &n->elem.key, &null_pubkey, FD_PUBKEY_FOOTPRINT ) == 0 ) {
4061 0 : continue;
4062 0 : }
4063 :
4064 0 : fd_vote_accounts_pair_t_mapnode_t * elem = fd_vote_accounts_pair_t_map_acquire(
4065 0 : slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool );
4066 0 : if ( FD_UNLIKELY(
4067 0 : fd_vote_accounts_pair_t_map_free( slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool ) == 0 ) ) {
4068 0 : FD_LOG_ERR(( "slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool full" ));
4069 0 : }
4070 :
4071 0 : fd_memcpy( &elem->elem, &n->elem, sizeof(fd_vote_accounts_pair_t));
4072 0 : fd_vote_accounts_pair_t_map_insert(
4073 0 : slot_ctx->slot_bank.epoch_stakes.vote_accounts_pool,
4074 0 : &slot_ctx->slot_bank.epoch_stakes.vote_accounts_root,
4075 0 : elem );
4076 0 : }
4077 0 : }
4078 0 : FD_SCRATCH_SCOPE_END;
4079 0 : }
4080 :
4081 : /* Copy epoch_bank->stakes.vote_accounts into epoch_bank->next_epoch_stakes. */
4082 : static
4083 0 : void fd_update_next_epoch_stakes( fd_exec_slot_ctx_t * slot_ctx ) {
4084 0 : FD_SCRATCH_SCOPE_BEGIN
4085 0 : {
4086 0 : fd_epoch_bank_t * epoch_bank = &slot_ctx->epoch_ctx->epoch_bank;
4087 :
4088 : /* Copy epoch_ctx->epoch_bank->stakes.vote_accounts into epoch_bank->next_epoch_stakes */
4089 0 : fd_vote_accounts_pair_t_map_release_tree(
4090 0 : epoch_bank->next_epoch_stakes.vote_accounts_pool,
4091 0 : epoch_bank->next_epoch_stakes.vote_accounts_root );
4092 :
4093 0 : epoch_bank->next_epoch_stakes.vote_accounts_pool = fd_exec_epoch_ctx_next_epoch_stakes_join( slot_ctx->epoch_ctx );
4094 0 : epoch_bank->next_epoch_stakes.vote_accounts_root = NULL;
4095 :
4096 0 : for ( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum(
4097 0 : epoch_bank->stakes.vote_accounts.vote_accounts_pool,
4098 0 : epoch_bank->stakes.vote_accounts.vote_accounts_root );
4099 0 : n;
4100 0 : n = fd_vote_accounts_pair_t_map_successor( epoch_bank->stakes.vote_accounts.vote_accounts_pool, n ) ) {
4101 0 : fd_vote_accounts_pair_t_mapnode_t * elem = fd_vote_accounts_pair_t_map_acquire( epoch_bank->next_epoch_stakes.vote_accounts_pool );
4102 0 : fd_memcpy( &elem->elem, &n->elem, sizeof(fd_vote_accounts_pair_t));
4103 0 : fd_vote_accounts_pair_t_map_insert( epoch_bank->next_epoch_stakes.vote_accounts_pool, &epoch_bank->next_epoch_stakes.vote_accounts_root, elem );
4104 0 : }
4105 0 : }
4106 0 : FD_SCRATCH_SCOPE_END;
4107 0 : }
4108 :
4109 : /* Starting a new epoch.
4110 : New epoch: T
4111 : Just ended epoch: T-1
4112 : Epoch before: T-2
4113 :
4114 : In this function:
4115 : - stakes in T-2 (slot_ctx->slot_bank.epoch_stakes) should be replaced by T-1 (epoch_bank->next_epoch_stakes)
4116 : - stakes at T-1 (epoch_bank->next_epoch_stakes) should be replaced by updated stakes at T (stakes->vote_accounts)
4117 : - leader schedule should be calculated using new T-2 stakes (slot_ctx->slot_bank.epoch_stakes)
4118 :
4119 : Invariant during an epoch T:
4120 : epoch_bank->next_epoch_stakes holds the stakes at T-1
4121 : slot_ctx->slot_bank.epoch_stakes holds the stakes at T-2
4122 : */
4123 : /* process for the start of a new epoch */
4124 : void fd_process_new_epoch(
4125 : fd_exec_slot_ctx_t *slot_ctx,
4126 : ulong parent_epoch )
4127 0 : {
4128 0 : ulong slot;
4129 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
4130 0 : ulong epoch = fd_slot_to_epoch(&epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot, &slot);
4131 :
4132 : // activate feature flags
4133 0 : fd_features_activate( slot_ctx );
4134 0 : fd_features_restore( slot_ctx );
4135 :
4136 : // Change the speed of the poh clock
4137 0 : if (FD_FEATURE_ACTIVE(slot_ctx, update_hashes_per_tick6))
4138 0 : epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK6;
4139 0 : else if (FD_FEATURE_ACTIVE(slot_ctx, update_hashes_per_tick5))
4140 0 : epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK5;
4141 0 : else if (FD_FEATURE_ACTIVE(slot_ctx, update_hashes_per_tick4))
4142 0 : epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK4;
4143 0 : else if (FD_FEATURE_ACTIVE(slot_ctx, update_hashes_per_tick3))
4144 0 : epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK3;
4145 0 : else if (FD_FEATURE_ACTIVE(slot_ctx, update_hashes_per_tick2))
4146 0 : epoch_bank->hashes_per_tick = UPDATED_HASHES_PER_TICK2;
4147 :
4148 : /* Updates stake history sysvar accumulated values. */
4149 0 : fd_stakes_activate_epoch( slot_ctx );
4150 :
4151 : /* Update the stakes epoch value to the new epoch */
4152 0 : epoch_bank->stakes.epoch = epoch;
4153 :
4154 : /* If appropiate, use the stakes at T-1 to generate the leader schedule instead of T-2.
4155 : This is due to a subtlety in how Agave's stake caches interact when loading from snapshots.
4156 : See the comment in fd_exec_slot_ctx_recover_. */
4157 0 : if( slot_ctx->slot_bank.has_use_preceeding_epoch_stakes && slot_ctx->slot_bank.use_preceeding_epoch_stakes == epoch ) {
4158 0 : fd_update_epoch_stakes( slot_ctx );
4159 0 : }
4160 :
4161 : /* Distribute rewards */
4162 0 : fd_hash_t const * parent_blockhash = slot_ctx->slot_bank.block_hash_queue.last_hash;
4163 0 : if ( ( FD_FEATURE_ACTIVE( slot_ctx, enable_partitioned_epoch_reward ) ||
4164 0 : FD_FEATURE_ACTIVE( slot_ctx, partitioned_epoch_rewards_superfeature ) ) ) {
4165 0 : fd_begin_partitioned_rewards( slot_ctx, parent_blockhash, parent_epoch );
4166 0 : } else {
4167 0 : fd_update_rewards( slot_ctx, parent_blockhash, parent_epoch );
4168 0 : }
4169 :
4170 : /* Updates stakes at time T */
4171 0 : fd_stake_history_t const * history = fd_sysvar_cache_stake_history( slot_ctx->sysvar_cache );
4172 0 : if( FD_UNLIKELY( !history ) ) FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
4173 :
4174 0 : refresh_vote_accounts( slot_ctx, history );
4175 0 : fd_update_stake_delegations( slot_ctx );
4176 :
4177 : /* Replace stakes at T-2 (slot_ctx->slot_bank.epoch_stakes) by stakes at T-1 (epoch_bank->next_epoch_stakes) */
4178 0 : fd_update_epoch_stakes( slot_ctx );
4179 :
4180 : /* Replace stakes at T-1 (epoch_bank->next_epoch_stakes) by updated stakes at T (stakes->vote_accounts) */
4181 0 : fd_update_next_epoch_stakes( slot_ctx );
4182 :
4183 : /* Update current leaders using slot_ctx->slot_bank.epoch_stakes (new T-2 stakes) */
4184 0 : fd_runtime_update_leaders( slot_ctx, slot_ctx->slot_bank.slot );
4185 :
4186 0 : fd_calculate_epoch_accounts_hash_values( slot_ctx );
4187 0 : }
4188 :
4189 : /* Loads the sysvar cache. Expects acc_mgr, funk_txn, valloc to be non-NULL and valid. */
4190 0 : int fd_runtime_sysvar_cache_load( fd_exec_slot_ctx_t * slot_ctx ) {
4191 0 : if (FD_UNLIKELY(!slot_ctx->acc_mgr)) return -1;
4192 : // if (FD_UNLIKELY(!slot_ctx->funk_txn)) return -1;
4193 :
4194 0 : fd_sysvar_cache_restore( slot_ctx->sysvar_cache, slot_ctx->acc_mgr, slot_ctx->funk_txn );
4195 :
4196 0 : return FD_RUNTIME_EXECUTE_SUCCESS;
4197 0 : }
4198 :
4199 : int
4200 0 : fd_runtime_process_genesis_block( fd_exec_slot_ctx_t * slot_ctx, fd_capture_ctx_t * capture_ctx ) {
4201 0 : ulong hashcnt_per_slot = slot_ctx->epoch_ctx->epoch_bank.hashes_per_tick * slot_ctx->epoch_ctx->epoch_bank.ticks_per_slot;
4202 0 : while(hashcnt_per_slot--) {
4203 0 : fd_sha256_hash( slot_ctx->slot_bank.poh.uc, 32UL, slot_ctx->slot_bank.poh.uc );
4204 0 : }
4205 :
4206 0 : slot_ctx->slot_bank.collected_execution_fees = 0;
4207 0 : slot_ctx->slot_bank.collected_priority_fees = 0;
4208 0 : slot_ctx->slot_bank.collected_rent = 0;
4209 0 : slot_ctx->signature_cnt = 0;
4210 :
4211 0 : fd_sysvar_slot_history_update(slot_ctx);
4212 :
4213 0 : fd_runtime_freeze( slot_ctx );
4214 :
4215 : /* sort and update bank hash */
4216 0 : int result = fd_update_hash_bank( slot_ctx, capture_ctx, &slot_ctx->slot_bank.banks_hash, slot_ctx->signature_cnt );
4217 0 : if (result != FD_EXECUTOR_INSTR_SUCCESS) {
4218 0 : FD_LOG_ERR(("Failed to update bank hash with error=%d", result));
4219 0 : }
4220 :
4221 0 : FD_TEST( FD_RUNTIME_EXECUTE_SUCCESS == fd_runtime_save_epoch_bank( slot_ctx ) );
4222 :
4223 0 : FD_TEST( FD_RUNTIME_EXECUTE_SUCCESS == fd_runtime_save_slot_bank( slot_ctx ) );
4224 :
4225 0 : return FD_RUNTIME_EXECUTE_SUCCESS;
4226 0 : }
4227 :
4228 : void
4229 : fd_runtime_read_genesis( fd_exec_slot_ctx_t * slot_ctx,
4230 : char const * genesis_filepath,
4231 : uchar is_snapshot,
4232 : fd_capture_ctx_t * capture_ctx
4233 0 : ) {
4234 0 : if ( strlen( genesis_filepath ) == 0 ) return;
4235 :
4236 0 : struct stat sbuf;
4237 0 : if( FD_UNLIKELY( stat( genesis_filepath, &sbuf) < 0 ) ) {
4238 0 : FD_LOG_ERR(("cannot open %s : %s", genesis_filepath, strerror(errno)));
4239 0 : }
4240 0 : int fd = open( genesis_filepath, O_RDONLY );
4241 0 : if( FD_UNLIKELY( fd < 0 ) ) {
4242 0 : FD_LOG_ERR(("cannot open %s : %s", genesis_filepath, strerror(errno)));
4243 0 : }
4244 :
4245 0 : fd_genesis_solana_t genesis_block;
4246 0 : fd_genesis_solana_new(&genesis_block);
4247 0 : fd_hash_t genesis_hash;
4248 :
4249 0 : fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
4250 :
4251 0 : FD_SCRATCH_SCOPE_BEGIN {
4252 0 : uchar * buf = fd_scratch_alloc(1UL, (ulong) sbuf.st_size); /* TODO Make this a scratch alloc */
4253 0 : ssize_t n = read(fd, buf, (ulong) sbuf.st_size);
4254 0 : close(fd);
4255 :
4256 :
4257 0 : fd_bincode_decode_ctx_t decode_ctx = {
4258 0 : .data = buf,
4259 0 : .dataend = buf + n,
4260 0 : .valloc = slot_ctx->valloc,
4261 0 : };
4262 0 : if( fd_genesis_solana_decode(&genesis_block, &decode_ctx) )
4263 0 : FD_LOG_ERR(("fd_genesis_solana_decode failed"));
4264 :
4265 : // The hash is generated from the raw data... don't mess with this..
4266 0 : fd_sha256_hash( buf, (ulong)n, genesis_hash.uc );
4267 :
4268 0 : } FD_SCRATCH_SCOPE_END;
4269 :
4270 0 : fd_memcpy( epoch_bank->genesis_hash.uc, genesis_hash.uc, 32U );
4271 0 : epoch_bank->cluster_type = genesis_block.cluster_type;
4272 :
4273 0 : fd_funk_start_write( slot_ctx->acc_mgr->funk );
4274 :
4275 0 : if ( !is_snapshot ) {
4276 0 : fd_runtime_init_bank_from_genesis( slot_ctx, &genesis_block, &genesis_hash );
4277 :
4278 0 : fd_runtime_init_program( slot_ctx );
4279 :
4280 0 : FD_LOG_DEBUG(( "start genesis accounts - count: %lu", genesis_block.accounts_len));
4281 :
4282 0 : for( ulong i=0; i < genesis_block.accounts_len; i++ ) {
4283 0 : fd_pubkey_account_pair_t * a = &genesis_block.accounts[i];
4284 :
4285 0 : FD_BORROWED_ACCOUNT_DECL(rec);
4286 :
4287 0 : int err = fd_acc_mgr_modify(
4288 0 : slot_ctx->acc_mgr,
4289 0 : slot_ctx->funk_txn,
4290 0 : &a->key,
4291 0 : /* do_create */ 1,
4292 0 : a->account.data_len,
4293 0 : rec);
4294 0 : if( FD_UNLIKELY( err ) )
4295 0 : FD_LOG_ERR(( "fd_acc_mgr_modify failed (%d)", err ));
4296 :
4297 0 : rec->meta->dlen = a->account.data_len;
4298 0 : rec->meta->info.lamports = a->account.lamports;
4299 0 : rec->meta->info.rent_epoch = a->account.rent_epoch;
4300 0 : rec->meta->info.executable = a->account.executable;
4301 0 : memcpy( rec->meta->info.owner, a->account.owner.key, 32UL );
4302 0 : if( a->account.data_len )
4303 0 : memcpy( rec->data, a->account.data, a->account.data_len );
4304 0 : }
4305 :
4306 0 : FD_LOG_DEBUG(( "end genesis accounts"));
4307 :
4308 0 : FD_LOG_DEBUG(( "native instruction processors - count: %lu", genesis_block.native_instruction_processors_len));
4309 :
4310 0 : for( ulong i=0; i < genesis_block.native_instruction_processors_len; i++ ) {
4311 0 : fd_string_pubkey_pair_t * a = &genesis_block.native_instruction_processors[i];
4312 0 : fd_write_builtin_bogus_account( slot_ctx, a->pubkey.uc, (const char *) a->string, a->string_len );
4313 0 : }
4314 :
4315 0 : fd_features_restore( slot_ctx );
4316 :
4317 0 : slot_ctx->slot_bank.slot = 0UL;
4318 :
4319 0 : int err = fd_runtime_process_genesis_block( slot_ctx, capture_ctx );
4320 0 : if( FD_UNLIKELY( err ) ) {
4321 0 : FD_LOG_ERR(( "Genesis slot 0 execute failed with error %d", err ));
4322 0 : }
4323 0 : }
4324 :
4325 0 : slot_ctx->slot_bank.stake_account_keys.stake_accounts_root = NULL;
4326 0 : slot_ctx->slot_bank.stake_account_keys.stake_accounts_pool = fd_stake_accounts_pair_t_map_alloc(slot_ctx->valloc, 100000);
4327 :
4328 0 : slot_ctx->slot_bank.vote_account_keys.vote_accounts_root = NULL;
4329 0 : slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool = fd_vote_accounts_pair_t_map_alloc(slot_ctx->valloc, 100000);
4330 :
4331 0 : fd_funk_end_write( slot_ctx->acc_mgr->funk );
4332 :
4333 0 : fd_bincode_destroy_ctx_t ctx2 = { .valloc = slot_ctx->valloc };
4334 0 : fd_genesis_solana_destroy(&genesis_block, &ctx2);
4335 :
4336 : // if( capture_ctx ) {
4337 : // fd_solcap_writer_fini( capture_ctx->capture );
4338 : // }
4339 0 : }
|