LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_hashes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 903 0.0 %
Date: 2025-07-14 05:02:59 Functions: 0 23 0.0 %

          Line data    Source code
       1             : #include "fd_hashes.h"
       2             : #include "fd_acc_mgr.h"
       3             : #include "fd_bank.h"
       4             : #include "fd_blockstore.h"
       5             : #include "fd_runtime.h"
       6             : #include "fd_borrowed_account.h"
       7             : #include "context/fd_capture_ctx.h"
       8             : #include "fd_runtime_public.h"
       9             : #include "sysvar/fd_sysvar_epoch_schedule.h"
      10             : #include "../capture/fd_solcap_writer.h"
      11             : #include "../../ballet/base58/fd_base58.h"
      12             : #include "../../ballet/blake3/fd_blake3.h"
      13             : #include "../../ballet/lthash/fd_lthash.h"
      14             : #include "../../ballet/sha256/fd_sha256.h"
      15             : 
      16             : #include <assert.h>
      17             : #include <stdio.h>
      18             : 
      19             : #define SORT_NAME sort_pubkey_hash_pair
      20           0 : #define SORT_KEY_T fd_pubkey_hash_pair_t
      21             : static int
      22           0 : fd_pubkey_hash_pair_compare(fd_pubkey_hash_pair_t const * a, fd_pubkey_hash_pair_t const * b) {
      23           0 :   for (uint i = 0; i < sizeof(fd_pubkey_t)/sizeof(ulong); ++i) {
      24             :     /* First byte is least significant when seen as a long. Make it most significant. */
      25           0 :     ulong al = __builtin_bswap64(a->rec->pair.key->ul[i]);
      26           0 :     ulong bl = __builtin_bswap64(b->rec->pair.key->ul[i]);
      27           0 :     if (al != bl) return (al < bl);
      28           0 :   }
      29           0 :   return 0;
      30           0 : }
      31           0 : #define SORT_BEFORE(a,b) fd_pubkey_hash_pair_compare(&a, &b)
      32             : #include "../../util/tmpl/fd_sort.c"
      33             : 
      34           0 : #define FD_ACCOUNT_DELTAS_MERKLE_FANOUT (16UL)
      35           0 : #define FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT (16UL)
      36             : 
      37             : static void
      38           0 : fd_hash_account_deltas( fd_pubkey_hash_pair_list_t * lists, ulong lists_len, fd_hash_t * hash ) {
      39           0 :   fd_sha256_t shas[FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT];
      40           0 :   uchar       num_hashes[FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT+1];
      41             : 
      42             :   // Init the number of hashes
      43           0 :   fd_memset( num_hashes, 0, sizeof(num_hashes) );
      44             : 
      45           0 :   for( ulong j = 0; j < FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT; ++j ) {
      46           0 :     fd_sha256_init( &shas[j] );
      47           0 : }
      48             : 
      49           0 :   if( lists_len == 0 ) {
      50           0 :     fd_sha256_fini( &shas[0], hash->hash );
      51           0 :     return;
      52           0 :   }
      53             : 
      54           0 :   fd_pubkey_hash_pair_t * prev_pair = NULL;
      55           0 :   for( ulong k = 0; k < lists_len; ++k ) {
      56           0 :     fd_pubkey_hash_pair_t * pairs     = lists[k].pairs;
      57           0 :     ulong                   pairs_len = lists[k].pairs_len;
      58           0 :     for( ulong i = 0; i < pairs_len; ++i ) {
      59           0 :       if( prev_pair ) FD_TEST(fd_pubkey_hash_pair_compare(prev_pair, &pairs[i]) > 0);
      60           0 :       prev_pair = &pairs[i];
      61           0 :       fd_sha256_append( &shas[0], pairs[i].hash->hash, sizeof( fd_hash_t ) );
      62           0 :       num_hashes[0]++;
      63             : 
      64           0 :       for( ulong j = 0; j < FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT; ++j ) {
      65           0 :         if (num_hashes[j] == FD_ACCOUNT_DELTAS_MERKLE_FANOUT) {
      66           0 :           num_hashes[j] = 0;
      67           0 :           num_hashes[j+1]++;
      68           0 :           fd_sha256_fini( &shas[j], hash->hash );
      69           0 :           fd_sha256_init( &shas[j] );
      70           0 :           fd_sha256_append( &shas[j+1], (uchar const *) hash->hash, sizeof( fd_hash_t ) );
      71           0 :         } else {
      72           0 :           break;
      73           0 :         }
      74           0 :       }
      75           0 :     }
      76           0 :   }
      77             : 
      78           0 :   ulong tot_num_hashes = 0;
      79           0 :   for( ulong k = 0; k < FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT; ++k ) {
      80           0 :     tot_num_hashes += num_hashes[k];
      81           0 :   }
      82             : 
      83           0 :   if( tot_num_hashes == 1 ) {
      84           0 :     return;
      85           0 :   }
      86             : 
      87             :   // TODO: use CZT on pairs_len
      88           0 :   ulong height = 0;
      89           0 :   for( long i = FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT-1; i >= 0; --i ) {
      90           0 :     if( num_hashes[i] != 0 ) {
      91           0 :       height = (ulong) i + 1;
      92           0 :       break;
      93           0 :     }
      94           0 :   }
      95             : 
      96             : 
      97           0 :   for( ulong i = 0; i < height; ++i ) {
      98           0 :     if( num_hashes[i]==0 ) {
      99           0 :       continue;
     100           0 :     }
     101             :     // At level i, finalize and append to i + 1
     102             :     //fd_hash_t sub_hash;
     103           0 :     fd_sha256_fini( &shas[i], hash );
     104           0 :     num_hashes[i] = 0;
     105           0 :     num_hashes[i+1]++;
     106             : 
     107           0 :     ulong tot_num_hashes = 0;
     108           0 :     for (ulong k = 0; k < FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT; ++k ) {
     109           0 :       tot_num_hashes += num_hashes[k];
     110           0 :     }
     111           0 :     if (i == (height-1)) {
     112           0 :       assert(tot_num_hashes == 1);
     113           0 :       return;
     114           0 :     }
     115           0 :     fd_sha256_append( &shas[i+1], (uchar const *) hash->hash, sizeof( fd_hash_t ) );
     116             : 
     117             :     // There is now one more hash at level i+1
     118             : 
     119             :     // check, have we filled this level and ones above it.
     120           0 :     for( ulong j = i+1; j < height; ++j ) {
     121             :       // if the level is full, finalize and push into next level.
     122           0 :       if (num_hashes[j] == FD_ACCOUNT_DELTAS_MERKLE_FANOUT) {
     123           0 :         num_hashes[j] = 0;
     124           0 :         num_hashes[j+1]++;
     125           0 :         fd_hash_t sub_hash;
     126           0 :         fd_sha256_fini( &shas[j], &sub_hash );
     127           0 :         if (j != height - 1) {
     128           0 :           fd_sha256_append( &shas[j+1], (uchar const *) sub_hash.hash, sizeof( fd_hash_t ) );
     129           0 :         } else {
     130           0 :           memcpy(hash->hash, sub_hash.hash, sizeof(fd_hash_t));
     131           0 :           return;
     132           0 :         }
     133           0 :       }
     134           0 :     }
     135           0 :   }
     136             : 
     137             :   // If the level at the `height' was rolled into, do something about it
     138           0 : }
     139             : 
     140             : 
     141             : void
     142           0 : fd_calculate_epoch_accounts_hash_values( fd_exec_slot_ctx_t * slot_ctx ) {
     143           0 :   ulong slot_idx = 0;
     144           0 :   fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
     145           0 :   ulong epoch = fd_slot_to_epoch( epoch_schedule, fd_bank_slot_get( slot_ctx->bank ), &slot_idx );
     146             : 
     147           0 :   if( FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, accounts_lt_hash) ) {
     148           0 :     fd_bank_eah_start_slot_set( slot_ctx->bank, ULONG_MAX );
     149           0 :     fd_bank_eah_stop_slot_set( slot_ctx->bank, ULONG_MAX );
     150           0 :     fd_bank_eah_interval_set( slot_ctx->bank, ULONG_MAX );
     151           0 :     return;
     152           0 :   }
     153             : 
     154           0 :   ulong slots_per_epoch = fd_epoch_slot_cnt( epoch_schedule, epoch );
     155           0 :   ulong first_slot_in_epoch           = fd_epoch_slot0   ( epoch_schedule, epoch );
     156             : 
     157           0 :   ulong calculation_offset_start = slots_per_epoch / 4;
     158           0 :   ulong calculation_offset_stop = slots_per_epoch / 4 * 3;
     159           0 :   ulong calculation_interval = fd_ulong_sat_sub(calculation_offset_stop, calculation_offset_start);
     160             : 
     161             :   // This came from the vote program.. maybe we need to put it into a header?
     162           0 :   const ulong MAX_LOCKOUT_HISTORY = 31UL;
     163           0 :   const ulong CALCULATION_INTERVAL_BUFFER = 150UL;
     164           0 :   const ulong MINIMUM_CALCULATION_INTERVAL = MAX_LOCKOUT_HISTORY + CALCULATION_INTERVAL_BUFFER;
     165             : 
     166           0 :   if( calculation_interval < MINIMUM_CALCULATION_INTERVAL ) {
     167           0 :     fd_bank_eah_start_slot_set( slot_ctx->bank, ULONG_MAX );
     168           0 :     fd_bank_eah_stop_slot_set( slot_ctx->bank, ULONG_MAX );
     169           0 :     fd_bank_eah_interval_set( slot_ctx->bank, ULONG_MAX );
     170           0 :     return;
     171           0 :   }
     172             : 
     173           0 :   fd_bank_eah_start_slot_set( slot_ctx->bank, first_slot_in_epoch + calculation_offset_start );
     174           0 :   if( fd_bank_slot_get( slot_ctx->bank ) > fd_bank_eah_start_slot_get( slot_ctx->bank ) ) {
     175           0 :     fd_bank_eah_start_slot_set( slot_ctx->bank, ULONG_MAX );
     176           0 :   }
     177             : 
     178           0 :   fd_bank_eah_stop_slot_set( slot_ctx->bank, first_slot_in_epoch + calculation_offset_stop );
     179           0 :   if( fd_bank_slot_get( slot_ctx->bank ) > fd_bank_eah_stop_slot_get( slot_ctx->bank ) ) {
     180           0 :     fd_bank_eah_stop_slot_set( slot_ctx->bank, ULONG_MAX );
     181           0 :   }
     182             : 
     183           0 :   fd_bank_eah_interval_set( slot_ctx->bank, calculation_interval );
     184             : 
     185           0 : }
     186             : 
     187             : // https://github.com/solana-labs/solana/blob/b0dcaf29e358c37a0fcb8f1285ce5fff43c8ec55/runtime/src/bank/epoch_accounts_hash_utils.rs#L13
     188             : static int
     189           0 : fd_should_include_epoch_accounts_hash( fd_exec_slot_ctx_t * slot_ctx ) {
     190           0 :   if( FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, accounts_lt_hash) ) {
     191           0 :     return 0;
     192           0 :   }
     193             : 
     194           0 :   ulong calculation_stop = fd_bank_eah_stop_slot_get( slot_ctx->bank );
     195           0 :   ulong prev_slot = fd_bank_parent_slot_get( slot_ctx->bank );
     196           0 :   return prev_slot < calculation_stop && (fd_bank_slot_get( slot_ctx->bank ) >= calculation_stop);
     197           0 : }
     198             : 
     199             : // slot_ctx should be const.
     200             : static void
     201             : fd_hash_bank( fd_exec_slot_ctx_t *    slot_ctx,
     202             :               fd_capture_ctx_t *      capture_ctx,
     203             :               fd_hash_t *             hash,
     204             :               fd_pubkey_hash_pair_t * dirty_keys,
     205           0 :               ulong                   dirty_key_cnt ) {
     206             : 
     207           0 :   fd_hash_t const * bank_hash = fd_bank_bank_hash_query( slot_ctx->bank );
     208             : 
     209           0 :   fd_bank_prev_bank_hash_set( slot_ctx->bank, *bank_hash );
     210             : 
     211           0 :   fd_bank_parent_signature_cnt_set( slot_ctx->bank, fd_bank_signature_count_get( slot_ctx->bank ) );
     212             : 
     213           0 :   fd_bank_lamports_per_signature_set( slot_ctx->bank, fd_bank_lamports_per_signature_get( slot_ctx->bank ) );
     214             : 
     215           0 :   fd_hash_t account_delta_hash;
     216             : 
     217           0 :   if( !FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, remove_accounts_delta_hash) ) {
     218           0 :     sort_pubkey_hash_pair_inplace( dirty_keys, dirty_key_cnt );
     219           0 :     fd_pubkey_hash_pair_list_t list1 = { .pairs = dirty_keys, .pairs_len = dirty_key_cnt };
     220           0 :     fd_hash_account_deltas(&list1, 1, &account_delta_hash );
     221           0 :   }
     222             : 
     223           0 :   fd_sha256_t sha;
     224           0 :   fd_sha256_init( &sha );
     225           0 :   fd_sha256_append( &sha, (uchar const *)bank_hash, sizeof( fd_hash_t ) );
     226           0 :   if( !FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, remove_accounts_delta_hash) )
     227           0 :     fd_sha256_append( &sha, (uchar const *) &account_delta_hash, sizeof( fd_hash_t  ) );
     228           0 :   fd_sha256_append( &sha, (uchar const *) fd_bank_signature_count_query( slot_ctx->bank ), sizeof( ulong ) );
     229             : 
     230           0 :   fd_sha256_append( &sha, (uchar const *) fd_bank_poh_query( slot_ctx->bank )->hash, sizeof( fd_hash_t ) );
     231             : 
     232           0 :   fd_sha256_fini( &sha, hash->hash );
     233             : 
     234             :   // https://github.com/anza-xyz/agave/blob/766cd682423b8049ddeac3c0ec6cebe0a1356e9e/runtime/src/bank.rs#L5250
     235           0 :   if( FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, accounts_lt_hash ) ) {
     236           0 :     fd_sha256_init( &sha );
     237           0 :     fd_sha256_append( &sha, (uchar const *) &hash->hash, sizeof( fd_hash_t ) );
     238           0 :     fd_slot_lthash_t const * lthash = fd_bank_lthash_query( slot_ctx->bank );
     239           0 :     fd_sha256_append( &sha, (uchar const *) lthash->lthash, sizeof( lthash->lthash ) );
     240           0 :     fd_sha256_fini( &sha, hash->hash );
     241           0 :   } else {
     242           0 :     if (fd_should_include_epoch_accounts_hash(slot_ctx)) {
     243           0 :       fd_sha256_init( &sha );
     244           0 :       fd_sha256_append( &sha, (uchar const *) &hash->hash, sizeof( fd_hash_t ) );
     245           0 :       fd_sha256_append( &sha, (uchar const *) fd_bank_epoch_account_hash_query( slot_ctx->bank ), sizeof( fd_hash_t ) );
     246           0 :       fd_sha256_fini( &sha, hash->hash );
     247           0 :     }
     248           0 :   }
     249             : 
     250           0 :   if( capture_ctx != NULL && capture_ctx->capture != NULL && fd_bank_slot_get( slot_ctx->bank )>=capture_ctx->solcap_start_slot ) {
     251           0 :     uchar *lthash = NULL;
     252             : 
     253           0 :     if( FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, accounts_lt_hash ) ) {
     254           0 :       lthash = (uchar *)fd_alloca_check( 1UL, 32UL );
     255           0 :       fd_slot_lthash_t const * lthash_val = fd_bank_lthash_query( slot_ctx->bank );
     256           0 :       fd_lthash_hash((fd_lthash_value_t *) lthash_val->lthash, lthash);
     257           0 :     }
     258             : 
     259           0 :     fd_solcap_write_bank_preimage(
     260           0 :         capture_ctx->capture,
     261           0 :         hash->hash,
     262           0 :         fd_bank_prev_bank_hash_query( slot_ctx->bank ),
     263           0 :         FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, remove_accounts_delta_hash) ? NULL : account_delta_hash.hash,
     264           0 :         lthash,
     265           0 :         fd_bank_poh_query( slot_ctx->bank )->hash,
     266           0 :         fd_bank_signature_count_get( slot_ctx->bank ) );
     267           0 :   }
     268             : 
     269           0 :   if( FD_FEATURE_ACTIVE_BANK( slot_ctx->bank, remove_accounts_delta_hash) ) {
     270           0 :     FD_LOG_NOTICE(( "\n\n[Replay]\n"
     271           0 :                     "slot:             %lu\n"
     272           0 :                     "bank hash:        %s\n"
     273           0 :                     "parent bank hash: %s\n"
     274           0 :                     "lthash:           %s\n"
     275           0 :                     "signature_count:  %lu\n"
     276           0 :                     "last_blockhash:   %s\n",
     277           0 :                     fd_bank_slot_get( slot_ctx->bank ),
     278           0 :                     FD_BASE58_ENC_32_ALLOCA( hash->hash ),
     279           0 :                     FD_BASE58_ENC_32_ALLOCA( fd_bank_prev_bank_hash_query( slot_ctx->bank ) ),
     280           0 :                     FD_LTHASH_ENC_32_ALLOCA( (fd_lthash_value_t *) fd_bank_lthash_query( slot_ctx->bank )->lthash ),
     281           0 :                     fd_bank_signature_count_get( slot_ctx->bank ),
     282           0 :                     FD_BASE58_ENC_32_ALLOCA( fd_bank_poh_query( slot_ctx->bank )->hash ) ));
     283           0 :   } else {
     284           0 :     FD_LOG_NOTICE(( "\n\n[Replay]\n"
     285           0 :                     "slot:             %lu\n"
     286           0 :                     "bank hash:        %s\n"
     287           0 :                     "parent bank hash: %s\n"
     288           0 :                     "accounts_delta:   %s\n"
     289           0 :                     "lthash:           %s\n"
     290           0 :                     "signature_count:  %lu\n"
     291           0 :                     "last_blockhash:   %s\n",
     292           0 :                     fd_bank_slot_get( slot_ctx->bank ),
     293           0 :                     FD_BASE58_ENC_32_ALLOCA( hash->hash ),
     294           0 :                     FD_BASE58_ENC_32_ALLOCA( fd_bank_prev_bank_hash_query( slot_ctx->bank ) ),
     295           0 :                     FD_BASE58_ENC_32_ALLOCA( account_delta_hash.hash ),
     296           0 :                     FD_LTHASH_ENC_32_ALLOCA( (fd_lthash_value_t *) fd_bank_lthash_query( slot_ctx->bank )->lthash ),
     297           0 :                     fd_bank_signature_count_get( slot_ctx->bank ),
     298           0 :                     FD_BASE58_ENC_32_ALLOCA( fd_bank_poh_query( slot_ctx->bank )->hash ) ));
     299           0 :   }
     300           0 : }
     301             : 
     302             : void
     303             : fd_account_hash( fd_funk_t *                    funk,
     304             :                  fd_funk_txn_t *                funk_txn,
     305             :                  fd_accounts_hash_task_info_t * task_info,
     306             :                  fd_lthash_value_t *            lt_hash,
     307             :                  ulong                          slot,
     308           0 :                  fd_features_t const *          features ) {
     309           0 :   int err = 0;
     310           0 :   fd_funk_txn_t const *     txn_out  = NULL;
     311           0 :   fd_account_meta_t const * acc_meta = fd_funk_get_acc_meta_readonly( funk,
     312           0 :                                                                       funk_txn,
     313           0 :                                                                       task_info->acc_pubkey,
     314           0 :                                                                       NULL,
     315           0 :                                                                       &err,
     316           0 :                                                                       &txn_out );
     317           0 :   if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS || !acc_meta ) ) {
     318           0 :     FD_LOG_WARNING(( "failed to view account during bank hash" ));
     319           0 :     return;
     320           0 :   }
     321             : 
     322           0 :   fd_account_meta_t const * acc_meta_parent = NULL;
     323           0 :   if( txn_out ) {
     324           0 :     fd_funk_txn_pool_t * txn_pool = fd_funk_txn_pool( funk );
     325           0 :     txn_out = fd_funk_txn_parent( txn_out, txn_pool );
     326           0 :     acc_meta_parent = fd_funk_get_acc_meta_readonly( funk, txn_out, task_info->acc_pubkey, NULL, &err, NULL );
     327           0 :   }
     328             : 
     329           0 :   if( FD_UNLIKELY( !acc_meta->info.lamports ) ) {
     330           0 :     fd_memset( task_info->acc_hash->hash, 0, FD_HASH_FOOTPRINT );
     331             : 
     332             :     /* If we erase records instantly, this causes problems with the
     333             :         iterator.  Instead, we will store away the record and erase
     334             :         it later where appropriate.  */
     335           0 :     task_info->should_erase = 1;
     336           0 :     if( FD_UNLIKELY( NULL != acc_meta_parent) ){
     337           0 :       if( FD_UNLIKELY( acc_meta->info.lamports != acc_meta_parent->info.lamports ) )
     338           0 :         task_info->hash_changed = 1;
     339           0 :     }
     340           0 :   } else {
     341           0 :     uchar *             acc_data = fd_account_meta_get_data((fd_account_meta_t *) acc_meta);
     342           0 :     fd_lthash_value_t new_lthash_value;
     343           0 :     fd_lthash_zero( &new_lthash_value );
     344           0 :     fd_hash_account_current( task_info->acc_hash->hash,
     345           0 :                              &new_lthash_value,
     346           0 :                              acc_meta,
     347           0 :                              task_info->acc_pubkey,
     348           0 :                              acc_data,
     349           0 :                              FD_HASH_BOTH_HASHES,
     350           0 :                              features );
     351             : 
     352           0 :     if( memcmp( task_info->acc_hash->hash, acc_meta->hash, sizeof(fd_hash_t) ) != 0 ) {
     353           0 :       task_info->hash_changed = 1;
     354             :       // char *prev_lthash = FD_LTHASH_ENC_32_ALLOCA( lt_hash );
     355           0 :       fd_lthash_add( lt_hash, &new_lthash_value);
     356             :       // FD_LOG_NOTICE(( "lthash %s + %s = %s (%s)", prev_lthash, FD_LTHASH_ENC_32_ALLOCA( &new_lthash_value ), FD_LTHASH_ENC_32_ALLOCA( lt_hash ),
     357             :       //    FD_BASE58_ENC_32_ALLOCA( task_info->acc_pubkey )));
     358           0 :     }
     359           0 :   }
     360           0 :   if( FD_LIKELY(task_info->hash_changed && ((NULL != acc_meta_parent) && (acc_meta_parent->info.lamports != 0) ) ) ) {
     361           0 :     uchar const * acc_data = fd_account_meta_get_data_const( acc_meta_parent );
     362           0 :     fd_lthash_value_t old_lthash_value;
     363           0 :     fd_lthash_zero(&old_lthash_value);
     364           0 :     fd_hash_t old_hash;
     365             : 
     366           0 :     fd_hash_account_current( old_hash.hash,
     367           0 :                              &old_lthash_value,
     368           0 :                              acc_meta_parent,
     369           0 :                              task_info->acc_pubkey,
     370           0 :                              acc_data,
     371           0 :                              FD_HASH_JUST_LTHASH,
     372           0 :                              features );
     373             : 
     374             :     // char *prev_lthash = FD_LTHASH_ENC_32_ALLOCA( lt_hash );
     375           0 :     fd_lthash_sub( lt_hash, &old_lthash_value );
     376             :     // FD_LOG_NOTICE(( "lthash %s - %s = %s (%s)", prev_lthash, FD_LTHASH_ENC_32_ALLOCA( &old_lthash_value ), FD_LTHASH_ENC_32_ALLOCA( lt_hash ),
     377             :     //    FD_BASE58_ENC_32_ALLOCA( task_info->acc_pubkey )));
     378           0 :   }
     379             : 
     380           0 :   if( acc_meta->slot == slot ) {
     381           0 :     task_info->hash_changed = 1;
     382           0 :   }
     383           0 : }
     384             : 
     385             : void
     386             : fd_account_hash_task( void * tpool,
     387             :                       ulong t0, ulong t1,
     388             :                       void *args,
     389             :                       void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
     390             :                       ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
     391             :                       ulong m0 FD_PARAM_UNUSED, ulong m1 FD_PARAM_UNUSED,
     392           0 :                       ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED ) {
     393             : 
     394           0 :   fd_accounts_hash_task_data_t * task_data  = (fd_accounts_hash_task_data_t *)tpool;
     395           0 :   ulong                          start_idx  = t0;
     396           0 :   ulong                          stop_idx   = t1;
     397             : 
     398           0 :   fd_lthash_value_t * lthash = (fd_lthash_value_t*)args;
     399             : 
     400           0 :   for( ulong i=start_idx; i<=stop_idx; i++ ) {
     401           0 :     fd_accounts_hash_task_info_t * task_info = &task_data->info[i];
     402           0 :     fd_exec_slot_ctx_t *           slot_ctx  = task_info->slot_ctx;
     403           0 :     fd_account_hash( slot_ctx->funk,
     404           0 :                      slot_ctx->funk_txn,
     405           0 :                      task_info,
     406           0 :                      lthash,
     407           0 :                      fd_bank_slot_get( slot_ctx->bank ),
     408           0 :                      fd_bank_features_query( slot_ctx->bank )
     409           0 :       );
     410           0 :   }
     411           0 : }
     412             : 
     413             : void
     414             : fd_collect_modified_accounts( fd_exec_slot_ctx_t *           slot_ctx,
     415             :                               fd_accounts_hash_task_data_t * task_data,
     416           0 :                               fd_spad_t *                    runtime_spad ) {
     417           0 :   fd_funk_t *     funk = slot_ctx->funk;
     418           0 :   fd_funk_txn_t * txn  = slot_ctx->funk_txn;
     419             : 
     420           0 :   ulong rec_cnt = 0;
     421           0 :   for( fd_funk_rec_t const * rec = fd_funk_txn_first_rec( funk, txn );
     422           0 :        NULL != rec;
     423           0 :        rec = fd_funk_txn_next_rec( funk, rec ) ) {
     424             : 
     425           0 :     if( !fd_funk_key_is_acc( rec->pair.key  ) ) {
     426           0 :       continue;
     427           0 :     }
     428             : 
     429           0 :     fd_funk_rec_key_t const * pubkey = rec->pair.key;
     430             : 
     431           0 :     if (((pubkey->ul[0] == 0) & (pubkey->ul[1] == 0) & (pubkey->ul[2] == 0) & (pubkey->ul[3] == 0)))
     432           0 :       FD_LOG_WARNING(( "null pubkey (system program?) showed up as modified" ));
     433             : 
     434           0 :     rec_cnt++;
     435           0 :   }
     436             : 
     437           0 :   task_data->info    = fd_spad_alloc( runtime_spad, alignof(fd_accounts_hash_task_info_t), rec_cnt * sizeof(fd_accounts_hash_task_info_t) );
     438           0 :   task_data->info_sz = rec_cnt;
     439             : 
     440             :   /* Iterate over accounts that have been changed in the current
     441             :      database transaction. */
     442           0 :   ulong recs_iterated = 0;
     443           0 :   for( fd_funk_rec_t const * rec = fd_funk_txn_first_rec( funk, txn );
     444           0 :        NULL != rec;
     445           0 :        rec = fd_funk_txn_next_rec( funk, rec ) ) {
     446             : 
     447           0 :     if( !fd_funk_key_is_acc( rec->pair.key ) ) continue;
     448             : 
     449           0 :     fd_accounts_hash_task_info_t * task_info = &task_data->info[recs_iterated++];
     450             : 
     451           0 :     memcpy( task_info->acc_pubkey, rec->pair.key->uc, sizeof(fd_pubkey_t) );
     452           0 :     task_info->slot_ctx     = slot_ctx;
     453           0 :     task_info->hash_changed = 0;
     454           0 :     task_info->should_erase = 0;
     455           0 :   }
     456             : 
     457           0 :   if( FD_UNLIKELY( recs_iterated!=task_data->info_sz ) ) {
     458           0 :     FD_LOG_ERR(( "recs_iterated: %lu, task_data->info_sz: %lu", recs_iterated, task_data->info_sz ));
     459           0 :   }
     460           0 : }
     461             : 
     462             : int
     463             : fd_update_hash_bank_exec_hash( fd_exec_slot_ctx_t *           slot_ctx,
     464             :                                fd_hash_t *                    hash,
     465             :                                fd_capture_ctx_t *             capture_ctx,
     466             :                                fd_accounts_hash_task_data_t * task_datas,
     467             :                                ulong                          task_datas_cnt,
     468             :                                fd_lthash_value_t *            lt_hashes,
     469             :                                ulong                          lt_hashes_cnt,
     470             :                                ulong                          signature_cnt,
     471           0 :                                fd_spad_t *                    runtime_spad ) {
     472           0 :   ulong dirty_key_cnt = 0;
     473             : 
     474           0 :   fd_funk_t *     funk = slot_ctx->funk;
     475           0 :   fd_funk_txn_t * txn  = slot_ctx->funk_txn;
     476             : 
     477             :   // Apply the lthash changes to the bank lthash
     478           0 :   fd_slot_lthash_t * lthash_val = fd_bank_lthash_modify( slot_ctx->bank );
     479           0 :   for( ulong i = 0; i < lt_hashes_cnt; i++ ) {
     480           0 :     fd_lthash_add( (fd_lthash_value_t *)lthash_val->lthash, &lt_hashes[i] );
     481           0 :   }
     482             : 
     483           0 :   for( ulong j=0UL; j<task_datas_cnt; j++ ) {
     484             : 
     485           0 :     fd_accounts_hash_task_data_t * task_data = &task_datas[j];
     486             : 
     487           0 :     fd_pubkey_hash_pair_t * dirty_keys = fd_spad_alloc( runtime_spad,
     488           0 :                                                         FD_PUBKEY_HASH_PAIR_ALIGN,
     489           0 :                                                         task_data->info_sz * FD_PUBKEY_HASH_PAIR_FOOTPRINT );
     490             : 
     491           0 :     for( ulong i = 0; i < task_data->info_sz; i++ ) {
     492           0 :       fd_accounts_hash_task_info_t * task_info = &task_data->info[i];
     493             :       /* Upgrade to writable record */
     494           0 :       if( !task_info->hash_changed ) {
     495           0 :         continue;
     496           0 :       }
     497             : 
     498           0 :       FD_TXN_ACCOUNT_DECL( acc_rec );
     499             : 
     500           0 :       fd_pubkey_t const * acc_key = task_info->acc_pubkey;
     501           0 :       int err = fd_txn_account_init_from_funk_mutable( acc_rec, acc_key, funk, txn, 0, 0UL);
     502           0 :       if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     503           0 :         FD_LOG_ERR(( "failed to modify account during bank hash" ));
     504           0 :       }
     505             : 
     506             :       /* Update hash */
     507           0 :       acc_rec->vt->set_hash( acc_rec, task_info->acc_hash );
     508           0 :       acc_rec->vt->set_slot( acc_rec, fd_bank_slot_get( slot_ctx->bank ) );
     509             : 
     510           0 :       fd_txn_account_mutable_fini( acc_rec, funk, txn );
     511             : 
     512             :       /* Add account to "dirty keys" list, which will be added to the
     513             :         bank hash. */
     514             : 
     515           0 :       fd_pubkey_hash_pair_t * dirty_entry = &dirty_keys[dirty_key_cnt++];
     516           0 :       dirty_entry->rec = acc_rec->vt->get_rec( acc_rec );
     517           0 :       dirty_entry->hash = acc_rec->vt->get_hash( acc_rec );
     518             : 
     519           0 :       char acc_key_string[ FD_BASE58_ENCODED_32_SZ ];
     520           0 :       fd_acct_addr_cstr( acc_key_string, (uchar const*)acc_key );
     521           0 :       char owner_string[ FD_BASE58_ENCODED_32_SZ ];
     522           0 :       fd_acct_addr_cstr( owner_string, acc_rec->vt->get_owner( acc_rec )->uc );
     523             : 
     524           0 :       FD_LOG_DEBUG(( "fd_acc_mgr_update_hash: %s "
     525           0 :                     "slot: %lu "
     526           0 :                     "lamports: %lu  "
     527           0 :                     "owner: %s "
     528           0 :                     "executable: %s,  "
     529           0 :                     "rent_epoch: %lu, "
     530           0 :                     "data_len: %lu",
     531           0 :                     acc_key_string,
     532           0 :                     fd_bank_slot_get( slot_ctx->bank ),
     533           0 :                     acc_rec->vt->get_lamports( acc_rec ),
     534           0 :                     owner_string,
     535           0 :                     acc_rec->vt->is_executable( acc_rec ) ? "true" : "false",
     536           0 :                     acc_rec->vt->get_rent_epoch( acc_rec ),
     537           0 :                     acc_rec->vt->get_data_len( acc_rec ) ));
     538             : 
     539           0 :       if( capture_ctx != NULL && capture_ctx->capture != NULL && fd_bank_slot_get( slot_ctx->bank )>=capture_ctx->solcap_start_slot ) {
     540           0 :         fd_account_meta_t const * acc_meta = fd_funk_get_acc_meta_readonly( slot_ctx->funk,
     541           0 :                                                                             slot_ctx->funk_txn,
     542           0 :                                                                             task_info->acc_pubkey,
     543           0 :                                                                             NULL,
     544           0 :                                                                             &err,
     545           0 :                                                                             NULL);
     546           0 :         if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     547           0 :           FD_LOG_WARNING(( "failed to view account during capture" ));
     548           0 :           continue;
     549           0 :         }
     550             : 
     551           0 :         uchar const * acc_data = (uchar *)acc_meta + acc_meta->hlen;
     552             : 
     553           0 :         err = fd_solcap_write_account( capture_ctx->capture,
     554           0 :                                       acc_key->uc,
     555           0 :                                       acc_rec->vt->get_info( acc_rec ),
     556           0 :                                       acc_data,
     557           0 :                                       acc_rec->vt->get_data_len( acc_rec ),
     558           0 :                                       task_info->acc_hash->hash );
     559             : 
     560           0 :         if( FD_UNLIKELY( err ) ) {
     561           0 :           FD_LOG_ERR(( "Unable to write out solcap file" ));
     562           0 :         }
     563           0 :       }
     564           0 :     }
     565             : 
     566             :     /* Sort and hash "dirty keys" to the accounts delta hash. */
     567             : 
     568           0 :     fd_bank_signature_count_set( slot_ctx->bank, signature_cnt );
     569             : 
     570           0 :     fd_hash_bank( slot_ctx, capture_ctx, hash, dirty_keys, dirty_key_cnt);
     571             : 
     572           0 :     for( ulong i = 0; i < task_data->info_sz; i++ ) {
     573           0 :       fd_accounts_hash_task_info_t * task_info = &task_data->info[i];
     574             :       /* Upgrade to writable record */
     575           0 :       if( FD_LIKELY( !task_info->should_erase ) ) {
     576           0 :         continue;
     577           0 :       }
     578             : 
     579             :       /* All removed recs should be stored with the slot from the funk txn. */
     580           0 :       int err = FD_ACC_MGR_SUCCESS;
     581           0 :       fd_funk_rec_t const * rec = NULL;
     582           0 :       fd_funk_get_acc_meta_readonly( slot_ctx->funk,
     583           0 :         slot_ctx->funk_txn,
     584           0 :         task_info->acc_pubkey,
     585           0 :         &rec,
     586           0 :         &err,
     587           0 :         NULL);
     588           0 :       if( err != FD_ACC_MGR_SUCCESS ) {
     589           0 :         FD_LOG_ERR(( "failed to view account" ));
     590           0 :       }
     591           0 :       fd_funk_rec_remove( funk, txn, rec->pair.key, NULL, rec->pair.xid->ul[0] );
     592           0 :     }
     593           0 :   }
     594             : 
     595           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     596             : 
     597           0 : }
     598             : 
     599             : int
     600             : fd_update_hash_bank_tpool( fd_exec_slot_ctx_t * slot_ctx,
     601             :                            fd_capture_ctx_t *   capture_ctx,
     602             :                            fd_hash_t *          hash,
     603             :                            ulong                signature_cnt,
     604             :                            fd_tpool_t *         tpool,
     605           0 :                            fd_spad_t *          runtime_spad ) {
     606             : 
     607             :   /* Collect list of changed accounts to be added to bank hash */
     608           0 :   fd_accounts_hash_task_data_t * task_data = fd_spad_alloc( runtime_spad,
     609           0 :                                                             alignof(fd_accounts_hash_task_data_t),
     610           0 :                                                             sizeof(fd_accounts_hash_task_data_t) );
     611             : 
     612           0 :   fd_collect_modified_accounts( slot_ctx, task_data, runtime_spad );
     613             : 
     614           0 :   ulong wcnt = 0UL;
     615             : 
     616             :   /* Handle non-tpool case in a single-threaded manner */
     617           0 :   if( FD_LIKELY( tpool ) ){
     618           0 :     wcnt = fd_tpool_worker_cnt( tpool );
     619           0 :   } else {
     620           0 :     wcnt = 1UL;
     621           0 :   }
     622             : 
     623             : 
     624           0 :   fd_lthash_value_t * lt_hashes = fd_spad_alloc( runtime_spad,
     625           0 :                                                  FD_LTHASH_VALUE_ALIGN,
     626           0 :                                                  wcnt * FD_LTHASH_VALUE_FOOTPRINT );
     627           0 :   for( ulong i=0UL; i<wcnt; i++ ) {
     628           0 :     fd_lthash_zero( &lt_hashes[i] );
     629           0 :   }
     630             : 
     631           0 :   if( FD_LIKELY( tpool ) ) {
     632           0 :     ulong cnt_per_worker = (wcnt>1) ? (task_data->info_sz / (wcnt-1UL)) + 1UL : task_data->info_sz;
     633           0 :     for( ulong worker_idx=1UL; worker_idx<wcnt; worker_idx++ ) {
     634           0 :       ulong start_idx = (worker_idx-1UL) * cnt_per_worker;
     635           0 :       if( start_idx >= task_data->info_sz ) {
     636           0 :         wcnt = worker_idx;
     637           0 :         break;
     638           0 :       }
     639           0 :       ulong end_idx = fd_ulong_sat_sub((worker_idx) * cnt_per_worker, 1UL);
     640           0 :       if( end_idx >= task_data->info_sz )
     641           0 :         end_idx = fd_ulong_sat_sub( task_data->info_sz, 1UL );;
     642           0 :       fd_tpool_exec( tpool, worker_idx, fd_account_hash_task,
     643           0 :         task_data, start_idx, end_idx,
     644           0 :         &lt_hashes[worker_idx], slot_ctx, 0UL,
     645           0 :         0UL, 0UL, worker_idx, 0UL, 0UL, 0UL );
     646           0 :     }
     647             : 
     648           0 :     for( ulong worker_idx=1UL; worker_idx<wcnt; worker_idx++ ) {
     649           0 :       fd_tpool_wait( tpool, worker_idx );
     650           0 :     }
     651           0 :   } else {
     652           0 :     for( ulong i=0UL; i<task_data->info_sz; i++ ) {
     653           0 :       fd_accounts_hash_task_info_t * task_info = &task_data->info[i];
     654           0 :       fd_account_hash( slot_ctx->funk,
     655           0 :                        slot_ctx->funk_txn,
     656           0 :                        task_info,
     657           0 :                        &lt_hashes[ 0 ],
     658           0 :                        fd_bank_slot_get( slot_ctx->bank ),
     659           0 :                        fd_bank_features_query( slot_ctx->bank ) );
     660           0 :     }
     661           0 :   }
     662             : 
     663           0 :   return fd_update_hash_bank_exec_hash( slot_ctx,
     664           0 :                                         hash,
     665           0 :                                         capture_ctx,
     666           0 :                                         task_data,
     667           0 :                                         1UL,
     668           0 :                                         lt_hashes,
     669           0 :                                         wcnt,
     670           0 :                                         signature_cnt,
     671           0 :                                         runtime_spad );
     672           0 : }
     673             : 
     674             : void const *
     675             : fd_hash_account( uchar                     hash[ static 32 ],
     676             :                  fd_lthash_value_t *       lthash,
     677             :                  fd_account_meta_t const * m,
     678             :                  fd_pubkey_t const *       pubkey,
     679             :                  uchar const *             data,
     680             :                  int                       hash_needed,
     681           0 :                  fd_features_t const *     features FD_PARAM_UNUSED ) {
     682           0 :   ulong         lamports   = m->info.lamports;  /* >0UL */
     683           0 :   ulong         rent_epoch = m->info.rent_epoch;
     684           0 :   uchar         executable = m->info.executable & 0x1;
     685           0 :   uchar const * owner      = (uchar const *)m->info.owner;
     686             : 
     687           0 :   if( (hash_needed & FD_HASH_JUST_ACCOUNT_HASH) ) {
     688           0 :     fd_blake3_t b3[1];
     689           0 :     fd_blake3_init  ( b3 );
     690           0 :     fd_blake3_append( b3, &lamports,   sizeof( ulong ) );
     691           0 :     fd_blake3_append( b3, &rent_epoch, sizeof( ulong ) );
     692           0 :     fd_blake3_append( b3, data,        m->dlen         );
     693           0 :     fd_blake3_append( b3, &executable, sizeof( uchar ) );
     694           0 :     fd_blake3_append( b3, owner,       32UL            );
     695           0 :     fd_blake3_append( b3, pubkey,      32UL            );
     696           0 :     fd_blake3_fini  ( b3, hash );
     697           0 :   }
     698             : 
     699           0 :   if( (hash_needed & FD_HASH_JUST_LTHASH) ) {
     700           0 :     fd_blake3_t b3[1];
     701           0 :     fd_blake3_init  ( b3 );
     702           0 :     fd_blake3_append( b3, &lamports,   sizeof( ulong ) );
     703           0 :     fd_blake3_append( b3, data,        m->dlen         );
     704           0 :     fd_blake3_append( b3, &executable, sizeof( uchar ) );
     705           0 :     fd_blake3_append( b3, owner,       32UL            );
     706           0 :     fd_blake3_append( b3, pubkey,      32UL            );
     707           0 :     fd_blake3_fini_varlen( b3, lthash->bytes, FD_LTHASH_LEN_BYTES );
     708           0 :   }
     709             : 
     710           0 :   return hash;
     711           0 : }
     712             : 
     713             : void const *
     714             : fd_hash_account_current( uchar                     hash[ static 32 ],
     715             :                          fd_lthash_value_t *       lthash,
     716             :                          fd_account_meta_t const * account,
     717             :                          fd_pubkey_t const       * pubkey,
     718             :                          uchar const *             data,
     719             :                          int                       hash_needed,
     720           0 :                          fd_features_t const *     features ) {
     721           0 :   return fd_hash_account( hash, lthash, account, pubkey, data, hash_needed, features );
     722           0 : }
     723             : 
     724             : struct accounts_hash {
     725             :   fd_funk_rec_t * key;
     726             :   ulong  hash;
     727             : };
     728             : typedef struct accounts_hash accounts_hash_t;
     729             : 
     730             : #define MAP_NAME accounts_hash
     731           0 : #define MAP_KEY_T fd_funk_rec_t *
     732           0 : #define MAP_HASH_T ulong
     733           0 : #define MAP_KEY_EQUAL(k0,k1) ((NULL != k0) && (NULL != k1) && fd_funk_rec_key_eq( k0->pair.key, k1->pair.key ))
     734           0 : #define MAP_KEY_HASH(p) fd_funk_rec_key_hash( p->pair.key, 2887034UL )
     735             : #define MAP_KEY_EQUAL_IS_SLOW 1
     736           0 : #define MAP_KEY_NULL 0UL
     737           0 : #define MAP_KEY_INVAL(k) (NULL == k)
     738             : 
     739             : // #define MAP_KEY_COPY(kd,ks)   fd_funk_xid_key_pair_copy((kd),(ks))
     740             : 
     741           0 : #define MAP_T    accounts_hash_t
     742             : #include "../../util/tmpl/fd_map_dynamic.c"
     743             : 
     744             : /* fd_accounts_sorted_subrange_count will determine the number of accounts that
     745             :    should be in the accounts slice for a given range_idx. This is split out to
     746             :    from fd_accounts_sorted_subrange_gather to avoid dynamic resizing of the pair.
     747             : 
     748             :    TODO: The common code in these functions could be factored out. */
     749             : 
     750             : ulong
     751             : fd_accounts_sorted_subrange_count( fd_funk_t * funk,
     752             :                                    uint        range_idx,
     753           0 :                                    uint        range_cnt ) {
     754             : 
     755           0 :   fd_wksp_t *           wksp      = fd_funk_wksp( funk );
     756           0 :   ulong                 num_pairs = 0UL;
     757           0 :   ulong                 range_len = ULONG_MAX/range_cnt;
     758           0 :   ulong                 range_min = range_len*range_idx;
     759           0 :   ulong                 range_max = (range_idx+1U<range_cnt) ? (range_min+range_len-1U) : ULONG_MAX;
     760             : 
     761           0 :   fd_funk_all_iter_t iter[1];
     762           0 :   for( fd_funk_all_iter_new( funk, iter );
     763           0 :        !fd_funk_all_iter_done( iter );
     764           0 :        fd_funk_all_iter_next( iter ) ) {
     765           0 :     fd_funk_rec_t const * rec = fd_funk_all_iter_ele_const( iter );
     766             : 
     767           0 :     if ( !fd_funk_key_is_acc( rec->pair.key ) ||         /* not a solana record */
     768           0 :         (rec->flags & FD_FUNK_REC_FLAG_ERASE) ||        /* this is a tombstone */
     769           0 :         (rec->pair.xid->ul[0] | rec->pair.xid->ul[1]) != 0 /* not root xid */ ) {
     770           0 :       continue;
     771           0 :     }
     772             : 
     773           0 :     ulong n = __builtin_bswap64( rec->pair.key->ul[0] );
     774           0 :     if( n<range_min || n>range_max ) {
     775           0 :       continue;
     776           0 :     }
     777             : 
     778           0 :     fd_account_meta_t * metadata = (fd_account_meta_t *)fd_funk_val_const( rec, wksp );
     779           0 :     int is_empty = (metadata->info.lamports == 0);
     780           0 :     if( is_empty ) {
     781           0 :       continue;
     782           0 :     }
     783             : 
     784           0 :     if( (metadata->info.executable & ~1) != 0 ) {
     785           0 :       continue;
     786           0 :     }
     787             : 
     788           0 :     num_pairs++;
     789           0 :   }
     790             : 
     791           0 :   return num_pairs;
     792           0 : }
     793             : 
     794             : void
     795             : fd_accounts_sorted_subrange_gather( fd_funk_t *             funk,
     796             :                                     uint                    range_idx,
     797             :                                     uint                    range_cnt,
     798             :                                     ulong *                 num_pairs_out,
     799             :                                     fd_lthash_value_t *     lthash_value_out,
     800             :                                     fd_pubkey_hash_pair_t * pairs,
     801           0 :                                     fd_features_t const *   features ) {
     802             : 
     803           0 :   fd_wksp_t *     wksp              = fd_funk_wksp( funk );
     804           0 :   ulong           num_pairs         = 0UL;
     805           0 :   ulong           range_len         = ULONG_MAX/range_cnt;
     806           0 :   ulong           range_min         = range_len*range_idx;
     807           0 :   ulong           range_max         = (range_idx+1U<range_cnt) ? (range_min+range_len-1U) : ULONG_MAX;
     808             : 
     809           0 :   fd_lthash_value_t accum = {0};
     810             : 
     811           0 :   fd_funk_all_iter_t iter[1];
     812           0 :   for( fd_funk_all_iter_new( funk, iter );
     813           0 :        !fd_funk_all_iter_done( iter );
     814           0 :        fd_funk_all_iter_next( iter ) ) {
     815           0 :     fd_funk_rec_t const * rec = fd_funk_all_iter_ele_const( iter );
     816           0 :     if ( !fd_funk_key_is_acc( rec->pair.key ) ||         /* not a solana record */
     817           0 :         (rec->flags & FD_FUNK_REC_FLAG_ERASE) ||        /* this is a tombstone */
     818           0 :         (rec->pair.xid->ul[0] | rec->pair.xid->ul[1]) != 0 /* not root xid */ ) {
     819           0 :       continue;
     820           0 :     }
     821             : 
     822           0 :     ulong n = __builtin_bswap64( rec->pair.key->ul[0] );
     823           0 :     if( n<range_min || n>range_max ) {
     824           0 :       continue;
     825           0 :     }
     826           0 :     fd_account_meta_t * metadata = (fd_account_meta_t *)fd_funk_val_const( rec, wksp );
     827           0 :     int is_empty = (metadata->info.lamports == 0);
     828           0 :     if( is_empty ) {
     829           0 :       continue;
     830           0 :     }
     831             : 
     832             :     /* FIXME: remove magic number */
     833           0 :     uchar hash[32];
     834           0 :     fd_lthash_value_t new_lthash_value = {0};
     835             : 
     836           0 :     fd_hash_account_current( (uchar *)hash, &new_lthash_value, metadata, fd_type_pun_const(rec->pair.key->uc), fd_account_meta_get_data( metadata ), FD_HASH_BOTH_HASHES, features  );
     837           0 :     fd_lthash_add( &accum, &new_lthash_value );
     838             : 
     839           0 :     fd_hash_t * h = (fd_hash_t *)metadata->hash;
     840           0 :     if( FD_LIKELY( (h->ul[0] | h->ul[1] | h->ul[2] | h->ul[3]) != 0 ) ) {
     841           0 :       if( FD_UNLIKELY( fd_account_meta_exists( metadata ) && memcmp( metadata->hash, &hash, 32 ) != 0 ) ) {
     842           0 :         FD_LOG_WARNING(( "snapshot hash (%s) doesn't match calculated hash (%s)", FD_BASE58_ENC_32_ALLOCA( metadata->hash ), FD_BASE58_ENC_32_ALLOCA( &hash ) ));
     843           0 :       }
     844           0 :     } else {
     845           0 :       fd_memcpy( metadata->hash, &hash, sizeof(fd_hash_t) );
     846           0 :     }
     847             : 
     848           0 :     if( (metadata->info.executable & ~1) != 0 ) {
     849           0 :       continue;
     850           0 :     }
     851             : 
     852           0 :     fd_pubkey_hash_pair_t * pair = &pairs[num_pairs++];
     853           0 :     pair->rec                    = rec;
     854           0 :     pair->hash                   = (const fd_hash_t *)metadata->hash;
     855           0 :   }
     856             : 
     857           0 :   sort_pubkey_hash_pair_inplace( pairs, num_pairs );
     858             : 
     859           0 :   *num_pairs_out = num_pairs;
     860             : 
     861           0 :   if (lthash_value_out) {
     862           0 :     fd_lthash_add( lthash_value_out, &accum  );
     863           0 :   }
     864           0 : }
     865             : 
     866             : static void
     867             : fd_accounts_sorted_subrange_count_task( void *tpool,
     868             :                                         ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
     869             :                                         void *args FD_PARAM_UNUSED,
     870             :                                         void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
     871             :                                         ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
     872             :                                         ulong m0, ulong m1 FD_PARAM_UNUSED,
     873           0 :                                         ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED ) {
     874           0 :   fd_subrange_task_info_t * task_info = (fd_subrange_task_info_t *)tpool;
     875           0 :   task_info->lists[m0].pairs_len = fd_accounts_sorted_subrange_count( task_info->funk,
     876           0 :                                                                       (uint)m0,
     877           0 :                                                                       (uint)task_info->num_lists );
     878           0 : }
     879             : 
     880             : static void
     881             : fd_accounts_sorted_subrange_gather_task( void *tpool,
     882             :                                          ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
     883             :                                          void *args FD_PARAM_UNUSED,
     884             :                                          void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
     885             :                                          ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
     886             :                                          ulong m0, ulong m1 FD_PARAM_UNUSED,
     887           0 :                                          ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED ) {
     888           0 :   fd_subrange_task_info_t * task_info = (fd_subrange_task_info_t *)tpool;
     889           0 :   fd_accounts_sorted_subrange_gather( task_info->funk, (uint)m0, (uint)task_info->num_lists,
     890           0 :                                       &task_info->lists[m0].pairs_len, &task_info->lthash_values[m0],
     891           0 :                                       task_info->lists[m0].pairs, task_info->features );
     892           0 : }
     893             : 
     894             : void
     895             : fd_accounts_hash_counter_and_gather_tpool_cb( void * para_arg_1,
     896             :                                               void * para_arg_2 FD_PARAM_UNUSED,
     897             :                                               void * fn_arg_1,
     898             :                                               void * fn_arg_2,
     899             :                                               void * fn_arg_3 FD_PARAM_UNUSED,
     900           0 :                                               void * fn_arg_4 FD_PARAM_UNUSED ) {
     901           0 :   fd_tpool_t *              tpool        = (fd_tpool_t *)para_arg_1;
     902           0 :   fd_subrange_task_info_t * task_info    = (fd_subrange_task_info_t *)fn_arg_1;
     903           0 :   fd_spad_t *               runtime_spad = (fd_spad_t *)fn_arg_2;
     904             : 
     905           0 :   ulong num_lists = fd_tpool_worker_cnt( tpool ) - 1UL;
     906             : 
     907           0 :   fd_pubkey_hash_pair_list_t * lists         = fd_spad_alloc( runtime_spad, alignof(fd_pubkey_hash_pair_list_t), num_lists * sizeof(fd_pubkey_hash_pair_list_t) );
     908           0 :   fd_lthash_value_t *          lthash_values = fd_spad_alloc( runtime_spad, FD_LTHASH_VALUE_ALIGN, num_lists * FD_LTHASH_VALUE_FOOTPRINT );
     909           0 :   for( ulong i = 0; i < num_lists; i++ ) {
     910           0 :     fd_lthash_zero(&lthash_values[i] );
     911           0 :   }
     912             : 
     913           0 :   task_info->num_lists     = num_lists;
     914           0 :   task_info->lists         = lists;
     915           0 :   task_info->lthash_values = lthash_values;
     916             : 
     917             :   /* Exec and wait counting the number of records to hash and exec */
     918           0 :   ulong worker_cnt = fd_tpool_worker_cnt( tpool );
     919           0 :   for( ulong worker_idx=1UL; worker_idx<worker_cnt; worker_idx++ ) {
     920           0 :     fd_tpool_exec( tpool, worker_idx, fd_accounts_sorted_subrange_count_task, task_info, 0UL,
     921           0 :                    0UL, NULL, NULL, 0UL, 0UL, 0UL, worker_idx-1UL, 0UL, 0UL, 0UL );
     922           0 :   }
     923             : 
     924           0 :   for( ulong worker_idx=1UL; worker_idx<worker_cnt; worker_idx++ ) {
     925           0 :     fd_tpool_wait( tpool, worker_idx );
     926           0 :   }
     927             : 
     928             :   /* Allocate out the number of pairs calculated. */
     929           0 :   for( ulong i=0UL; i<task_info->num_lists; i++ ) {
     930           0 :     task_info->lists[i].pairs     = fd_spad_alloc( runtime_spad,
     931           0 :                                                    FD_PUBKEY_HASH_PAIR_ALIGN,
     932           0 :                                                    task_info->lists[i].pairs_len * sizeof(fd_pubkey_hash_pair_t) );
     933           0 :     task_info->lists[i].pairs_len = 0UL;
     934           0 :   }
     935             : 
     936             :   /* Exec and wait gathering the accounts to hash. */
     937           0 :   for( ulong worker_idx=1UL; worker_idx<worker_cnt; worker_idx++ ) {
     938           0 :     fd_tpool_exec( tpool, worker_idx, fd_accounts_sorted_subrange_gather_task, task_info, 0UL,
     939           0 :                     0UL, NULL, NULL, 0UL, 0UL, 0UL, worker_idx-1UL, 0UL, 0UL, 0UL );
     940           0 :   }
     941             : 
     942           0 :   for( ulong worker_idx=1UL; worker_idx<worker_cnt; worker_idx++ ) {
     943           0 :     fd_tpool_wait( tpool, worker_idx );
     944           0 :   }
     945             : 
     946           0 : }
     947             : 
     948             : int
     949             : fd_accounts_hash( fd_funk_t *             funk,
     950             :                   ulong                   slot,
     951             :                   fd_hash_t *             accounts_hash,
     952             :                   fd_spad_t *             runtime_spad,
     953             :                   fd_features_t const *   features,
     954             :                   fd_exec_para_cb_ctx_t * exec_para_ctx,
     955           0 :                   fd_lthash_value_t *     lt_hash ) {
     956             : 
     957           0 :   int lthash_enabled = (NULL != lt_hash) && (FD_FEATURE_ACTIVE( slot, features, snapshots_lt_hash ) || FD_FEATURE_ACTIVE( slot, features, accounts_lt_hash ) );
     958             : 
     959           0 :   FD_LOG_NOTICE(("accounts_hash start"));
     960             : 
     961             :   /* FIXME: this is not the correct lock to use, although in reality this is fine as we never modify
     962             :      accounts at the same time as hashing them. Once the hashing has been moved into tiles, we need to
     963             :      change it so that we partition by hash chains, and acquire the lock on the individual hash chains. */
     964           0 :   fd_funk_rec_pool_t * rec_pool = fd_funk_rec_pool( funk );
     965           0 :   fd_funk_rec_pool_lock( rec_pool, 1 );
     966           0 :   fd_funk_txn_start_read( funk );
     967             : 
     968           0 :   if( fd_exec_para_cb_is_single_threaded( exec_para_ctx ) ) {
     969           0 :     ulong               num_pairs     = 0UL;
     970           0 :     fd_lthash_value_t * lthash_values = ( NULL != lt_hash ) ? fd_spad_alloc( runtime_spad, FD_LTHASH_VALUE_ALIGN, FD_LTHASH_VALUE_FOOTPRINT ) : NULL;
     971             : 
     972           0 :     if ( NULL != lt_hash )
     973           0 :       fd_lthash_zero( &lthash_values[0] );
     974             : 
     975           0 :     fd_pubkey_hash_pair_t * pairs =
     976           0 :         fd_spad_alloc( runtime_spad, FD_PUBKEY_HASH_PAIR_ALIGN, fd_funk_rec_max( funk ) * sizeof(fd_pubkey_hash_pair_t) );
     977             : 
     978           0 :     fd_accounts_sorted_subrange_gather( funk, 0, 1, &num_pairs, lthash_values, pairs, features );
     979           0 :     if( FD_UNLIKELY( !pairs ) ) {
     980           0 :       FD_LOG_ERR(( "failed to allocate memory for account hash" ));
     981           0 :     }
     982           0 :     fd_pubkey_hash_pair_list_t list1 = { .pairs = pairs, .pairs_len = num_pairs };
     983           0 :     fd_hash_account_deltas( &list1, 1, accounts_hash );
     984             : 
     985           0 :     if ( NULL != lt_hash )
     986           0 :       fd_lthash_add( lt_hash, &lthash_values[0] );
     987             : 
     988           0 :   } else {
     989             :     /* First calculate how big the list needs to be sized out to be, bump
     990             :        allocate the size of the array then caclulate the hash. */
     991             : 
     992           0 :     fd_subrange_task_info_t task_info = {
     993           0 :       .features      = features,
     994           0 :       .funk          = funk,
     995           0 :     };
     996             : 
     997           0 :     exec_para_ctx->fn_arg_1 = &task_info;
     998           0 :     exec_para_ctx->fn_arg_2 = runtime_spad;
     999           0 :     fd_exec_para_call_func( exec_para_ctx );
    1000             : 
    1001           0 :     fd_hash_account_deltas( task_info.lists, task_info.num_lists, accounts_hash );
    1002             : 
    1003           0 :     if ( NULL!= lt_hash ) {
    1004           0 :       for( ulong i = 0UL; i < task_info.num_lists; i++ ) {
    1005           0 :         fd_lthash_add( lt_hash, &task_info.lthash_values[i] );
    1006           0 :       }
    1007           0 :     }
    1008           0 :   }
    1009             : 
    1010           0 :   if( lthash_enabled ) {
    1011           0 :     FD_LOG_NOTICE(( "accounts_lthash %s", FD_LTHASH_ENC_32_ALLOCA( lt_hash ) ));
    1012           0 :   } else {
    1013           0 :     FD_LOG_NOTICE(( "accounts_hash %s", FD_BASE58_ENC_32_ALLOCA( accounts_hash->hash ) ));
    1014           0 :   }
    1015             : 
    1016           0 :   fd_funk_rec_pool_unlock( rec_pool );
    1017           0 :   fd_funk_txn_end_read( funk );
    1018             : 
    1019           0 :   return 0;
    1020           0 : }
    1021             : 
    1022             : int
    1023             : fd_accounts_hash_inc_only( fd_exec_slot_ctx_t * slot_ctx,
    1024             :                            fd_hash_t *          accounts_hash,
    1025             :                            fd_funk_txn_t *      child_txn,
    1026             :                            ulong                do_hash_verify,
    1027           0 :                            fd_spad_t *          spad ) {
    1028           0 :   FD_LOG_NOTICE(( "accounts_hash_inc_only start for txn %p, do_hash_verify=%s", (void *)child_txn, do_hash_verify ? "true" : "false" ));
    1029             : 
    1030           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
    1031             : 
    1032           0 :   fd_funk_t * funk = slot_ctx->funk;
    1033           0 :   fd_wksp_t * wksp = fd_funk_wksp( funk );
    1034             : 
    1035             :   // How many total records are we dealing with?
    1036           0 :   ulong                   num_pairs         = 0UL;
    1037           0 :   ulong                   num_iter_accounts = 0UL;
    1038           0 :   for (fd_funk_rec_t const *rec = fd_funk_txn_first_rec( funk, child_txn ); NULL != rec; rec = fd_funk_txn_next_rec(funk, rec)) {
    1039           0 :     if ( !fd_funk_key_is_acc( rec->pair.key ) || ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) )
    1040           0 :       continue;
    1041           0 :     ++num_iter_accounts;
    1042           0 :   }
    1043             : 
    1044           0 :   fd_pubkey_hash_pair_t * pairs             = fd_spad_alloc( spad, FD_PUBKEY_HASH_PAIR_ALIGN, num_iter_accounts * sizeof(fd_pubkey_hash_pair_t) );
    1045           0 :   if( FD_UNLIKELY( !pairs ) ) {
    1046           0 :     FD_LOG_ERR(( "failed to allocate memory for pairs" ));
    1047           0 :   }
    1048             : 
    1049           0 :   fd_blake3_t * b3 = NULL;
    1050             : 
    1051           0 :   for (fd_funk_rec_t const *rec = fd_funk_txn_first_rec( funk, child_txn ); NULL != rec; rec = fd_funk_txn_next_rec(funk, rec)) {
    1052           0 :     if ( !fd_funk_key_is_acc( rec->pair.key ) || ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) )
    1053           0 :       continue;
    1054             : 
    1055           0 :     fd_account_meta_t * metadata = (fd_account_meta_t *) fd_funk_val_const( rec, wksp );
    1056           0 :     int is_empty = (metadata->info.lamports == 0);
    1057             : 
    1058           0 :     if (is_empty) {
    1059           0 :       pairs[num_pairs].rec = rec;
    1060             : 
    1061           0 :       fd_hash_t * hash = fd_spad_alloc( spad, alignof(fd_hash_t), sizeof(fd_hash_t) );
    1062           0 :       if( NULL == b3 ) {
    1063           0 :         b3 = fd_spad_alloc( spad, alignof(fd_blake3_t), sizeof(fd_blake3_t) );
    1064           0 :       }
    1065           0 :       fd_blake3_init( b3 );
    1066           0 :       fd_blake3_append( b3, rec->pair.key->uc, sizeof( fd_pubkey_t ) );
    1067           0 :       fd_blake3_fini( b3, hash );
    1068             : 
    1069           0 :       pairs[num_pairs].hash = hash;
    1070           0 :       num_pairs++;
    1071           0 :       continue;
    1072           0 :     } else {
    1073           0 :       fd_hash_t *h = (fd_hash_t *) metadata->hash;
    1074           0 :       if ((h->ul[0] | h->ul[1] | h->ul[2] | h->ul[3]) == 0) {
    1075             :         // By the time we fall into this case, we can assume the ignore_slot feature is enabled...
    1076           0 :         fd_hash_account_current( (uchar *) metadata->hash, NULL, metadata, fd_type_pun_const(rec->pair.key->uc), fd_account_meta_get_data(metadata), FD_HASH_JUST_ACCOUNT_HASH, fd_bank_features_query( slot_ctx->bank ) );
    1077           0 :       } else if( do_hash_verify ) {
    1078           0 :         uchar hash[32];
    1079           0 :         fd_hash_account_current( (uchar *) &hash, NULL, metadata, fd_type_pun_const(rec->pair.key->uc), fd_account_meta_get_data(metadata), FD_HASH_JUST_ACCOUNT_HASH, fd_bank_features_query( slot_ctx->bank ) );
    1080           0 :         if ( fd_account_meta_exists( metadata ) && memcmp( metadata->hash, &hash, 32 ) != 0 ) {
    1081           0 :           FD_LOG_WARNING(( "snapshot hash (%s) doesn't match calculated hash (%s)", FD_BASE58_ENC_32_ALLOCA( metadata->hash ), FD_BASE58_ENC_32_ALLOCA( &hash ) ));
    1082           0 :         }
    1083           0 :       }
    1084           0 :     }
    1085             : 
    1086           0 :     if ((metadata->info.executable & ~1) != 0)
    1087           0 :       continue;
    1088             : 
    1089           0 :     pairs[num_pairs].rec = rec;
    1090           0 :     pairs[num_pairs].hash = (const fd_hash_t *)metadata->hash;
    1091           0 :     num_pairs++;
    1092           0 :   }
    1093             : 
    1094           0 :   sort_pubkey_hash_pair_inplace( pairs, num_pairs );
    1095           0 :   fd_pubkey_hash_pair_list_t list1 = { .pairs = pairs, .pairs_len = num_pairs };
    1096           0 :   fd_hash_account_deltas( &list1, 1, accounts_hash );
    1097             : 
    1098           0 :   FD_LOG_INFO(( "accounts_hash %s", FD_BASE58_ENC_32_ALLOCA( accounts_hash->hash) ));
    1099             : 
    1100           0 :   } FD_SPAD_FRAME_END;
    1101             : 
    1102           0 :   return 0;
    1103           0 : }
    1104             : 
    1105             : /* Same as fd_accounts_hash_inc_only but takes a list of pubkeys to hash.
    1106             :    Query the accounts from the root of funk. This is done as a read-only
    1107             :    way to generate an accounts hash from a subset of accounts from funk. */
    1108             : static int
    1109             : fd_accounts_hash_inc_no_txn( fd_funk_t *                 funk,
    1110             :                              fd_hash_t *                 accounts_hash,
    1111             :                              fd_funk_rec_key_t const * * pubkeys,
    1112             :                              ulong                       pubkeys_len,
    1113             :                              ulong                       do_hash_verify,
    1114             :                              fd_spad_t *                 spad,
    1115           0 :                              fd_features_t *             features ) {
    1116           0 :   FD_LOG_NOTICE(( "accounts_hash_inc_no_txn" ));
    1117             : 
    1118           0 :   fd_wksp_t *     wksp    = fd_funk_wksp( funk );
    1119             : 
    1120             :   /* Pre-allocate the number of pubkey pairs that we are iterating over. */
    1121             : 
    1122           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
    1123             : 
    1124           0 :   ulong                   num_pairs         = 0UL;
    1125           0 :   fd_pubkey_hash_pair_t * pairs             = fd_spad_alloc( spad,
    1126           0 :                                                              FD_PUBKEY_HASH_PAIR_ALIGN,
    1127           0 :                                                              pubkeys_len * sizeof(fd_pubkey_hash_pair_t) );
    1128             : 
    1129           0 :   if( FD_UNLIKELY( !pairs ) ) {
    1130           0 :     FD_LOG_ERR(( "failed to allocate memory for pairs" ));
    1131           0 :   }
    1132             : 
    1133           0 :   fd_blake3_t * b3 = NULL;
    1134             : 
    1135             : 
    1136           0 :   for( ulong i=0UL; i<pubkeys_len; i++ ) {
    1137           0 :     fd_funk_rec_query_t query[1];
    1138           0 :     fd_funk_rec_t const * rec = fd_funk_rec_query_try( funk, NULL, pubkeys[i], query );
    1139             : 
    1140           0 :     fd_account_meta_t * metadata = (fd_account_meta_t *) fd_funk_val_const( rec, wksp );
    1141           0 :     int is_empty = (!metadata || metadata->info.lamports == 0);
    1142             : 
    1143           0 :     if( is_empty ) {
    1144           0 :       pairs[num_pairs].rec = rec;
    1145             : 
    1146           0 :       fd_hash_t * hash = fd_spad_alloc( spad, alignof(fd_hash_t), sizeof(fd_hash_t) );
    1147           0 :       if( !b3 ) {
    1148           0 :         b3 = fd_spad_alloc( spad, alignof(fd_blake3_t), sizeof(fd_blake3_t) );
    1149           0 :       }
    1150           0 :       fd_blake3_init( b3 );
    1151           0 :       fd_blake3_append( b3, rec->pair.key->uc, sizeof(fd_pubkey_t) );
    1152           0 :       fd_blake3_fini( b3, hash );
    1153             : 
    1154           0 :       pairs[ num_pairs ].hash = hash;
    1155           0 :       num_pairs++;
    1156           0 :       continue;
    1157           0 :     } else {
    1158           0 :       fd_hash_t *h = (fd_hash_t*)metadata->hash;
    1159           0 :       if( !(h->ul[ 0 ] | h->ul[ 1 ] | h->ul[ 2 ] | h->ul[ 3 ]) ) {
    1160             :         // By the time we fall into this case, we can assume the ignore_slot feature is enabled...
    1161           0 :         fd_hash_account_current( (uchar*)metadata->hash, NULL, metadata, fd_type_pun_const(rec->pair.key->uc), fd_account_meta_get_data( metadata ), FD_HASH_JUST_ACCOUNT_HASH, features );
    1162           0 :       } else if( do_hash_verify ) {
    1163           0 :         uchar hash[ FD_HASH_FOOTPRINT ];
    1164           0 :         fd_hash_account_current( (uchar*)&hash, NULL, metadata, fd_type_pun_const(rec->pair.key->uc), fd_account_meta_get_data( metadata ), FD_HASH_JUST_ACCOUNT_HASH, features );
    1165           0 :         if( fd_account_meta_exists( metadata ) && memcmp( metadata->hash, &hash, FD_HASH_FOOTPRINT ) ) {
    1166           0 :           FD_LOG_WARNING(( "snapshot hash (%s) doesn't match calculated hash (%s)", FD_BASE58_ENC_32_ALLOCA(metadata->hash), FD_BASE58_ENC_32_ALLOCA(&hash) ));
    1167           0 :         }
    1168           0 :       }
    1169           0 :     }
    1170             : 
    1171           0 :     if( (metadata->info.executable & ~1) ) {
    1172           0 :       continue;
    1173           0 :     }
    1174             : 
    1175           0 :     pairs[ num_pairs ].rec = rec;
    1176           0 :     pairs[ num_pairs ].hash = (fd_hash_t const *)metadata->hash;
    1177           0 :     num_pairs++;
    1178             : 
    1179           0 :     FD_TEST( !fd_funk_rec_query_test( query ) );
    1180           0 :   }
    1181             : 
    1182           0 :   sort_pubkey_hash_pair_inplace( pairs, num_pairs );
    1183           0 :   fd_pubkey_hash_pair_list_t list1 = { .pairs = pairs, .pairs_len = num_pairs };
    1184           0 :   fd_hash_account_deltas( &list1, 1, accounts_hash );
    1185             : 
    1186           0 :   } FD_SPAD_FRAME_END;
    1187             : 
    1188           0 :   FD_LOG_INFO(( "accounts_hash %s", FD_BASE58_ENC_32_ALLOCA( accounts_hash->hash ) ));
    1189             : 
    1190           0 :   return 0;
    1191           0 : }
    1192             : 
    1193             : 
    1194             : /* TODO: Combine with the above to get correct snapshot hash verification. */
    1195             : 
    1196             : int
    1197             : fd_snapshot_service_hash( fd_hash_t *       accounts_hash,
    1198             :                           fd_hash_t *       snapshot_hash,
    1199             :                           fd_funk_t *       funk,
    1200             :                           fd_tpool_t *      tpool,
    1201             :                           fd_spad_t *       runtime_spad,
    1202           0 :                           fd_features_t *   features ) {
    1203             : 
    1204           0 :   fd_sha256_t h;
    1205             : 
    1206           0 :   fd_exec_para_cb_ctx_t exec_para_ctx = {
    1207           0 :     .func       = fd_accounts_hash_counter_and_gather_tpool_cb,
    1208           0 :     .para_arg_1 = tpool
    1209           0 :   };
    1210             : 
    1211             :   /* FIXME: this has an invalid slot number. */
    1212           0 :   fd_accounts_hash( funk, 0UL, accounts_hash, runtime_spad, features, &exec_para_ctx, NULL );
    1213             : 
    1214             : 
    1215             :   // int should_include_eah = eah_stop_slot != ULONG_MAX && eah_start_slot == ULONG_MAX;
    1216           0 :   int should_include_eah = 0;
    1217             : 
    1218           0 :   if( should_include_eah ) {
    1219           0 :     fd_sha256_init( &h );
    1220           0 :     fd_sha256_append( &h, (uchar const *) accounts_hash, sizeof( fd_hash_t ) );
    1221             :     // fd_sha256_append( &h, (uchar const *) slot_bank->epoch_account_hash.hash, sizeof( fd_hash_t ) );
    1222           0 :     fd_sha256_fini( &h, snapshot_hash );
    1223           0 :   } else {
    1224           0 :     *snapshot_hash = *accounts_hash;
    1225           0 :   }
    1226             : 
    1227           0 :   return 0;
    1228           0 : }
    1229             : 
    1230             : int
    1231             : fd_snapshot_service_inc_hash( fd_hash_t *                 accounts_hash,
    1232             :                               fd_hash_t *                 snapshot_hash,
    1233             :                               fd_funk_t *                 funk,
    1234             :                               fd_funk_rec_key_t const * * pubkeys,
    1235             :                               ulong                       pubkeys_len,
    1236             :                               fd_spad_t *                 spad,
    1237           0 :                               fd_features_t              *features ) {
    1238           0 :   fd_sha256_t h;
    1239           0 :   fd_accounts_hash_inc_no_txn( funk, accounts_hash, pubkeys, pubkeys_len, 0UL, spad, features );
    1240             : 
    1241           0 :   int should_include_eah = 0;
    1242             : 
    1243           0 :   if( should_include_eah ) {
    1244           0 :     fd_sha256_init( &h );
    1245           0 :     fd_sha256_append( &h, (uchar const *) accounts_hash, sizeof( fd_hash_t ) );
    1246             :     // fd_sha256_append( &h, (uchar const *) slot_bank->epoch_account_hash.hash, sizeof( fd_hash_t ) );
    1247           0 :     fd_sha256_fini( &h, snapshot_hash );
    1248           0 :   } else {
    1249           0 :     *snapshot_hash = *accounts_hash;
    1250           0 :   }
    1251             : 
    1252           0 :   return 0;
    1253           0 : }
    1254             : 
    1255             : /* Re-computes the lthash from the current slot */
    1256             : void
    1257             : fd_accounts_check_lthash( fd_funk_t *      funk,
    1258             :                           fd_funk_txn_t *  funk_txn,
    1259             :                           fd_spad_t *      runtime_spad,
    1260           0 :                           fd_features_t *  features ) {
    1261             : 
    1262           0 :   fd_wksp_t *          wksp     = fd_funk_wksp( funk );
    1263           0 :   fd_funk_txn_pool_t * txn_pool = fd_funk_txn_pool( funk );
    1264             : 
    1265             :   // How many txns are we dealing with?
    1266           0 :   fd_funk_txn_start_read( funk );
    1267           0 :   ulong txn_cnt = 1;
    1268           0 :   fd_funk_txn_t * txn = funk_txn;
    1269           0 :   while (NULL != txn) {
    1270           0 :     txn_cnt++;
    1271           0 :     txn = fd_funk_txn_parent( txn, txn_pool );
    1272           0 :   }
    1273             : 
    1274           0 :   fd_funk_txn_t ** txns = fd_alloca_check(sizeof(fd_funk_txn_t *), sizeof(fd_funk_txn_t *) * txn_cnt);
    1275           0 :   if ( FD_UNLIKELY(NULL == txns))
    1276           0 :     FD_LOG_ERR(( "Unable to allocate txn pointers" ));
    1277             : 
    1278             :   // Lay it flat to make it easier to walk backwards up the chain from
    1279             :   // the root
    1280           0 :   txn = funk_txn;
    1281           0 :   ulong txn_idx = txn_cnt;
    1282           0 :   while (1) {
    1283           0 :     txns[--txn_idx] = txn;
    1284           0 :     if (NULL == txn)
    1285           0 :       break;
    1286           0 :     txn = fd_funk_txn_parent( txn, txn_pool );
    1287           0 :   }
    1288             : 
    1289             :   // How many total records are we dealing with?
    1290           0 :   ulong num_iter_accounts = fd_funk_rec_max( funk );
    1291             : 
    1292           0 :   int accounts_hash_slots = fd_ulong_find_msb(num_iter_accounts  ) + 1;
    1293             : 
    1294           0 :   FD_LOG_WARNING(("allocating memory for hash.  num_iter_accounts: %lu   slots: %d", num_iter_accounts, accounts_hash_slots));
    1295           0 :   void * hashmem = fd_spad_alloc( runtime_spad, accounts_hash_align(), accounts_hash_footprint(accounts_hash_slots));
    1296           0 :   FD_LOG_WARNING(("initializing memory for hash"));
    1297           0 :   accounts_hash_t * hash_map = accounts_hash_join(accounts_hash_new(hashmem, accounts_hash_slots));
    1298             : 
    1299           0 :   FD_LOG_WARNING(("copying in accounts"));
    1300             : 
    1301             :   // walk up the transactions...
    1302           0 :   for (ulong idx = 0; idx < txn_cnt; idx++) {
    1303           0 :     for (fd_funk_rec_t const *rec = fd_funk_txn_first_rec( funk, txns[idx]);
    1304           0 :          NULL != rec;
    1305           0 :          rec = fd_funk_txn_next_rec(funk, rec)) {
    1306           0 :       if ( fd_funk_key_is_acc( rec->pair.key ) && !( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) {
    1307           0 :         accounts_hash_t * q = accounts_hash_query(hash_map, (fd_funk_rec_t *) rec, NULL);
    1308           0 :         if (NULL != q)
    1309           0 :           accounts_hash_remove(hash_map, q);
    1310           0 :         if (!(rec->flags & FD_FUNK_REC_FLAG_ERASE))
    1311           0 :           accounts_hash_insert(hash_map, (fd_funk_rec_t *) rec);
    1312           0 :       }
    1313           0 :     }
    1314           0 :   }
    1315             : 
    1316           0 :   FD_LOG_WARNING(("assumulating a new lthash"));
    1317             : 
    1318             :   // Initialize the accumulator to zero
    1319           0 :   fd_lthash_value_t acc_lthash;
    1320           0 :   fd_lthash_zero( &acc_lthash );
    1321             : 
    1322           0 :   ulong slot_cnt = accounts_hash_slot_cnt(hash_map);;
    1323           0 :   for( ulong slot_idx=0UL; slot_idx<slot_cnt; slot_idx++ ) {
    1324           0 :     accounts_hash_t *slot = &hash_map[slot_idx];
    1325           0 :     if (FD_UNLIKELY (NULL != slot->key)) {
    1326           0 :       void const * data = fd_funk_val_const( slot->key, wksp );
    1327           0 :       fd_account_meta_t * metadata = (fd_account_meta_t *)fd_type_pun_const( data );
    1328           0 :       if( FD_UNLIKELY(metadata->info.lamports != 0) ) {
    1329           0 :         uchar * acc_data = fd_account_meta_get_data(metadata);
    1330           0 :         uchar hash  [ 32 ];
    1331           0 :         fd_lthash_value_t new_lthash_value;
    1332           0 :         fd_lthash_zero(&new_lthash_value);
    1333           0 :         fd_hash_account_current( hash, &new_lthash_value, metadata, fd_type_pun_const(slot->key->pair.key[0].uc), acc_data, FD_HASH_BOTH_HASHES, features );
    1334           0 :         fd_lthash_add( &acc_lthash, &new_lthash_value );
    1335             : 
    1336           0 :         if (fd_account_meta_exists( metadata ) && memcmp( metadata->hash, &hash, 32 ) != 0 ) {
    1337           0 :           FD_LOG_WARNING(( "snapshot hash (%s) doesn't match calculated hash (%s)", FD_BASE58_ENC_32_ALLOCA( metadata->hash ), FD_BASE58_ENC_32_ALLOCA( &hash ) ));
    1338           0 :         }
    1339           0 :       }
    1340           0 :     }
    1341           0 :   }
    1342             : 
    1343             :   // Compare the accumulator to the slot
    1344           0 :   fd_lthash_value_t * acc = (fd_lthash_value_t *)fd_type_pun_const( NULL );
    1345           0 :   if ( memcmp( acc, &acc_lthash, sizeof( fd_lthash_value_t ) ) == 0 ) {
    1346           0 :     FD_LOG_NOTICE(("accounts_lthash %s == %s", FD_LTHASH_ENC_32_ALLOCA (acc), FD_LTHASH_ENC_32_ALLOCA (&acc_lthash)));
    1347           0 :   } else {
    1348           0 :     FD_LOG_ERR(("accounts_lthash %s != %s", FD_LTHASH_ENC_32_ALLOCA (acc), FD_LTHASH_ENC_32_ALLOCA (&acc_lthash)));
    1349           0 :   }
    1350             : 
    1351           0 :   fd_funk_txn_end_read( funk );
    1352           0 : }

Generated by: LCOV version 1.14