LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_hashes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 733 0.0 %
Date: 2025-01-08 12:08:44 Functions: 0 21 0.0 %

          Line data    Source code
       1             : #include "fd_hashes.h"
       2             : #include "fd_acc_mgr.h"
       3             : #include "fd_runtime.h"
       4             : #include "fd_account.h"
       5             : #include "context/fd_capture_ctx.h"
       6             : #include "sysvar/fd_sysvar_epoch_schedule.h"
       7             : #include "../capture/fd_solcap_writer.h"
       8             : #include "../../ballet/base58/fd_base58.h"
       9             : #include "../../ballet/blake3/fd_blake3.h"
      10             : #include "../../ballet/lthash/fd_lthash.h"
      11             : #include "../../ballet/sha256/fd_sha256.h"
      12             : 
      13             : #include <assert.h>
      14             : #include <stdio.h>
      15             : 
      16             : #define SORT_NAME sort_pubkey_hash_pair
      17           0 : #define SORT_KEY_T fd_pubkey_hash_pair_t
      18             : static int
      19           0 : fd_pubkey_hash_pair_compare(fd_pubkey_hash_pair_t const * a, fd_pubkey_hash_pair_t const * b) {
      20           0 :   for (uint i = 0; i < sizeof(fd_pubkey_t)/sizeof(ulong); ++i) {
      21             :     /* First byte is least significant when seen as a long. Make it most significant. */
      22           0 :     ulong al = __builtin_bswap64(a->rec->pair.key->ul[i]);
      23           0 :     ulong bl = __builtin_bswap64(b->rec->pair.key->ul[i]);
      24           0 :     if (al != bl) return (al < bl);
      25           0 :   }
      26           0 :   return 0;
      27           0 : }
      28           0 : #define SORT_BEFORE(a,b) fd_pubkey_hash_pair_compare(&a, &b)
      29             : #include "../../util/tmpl/fd_sort.c"
      30             : 
      31           0 : #define FD_ACCOUNT_DELTAS_MERKLE_FANOUT (16UL)
      32           0 : #define FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT (16UL)
      33             : 
      34             : struct fd_pubkey_hash_pair_list {
      35             :   fd_pubkey_hash_pair_t * pairs;
      36             :   ulong pairs_len;
      37             : };
      38             : typedef struct fd_pubkey_hash_pair_list fd_pubkey_hash_pair_list_t;
      39             : 
      40             : static void
      41           0 : fd_hash_account_deltas( fd_pubkey_hash_pair_list_t * lists, ulong lists_len, fd_hash_t * hash ) {
      42           0 :   fd_sha256_t shas[FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT];
      43           0 :   uchar       num_hashes[FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT+1];
      44             : 
      45             :   // Init the number of hashes
      46           0 :   fd_memset( num_hashes, 0, sizeof(num_hashes) );
      47             : 
      48             :   // FD_LOG_DEBUG(("sorting %d", pairs_len));
      49             :   // long timer_sort = -fd_log_wallclock();
      50             :   // sort_pubkey_hash_pair_inplace( pairs, pairs_len );
      51             :   // timer_sort += fd_log_wallclock();
      52             :   // FD_LOG_DEBUG(("sorting done %6.3f ms", (double)timer_sort*(1e-6)));
      53             : 
      54             :   // FD_LOG_DEBUG(("fancy bmtree started"));
      55           0 :   for( ulong j = 0; j < FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT; ++j ) {
      56           0 :     fd_sha256_init( &shas[j] );
      57           0 : }
      58             : 
      59           0 :   if( lists_len == 0 ) {
      60           0 :     fd_sha256_fini( &shas[0], hash->hash );
      61           0 :     return;
      62           0 :   }
      63             : 
      64           0 :   fd_pubkey_hash_pair_t * prev_pair = NULL;
      65           0 :   for( ulong k = 0; k < lists_len; ++k ) {
      66           0 :     fd_pubkey_hash_pair_t * pairs = lists[k].pairs;
      67           0 :     ulong pairs_len               = lists[k].pairs_len;
      68           0 :     for( ulong i = 0; i < pairs_len; ++i ) {
      69             : #ifdef VLOG
      70             :       FD_LOG_NOTICE(( "account delta hash X { \"key\":%ld, \"pubkey\":\"%s\", \"hash\":\"%s\" },",
      71             :                       i,
      72             :                       FD_BASE58_ENC_32_ALLOCA( pairs[i].pubkey->key ),
      73             :                       FD_BASE58_ENC_32_ALLOCA( pairs[i].hash->hash ) ));
      74             : #endif
      75             : 
      76           0 :       if( prev_pair ) FD_TEST(fd_pubkey_hash_pair_compare(prev_pair, &pairs[i]) > 0);
      77           0 :       prev_pair = &pairs[i];
      78           0 :       fd_sha256_append( &shas[0], pairs[i].hash->hash, sizeof( fd_hash_t ) );
      79           0 :       num_hashes[0]++;
      80             : 
      81           0 :       for( ulong j = 0; j < FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT; ++j ) {
      82           0 :         if (num_hashes[j] == FD_ACCOUNT_DELTAS_MERKLE_FANOUT) {
      83           0 :           num_hashes[j] = 0;
      84           0 :           num_hashes[j+1]++;
      85           0 :           fd_sha256_fini( &shas[j], hash->hash );
      86           0 :           fd_sha256_init( &shas[j] );
      87           0 :           fd_sha256_append( &shas[j+1], (uchar const *) hash->hash, sizeof( fd_hash_t ) );
      88           0 :         } else {
      89           0 :           break;
      90           0 :         }
      91           0 :       }
      92           0 :     }
      93           0 :   }
      94             : 
      95           0 :   ulong tot_num_hashes = 0;
      96           0 :   for (ulong k = 0; k < FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT; ++k ) {
      97           0 :     tot_num_hashes += num_hashes[k];
      98           0 :   }
      99             : 
     100           0 :   if (tot_num_hashes == 1) {
     101           0 :     return;
     102           0 :   }
     103             : 
     104             :   // TODO: use CZT on pairs_len
     105           0 :   ulong height = 0;
     106           0 :   for( long i = FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT-1; i >= 0; --i ) {
     107           0 :     if( num_hashes[i] != 0 ) {
     108           0 :       height = (ulong) i + 1;
     109           0 :       break;
     110           0 :     }
     111           0 :   }
     112             : 
     113             : 
     114           0 :   for( ulong i = 0; i < height; ++i ) {
     115           0 :     if( num_hashes[i]==0 ) {
     116           0 :       continue;
     117           0 :     }
     118             :     // At level i, finalize and append to i + 1
     119             :     //fd_hash_t sub_hash;
     120           0 :     fd_sha256_fini( &shas[i], hash );
     121           0 :     num_hashes[i] = 0;
     122           0 :     num_hashes[i+1]++;
     123             : 
     124           0 :     ulong tot_num_hashes = 0;
     125           0 :     for (ulong k = 0; k < FD_ACCOUNT_DELTAS_MAX_MERKLE_HEIGHT; ++k ) {
     126           0 :       tot_num_hashes += num_hashes[k];
     127           0 :     }
     128           0 :     if (i == (height-1)) {
     129           0 :       assert(tot_num_hashes == 1);
     130           0 :       return;
     131           0 :     }
     132           0 :     fd_sha256_append( &shas[i+1], (uchar const *) hash->hash, sizeof( fd_hash_t ) );
     133             : 
     134             :     // There is now one more hash at level i+1
     135             : 
     136             :     // check, have we filled this level and ones above it.
     137           0 :     for( ulong j = i+1; j < height; ++j ) {
     138             :       // if the level is full, finalize and push into next level.
     139           0 :       if (num_hashes[j] == FD_ACCOUNT_DELTAS_MERKLE_FANOUT) {
     140           0 :         num_hashes[j] = 0;
     141           0 :         num_hashes[j+1]++;
     142           0 :         fd_hash_t sub_hash;
     143           0 :         fd_sha256_fini( &shas[j], &sub_hash );
     144           0 :         if (j != height - 1) {
     145           0 :           fd_sha256_append( &shas[j+1], (uchar const *) sub_hash.hash, sizeof( fd_hash_t ) );
     146           0 :         } else {
     147           0 :           memcpy(hash->hash, sub_hash.hash, sizeof(fd_hash_t));
     148           0 :           return;
     149           0 :         }
     150           0 :       }
     151           0 :     }
     152           0 :   }
     153             : 
     154             :   // If the level at the `height' was rolled into, do something about it
     155           0 : }
     156             : 
     157             : 
     158             : void
     159           0 : fd_calculate_epoch_accounts_hash_values(fd_exec_slot_ctx_t * slot_ctx) {
     160             : 
     161           0 :   ulong slot_idx = 0;
     162           0 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     163           0 :   ulong epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, slot_ctx->slot_bank.slot, &slot_idx );
     164             : 
     165           0 :   ulong slots_per_epoch = fd_epoch_slot_cnt( &epoch_bank->epoch_schedule, epoch );
     166           0 :   ulong first_slot_in_epoch           = fd_epoch_slot0   ( &epoch_bank->epoch_schedule, epoch );
     167             : 
     168           0 :   ulong calculation_offset_start = slots_per_epoch / 4;
     169           0 :   ulong calculation_offset_stop = slots_per_epoch / 4 * 3;
     170           0 :   ulong calculation_interval = fd_ulong_sat_sub(calculation_offset_stop, calculation_offset_start);
     171             : 
     172             :   // This came from the vote program.. maybe we need to put it into a header?
     173           0 :   const ulong MAX_LOCKOUT_HISTORY = 31UL;
     174           0 :   const ulong CALCULATION_INTERVAL_BUFFER = 150UL;
     175           0 :   const ulong MINIMUM_CALCULATION_INTERVAL = MAX_LOCKOUT_HISTORY + CALCULATION_INTERVAL_BUFFER;
     176             : 
     177           0 :   if (calculation_interval < MINIMUM_CALCULATION_INTERVAL) {
     178           0 :     epoch_bank->eah_start_slot = ULONG_MAX;
     179           0 :     epoch_bank->eah_stop_slot = ULONG_MAX;
     180           0 :     epoch_bank->eah_interval = ULONG_MAX;
     181           0 :     return;
     182           0 :   }
     183             : 
     184           0 :   epoch_bank->eah_start_slot = first_slot_in_epoch + calculation_offset_start;
     185           0 :   if (slot_ctx->slot_bank.slot > epoch_bank->eah_start_slot)
     186           0 :     epoch_bank->eah_start_slot = ULONG_MAX;
     187           0 :   epoch_bank->eah_stop_slot = first_slot_in_epoch + calculation_offset_stop;
     188           0 :   if (slot_ctx->slot_bank.slot > epoch_bank->eah_stop_slot)
     189           0 :     epoch_bank->eah_stop_slot = ULONG_MAX;
     190           0 :   epoch_bank->eah_interval = calculation_interval;
     191           0 : }
     192             : 
     193             : // https://github.com/solana-labs/solana/blob/b0dcaf29e358c37a0fcb8f1285ce5fff43c8ec55/runtime/src/bank/epoch_accounts_hash_utils.rs#L13
     194             : static int
     195           0 : fd_should_include_epoch_accounts_hash(fd_exec_slot_ctx_t * slot_ctx) {
     196             : 
     197           0 :   fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     198           0 :   ulong calculation_stop = epoch_bank->eah_stop_slot;
     199           0 :   return slot_ctx->slot_bank.prev_slot < calculation_stop && (slot_ctx->slot_bank.slot >= calculation_stop);
     200           0 : }
     201             : 
     202             : static int
     203           0 : fd_should_snapshot_include_epoch_accounts_hash(fd_exec_slot_ctx_t * slot_ctx) {
     204             : 
     205           0 :   fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     206             : 
     207             :   // We need to find the correct logic
     208           0 :   if (epoch_bank->eah_start_slot != ULONG_MAX)
     209           0 :     return 0;
     210           0 :   if (epoch_bank->eah_stop_slot == ULONG_MAX)
     211           0 :     return 0;
     212           0 :   return 1;
     213           0 : }
     214             : 
     215             : // slot_ctx should be const.
     216             : static void
     217             : fd_hash_bank( fd_exec_slot_ctx_t * slot_ctx,
     218             :               fd_capture_ctx_t * capture_ctx,
     219             :               fd_hash_t * hash,
     220             :               fd_pubkey_hash_pair_t * dirty_keys,
     221           0 :               ulong dirty_key_cnt ) {
     222           0 :   slot_ctx->slot_bank.prev_banks_hash = slot_ctx->slot_bank.banks_hash;
     223           0 :   slot_ctx->slot_bank.parent_signature_cnt = slot_ctx->signature_cnt;
     224           0 :   slot_ctx->prev_lamports_per_signature = slot_ctx->slot_bank.lamports_per_signature;
     225           0 :   slot_ctx->parent_transaction_count = slot_ctx->slot_bank.transaction_count;
     226             : 
     227           0 :   sort_pubkey_hash_pair_inplace( dirty_keys, dirty_key_cnt );
     228           0 :   fd_pubkey_hash_pair_list_t list1 = { .pairs = dirty_keys, .pairs_len = dirty_key_cnt };
     229             : 
     230           0 :   fd_hash_account_deltas(&list1, 1, &slot_ctx->account_delta_hash );
     231             : 
     232           0 :   fd_sha256_t sha;
     233           0 :   fd_sha256_init( &sha );
     234           0 :   fd_sha256_append( &sha, (uchar const *) &slot_ctx->slot_bank.banks_hash, sizeof( fd_hash_t ) );
     235           0 :   fd_sha256_append( &sha, (uchar const *) &slot_ctx->account_delta_hash, sizeof( fd_hash_t  ) );
     236           0 :   fd_sha256_append( &sha, (uchar const *) &slot_ctx->signature_cnt, sizeof( ulong ) );
     237           0 :   fd_sha256_append( &sha, (uchar const *) &slot_ctx->slot_bank.poh, sizeof( fd_hash_t ) );
     238             : 
     239           0 :   fd_sha256_fini( &sha, hash->hash );
     240             : 
     241           0 :   if (fd_should_include_epoch_accounts_hash(slot_ctx)) {
     242           0 :     fd_sha256_init( &sha );
     243           0 :     fd_sha256_append( &sha, (uchar const *) &hash->hash, sizeof( fd_hash_t ) );
     244             : 
     245           0 :     fd_sha256_append( &sha, (uchar const *) &slot_ctx->slot_bank.epoch_account_hash.hash, sizeof( fd_hash_t ) );
     246             : 
     247           0 :     fd_sha256_fini( &sha, hash->hash );
     248           0 :   }
     249             : 
     250           0 :   if( capture_ctx != NULL && capture_ctx->capture != NULL ) {
     251           0 :     fd_solcap_write_bank_preimage(
     252           0 :         capture_ctx->capture,
     253           0 :         hash->hash,
     254           0 :         slot_ctx->slot_bank.prev_banks_hash.hash,
     255           0 :         slot_ctx->account_delta_hash.hash,
     256           0 :         &slot_ctx->slot_bank.poh.hash,
     257           0 :         slot_ctx->signature_cnt );
     258           0 :   }
     259             : 
     260           0 :   FD_LOG_NOTICE(( "\n\n[Replay]\n"
     261           0 :                   "slot:             %lu\n"
     262           0 :                   "bank hash:        %s\n"
     263           0 :                   "parent bank hash: %s\n"
     264           0 :                   "accounts_delta:   %s\n"
     265           0 :                   "lthash:           %s\n"
     266           0 :                   "signature_count:  %lu\n"
     267           0 :                   "last_blockhash:   %s\n",
     268           0 :                   slot_ctx->slot_bank.slot,
     269           0 :                   FD_BASE58_ENC_32_ALLOCA( hash->hash ),
     270           0 :                   FD_BASE58_ENC_32_ALLOCA( slot_ctx->slot_bank.prev_banks_hash.hash ),
     271           0 :                   FD_BASE58_ENC_32_ALLOCA( slot_ctx->account_delta_hash.hash ),
     272           0 :                   FD_LTHASH_ENC_32_ALLOCA( (fd_lthash_value_t *) slot_ctx->slot_bank.lthash.lthash ),
     273           0 :                   slot_ctx->signature_cnt,
     274           0 :                   FD_BASE58_ENC_32_ALLOCA( slot_ctx->slot_bank.poh.hash ) ));
     275           0 : }
     276             : 
     277             : struct fd_accounts_hash_task_info {
     278             :   fd_exec_slot_ctx_t * slot_ctx;
     279             :   fd_pubkey_t acc_pubkey[1];
     280             :   fd_hash_t acc_hash[1];
     281             :   fd_funk_rec_t const * rec;
     282             :   uint should_erase;
     283             :   uint hash_changed;
     284             : };
     285             : typedef struct fd_accounts_hash_task_info fd_accounts_hash_task_info_t;
     286             : 
     287             : struct fd_accounts_hash_task_data {
     288             :   struct fd_accounts_hash_task_info *info;
     289             :   ulong                              info_sz;
     290             :   fd_lthash_value_t                 *lthash_values;
     291             : };
     292             : typedef struct fd_accounts_hash_task_data fd_accounts_hash_task_data_t;
     293             : 
     294             : static void
     295             : fd_account_hash_task( void *tpool,
     296             :                       ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
     297             :                       void *args FD_PARAM_UNUSED,
     298             :                       void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
     299             :                       ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
     300             :                       ulong m0, ulong m1 FD_PARAM_UNUSED,
     301           0 :                       ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED) {
     302           0 :   fd_accounts_hash_task_info_t * task_info = ((fd_accounts_hash_task_data_t *)tpool)->info + m0;
     303           0 :   fd_exec_slot_ctx_t * slot_ctx = task_info->slot_ctx;
     304           0 :   int err = 0;
     305           0 :   fd_funk_txn_t const * txn_out = NULL;
     306           0 :   fd_account_meta_t const * acc_meta = fd_acc_mgr_view_raw( slot_ctx->acc_mgr, slot_ctx->funk_txn, task_info->acc_pubkey, &task_info->rec, &err, &txn_out );
     307           0 :   if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS || !acc_meta ) ) {
     308           0 :     FD_LOG_WARNING(( "failed to view account during bank hash" ));
     309           0 :     return;
     310           0 :   }
     311             : 
     312           0 :   fd_account_meta_t * acc_meta_parent = NULL;
     313           0 :   if( NULL != txn_out ) {
     314           0 :     fd_funk_t *     funk = slot_ctx->acc_mgr->funk;
     315           0 :     fd_wksp_t *     wksp = fd_funk_wksp( funk );
     316           0 :     fd_funk_txn_t * txn_map  = fd_funk_txn_map( funk, wksp );
     317           0 :     txn_out = fd_funk_txn_parent( (fd_funk_txn_t *) txn_out, txn_map );
     318           0 :     acc_meta_parent = (fd_account_meta_t *)fd_acc_mgr_view_raw( slot_ctx->acc_mgr, txn_out, task_info->acc_pubkey, NULL, &err, NULL);
     319           0 :   }
     320             : 
     321           0 :   fd_lthash_value_t * acc = &(((fd_accounts_hash_task_data_t *)tpool)->lthash_values[n0]);
     322             : 
     323           0 :   if( FD_UNLIKELY(acc_meta->info.lamports == 0) ) {
     324           0 :     fd_memset( task_info->acc_hash->hash, 0, FD_HASH_FOOTPRINT );
     325             : 
     326             :     /* If we erase records instantly, this causes problems with the
     327             :         iterator.  Instead, we will store away the record and erase
     328             :         it later where appropriate.  */
     329           0 :     task_info->should_erase = 1;
     330             :     /* In the exceedingly unlikely event that the account's old hash is
     331             :        actually 0, this would cause the account not to be included in
     332             :        the bank hash. */
     333           0 :     if( memcmp( task_info->acc_hash->hash, acc_meta->hash, sizeof(fd_hash_t) ) != 0 ) {
     334           0 :       task_info->hash_changed = 1;
     335           0 :     }
     336           0 :   } else {
     337           0 :     uchar *             acc_data = fd_account_get_data((fd_account_meta_t *) acc_meta);
     338           0 :     fd_pubkey_t const * acc_key  = fd_funk_key_to_acc( task_info->rec->pair.key );
     339           0 :     fd_lthash_value_t new_lthash_value;
     340           0 :     fd_lthash_zero(&new_lthash_value);
     341           0 :     fd_hash_account_current( task_info->acc_hash->hash, &new_lthash_value, acc_meta, acc_key->key, acc_data );
     342             : 
     343           0 :     if( memcmp( task_info->acc_hash->hash, acc_meta->hash, sizeof(fd_hash_t) ) != 0 ) {
     344           0 :       task_info->hash_changed = 1;
     345           0 :       fd_lthash_add( acc, &new_lthash_value);
     346           0 :     }
     347           0 :   }
     348             : 
     349           0 :   if( FD_LIKELY(task_info->hash_changed && ((NULL != acc_meta_parent) && (acc_meta_parent->info.lamports != 0) ) ) ) {
     350           0 :     uchar *             acc_data = fd_account_get_data(acc_meta_parent);
     351           0 :     fd_pubkey_t const * acc_key  = fd_funk_key_to_acc( task_info->rec->pair.key );
     352           0 :     fd_lthash_value_t old_lthash_value;
     353           0 :     fd_lthash_zero(&old_lthash_value);
     354           0 :     fd_hash_t old_hash;
     355             : 
     356           0 :     fd_hash_account_current( old_hash.hash, &old_lthash_value, acc_meta_parent, acc_key->key, acc_data );
     357           0 :     fd_lthash_sub( acc, &old_lthash_value );
     358           0 :   }
     359             : 
     360           0 :   if( acc_meta->slot == slot_ctx->slot_bank.slot ) {
     361           0 :       task_info->hash_changed = 1;
     362           0 :   }
     363           0 : }
     364             : 
     365             : void
     366             : fd_collect_modified_accounts( fd_exec_slot_ctx_t * slot_ctx,
     367           0 :                               fd_accounts_hash_task_data_t *task_data ) {
     368           0 :   fd_acc_mgr_t *  acc_mgr = slot_ctx->acc_mgr;
     369           0 :   fd_funk_t *     funk    = acc_mgr->funk;
     370           0 :   fd_funk_txn_t * txn     = slot_ctx->funk_txn;
     371             : 
     372           0 :   ulong rec_cnt = 0;
     373           0 :   for( fd_funk_rec_t const * rec = fd_funk_txn_first_rec( funk, txn );
     374           0 :        NULL != rec;
     375           0 :        rec = fd_funk_txn_next_rec( funk, rec ) ) {
     376             : 
     377           0 :     if( !fd_funk_key_is_acc( rec->pair.key  ) )
     378           0 :       continue;
     379             : 
     380           0 :     fd_pubkey_t const * pubkey  = fd_funk_key_to_acc( rec->pair.key );
     381             : 
     382           0 :     if (((pubkey->ul[0] == 0) & (pubkey->ul[1] == 0) & (pubkey->ul[2] == 0) & (pubkey->ul[3] == 0)))
     383           0 :       FD_LOG_WARNING(( "null pubkey (system program?) showed up as modified" ));
     384             : 
     385           0 :     rec_cnt++;
     386           0 :   }
     387             : 
     388           0 :   task_data->info = fd_valloc_malloc( slot_ctx->valloc, 8UL, rec_cnt * sizeof(fd_accounts_hash_task_info_t) );
     389             : 
     390             :   /* Iterate over accounts that have been changed in the current
     391             :      database transaction. */
     392           0 :   ulong task_info_idx = 0;
     393           0 :   for( fd_funk_rec_t const * rec = fd_funk_txn_first_rec( funk, txn );
     394           0 :        NULL != rec;
     395           0 :        rec = fd_funk_txn_next_rec( funk, rec ) ) {
     396             : 
     397           0 :     fd_pubkey_t const * acc_key  = fd_funk_key_to_acc( rec->pair.key );
     398             : 
     399           0 :     if( !fd_funk_key_is_acc( rec->pair.key  ) )
     400           0 :       continue;
     401             : 
     402           0 :     fd_accounts_hash_task_info_t * task_info = &task_data->info[task_info_idx++];
     403             : 
     404           0 :     *task_info->acc_pubkey = *acc_key;
     405           0 :     task_info->slot_ctx = slot_ctx;
     406           0 :     task_info->hash_changed = 0;
     407           0 :     task_info->should_erase = 0;
     408           0 :   }
     409             : 
     410           0 :   task_data->info_sz = task_info_idx;
     411           0 : }
     412             : 
     413             : int
     414             : fd_update_hash_bank_tpool( fd_exec_slot_ctx_t * slot_ctx,
     415             :                            fd_capture_ctx_t *   capture_ctx,
     416             :                            fd_hash_t *          hash,
     417             :                            ulong                signature_cnt,
     418           0 :                            fd_tpool_t *         tpool ) {
     419           0 :   fd_acc_mgr_t *  acc_mgr = slot_ctx->acc_mgr;
     420           0 :   fd_funk_t *     funk    = acc_mgr->funk;
     421           0 :   fd_funk_txn_t * txn     = slot_ctx->funk_txn;
     422             : 
     423             :   /* Collect list of changed accounts to be added to bank hash */
     424           0 :   fd_accounts_hash_task_data_t task_data;
     425             : 
     426           0 :   ulong wcnt = fd_tpool_worker_cnt( tpool );
     427           0 :   task_data.lthash_values = fd_valloc_malloc( slot_ctx->valloc, FD_LTHASH_VALUE_ALIGN, wcnt * FD_LTHASH_VALUE_FOOTPRINT );
     428           0 :   for( ulong i = 0; i < wcnt; i++ ) {
     429           0 :     fd_lthash_zero(&task_data.lthash_values[i]);
     430           0 :   }
     431             : 
     432             :   /* Find accounts which might have changed */
     433           0 :   fd_collect_modified_accounts( slot_ctx, &task_data);
     434             : 
     435           0 :   fd_pubkey_hash_pair_t * dirty_keys = fd_valloc_malloc( slot_ctx->valloc, FD_PUBKEY_HASH_PAIR_ALIGN, task_data.info_sz * FD_PUBKEY_HASH_PAIR_FOOTPRINT );
     436           0 :   ulong dirty_key_cnt = 0;
     437             : 
     438             :   /* Find accounts which have changed */
     439           0 :   fd_tpool_exec_all_rrobin( tpool, 0, wcnt, fd_account_hash_task, &task_data, NULL, NULL, 1, 0, task_data.info_sz );
     440             : 
     441             :   // Apply the lthash changes to the bank lthash
     442           0 :   fd_lthash_value_t * acc = (fd_lthash_value_t *)fd_type_pun(slot_ctx->slot_bank.lthash.lthash);
     443           0 :   for( ulong i = 0; i < wcnt; i++ ) {
     444           0 :     fd_lthash_add( acc, &task_data.lthash_values[i] );
     445           0 :   }
     446             : 
     447           0 :   for( ulong i = 0; i < task_data.info_sz; i++ ) {
     448           0 :     fd_accounts_hash_task_info_t * task_info = &task_data.info[i];
     449             :     /* Upgrade to writable record */
     450           0 :     if( !task_info->hash_changed ) {
     451           0 :       continue;
     452           0 :     }
     453             : 
     454           0 :     FD_BORROWED_ACCOUNT_DECL(acc_rec);
     455           0 :     acc_rec->const_rec = task_info->rec;
     456             : 
     457           0 :     fd_pubkey_t const * acc_key = fd_funk_key_to_acc( task_info->rec->pair.key );
     458           0 :     int err = fd_acc_mgr_modify( acc_mgr, txn, acc_key, 0, 0UL, acc_rec);
     459           0 :     if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     460           0 :       FD_LOG_ERR(( "failed to modify account during bank hash" ));
     461           0 :     }
     462             : 
     463             :     /* Update hash */
     464             : 
     465           0 :     memcpy( acc_rec->meta->hash, task_info->acc_hash->hash, sizeof(fd_hash_t) );
     466           0 :     acc_rec->meta->slot = slot_ctx->slot_bank.slot;
     467             : 
     468             :     /* Add account to "dirty keys" list, which will be added to the
     469             :        bank hash. */
     470             : 
     471           0 :     fd_pubkey_hash_pair_t * dirty_entry = &dirty_keys[dirty_key_cnt++];
     472           0 :     dirty_entry->rec = task_info->rec;
     473           0 :     dirty_entry->hash = (fd_hash_t const *)acc_rec->meta->hash;
     474             : 
     475           0 :     char acc_key_string[ FD_BASE58_ENCODED_32_SZ ];
     476           0 :     fd_acct_addr_cstr( acc_key_string, (uchar const*)acc_key );
     477           0 :     char owner_string[ FD_BASE58_ENCODED_32_SZ ];
     478           0 :     fd_acct_addr_cstr( owner_string, acc_rec->meta->info.owner );
     479             : 
     480           0 :     FD_LOG_DEBUG(( "fd_acc_mgr_update_hash: %s "
     481           0 :         "slot: %lu "
     482           0 :         "lamports: %lu  "
     483           0 :         "owner: %s "
     484           0 :         "executable: %s,  "
     485           0 :         "rent_epoch: %lu, "
     486           0 :         "data_len: %lu",
     487           0 :         acc_key_string,
     488           0 :         slot_ctx->slot_bank.slot,
     489           0 :         acc_rec->meta->info.lamports,
     490           0 :         owner_string,
     491           0 :         acc_rec->meta->info.executable ? "true" : "false",
     492           0 :         acc_rec->meta->info.rent_epoch,
     493           0 :         acc_rec->meta->dlen ));
     494             : 
     495           0 :     if( capture_ctx != NULL && capture_ctx->capture != NULL ) {
     496           0 :       fd_account_meta_t const * acc_meta = fd_acc_mgr_view_raw( slot_ctx->acc_mgr, slot_ctx->funk_txn, task_info->acc_pubkey, &task_info->rec, &err, NULL);
     497           0 :       if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     498           0 :         FD_LOG_WARNING(( "failed to view account during capture" ));
     499           0 :         continue;
     500           0 :       }
     501             : 
     502           0 :       uchar const *       acc_data = (uchar *)acc_meta + acc_meta->hlen;
     503             : 
     504           0 :       err = fd_solcap_write_account(
     505           0 :         capture_ctx->capture,
     506           0 :         acc_key->uc,
     507           0 :         &acc_rec->meta->info,
     508           0 :         acc_data,
     509           0 :         acc_rec->meta->dlen,
     510           0 :         task_info->acc_hash->hash );
     511           0 :       FD_TEST( err==0 );
     512           0 :     }
     513           0 :   }
     514             : 
     515             :   /* Sort and hash "dirty keys" to the accounts delta hash. */
     516             : 
     517             :   // FD_LOG_DEBUG(("slot %ld, dirty %ld", slot_ctx->slot_bank.slot, dirty_key_cnt));
     518             : 
     519           0 :   slot_ctx->signature_cnt = signature_cnt;
     520           0 :   fd_hash_bank( slot_ctx, capture_ctx, hash, dirty_keys, dirty_key_cnt);
     521             : 
     522           0 :   for( ulong i = 0; i < task_data.info_sz; i++ ) {
     523           0 :     fd_accounts_hash_task_info_t * task_info = &task_data.info[i];
     524             :     /* Upgrade to writable record */
     525           0 :     if( FD_LIKELY( !task_info->should_erase ) ) {
     526           0 :       continue;
     527           0 :     }
     528             : 
     529             :     /* All removed recs should be stored with the slot from the funk txn. */
     530           0 :     fd_funk_rec_remove( funk, fd_funk_rec_modify(funk, task_info->rec), task_info->rec->pair.xid->ul[0] );
     531           0 :   }
     532             : 
     533             :   // Sanity-check LT Hash
     534             :   //    fd_accounts_check_lthash( slot_ctx );
     535             : 
     536           0 :   fd_valloc_free( slot_ctx->valloc, task_data.info );
     537           0 :   fd_valloc_free( slot_ctx->valloc, task_data.lthash_values );
     538           0 :   fd_valloc_free( slot_ctx->valloc, dirty_keys );
     539             : 
     540           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     541           0 : }
     542             : 
     543             : int
     544             : fd_print_account_hashes( fd_exec_slot_ctx_t * slot_ctx,
     545           0 :                          fd_tpool_t *         tpool ) {
     546             : 
     547             :   // fd_acc_mgr_t *  acc_mgr = slot_ctx->acc_mgr;
     548             :   // fd_funk_txn_t * txn     = slot_ctx->funk_txn;
     549             : 
     550             :   /* Collect list of changed accounts to be added to bank hash */
     551           0 :   fd_accounts_hash_task_data_t task_data;
     552             : 
     553           0 :   fd_collect_modified_accounts( slot_ctx, &task_data );
     554             : 
     555           0 :   fd_pubkey_hash_pair_t * dirty_keys = fd_valloc_malloc( slot_ctx->valloc, FD_PUBKEY_HASH_PAIR_ALIGN, task_data.info_sz * FD_PUBKEY_HASH_PAIR_FOOTPRINT );
     556           0 :   ulong dirty_key_cnt = 0;
     557             : 
     558           0 :   ulong wcnt = fd_tpool_worker_cnt( tpool );
     559           0 :   task_data.lthash_values = fd_valloc_malloc( slot_ctx->valloc, FD_LTHASH_VALUE_ALIGN, wcnt * FD_LTHASH_VALUE_FOOTPRINT );
     560           0 :   for( ulong i = 0; i < wcnt; i++ ) {
     561           0 :     fd_lthash_zero(&task_data.lthash_values[i]);
     562           0 :   }
     563             : 
     564             :   /* Find accounts which have changed */
     565           0 :   fd_tpool_exec_all_rrobin( tpool, 0, fd_tpool_worker_cnt( tpool ), fd_account_hash_task, task_data.info, NULL, NULL, 1, 0, task_data.info_sz );
     566             : 
     567           0 :   for( ulong i = 0; i < task_data.info_sz; i++ ) {
     568           0 :     fd_accounts_hash_task_info_t * task_info = &task_data.info[i];
     569             :     /* Upgrade to writable record */
     570           0 :     if( !task_info->hash_changed ) {
     571           0 :       continue;
     572           0 :     }
     573             : 
     574           0 :     FD_BORROWED_ACCOUNT_DECL(acc_rec);
     575           0 :     acc_rec->const_rec = task_info->rec;
     576             : 
     577             :     // int err = fd_acc_mgr_modify( acc_mgr, txn, acc_key, 0, 0UL, acc_rec);
     578             :     // if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     579             :     //   FD_LOG_ERR(( "failed to modify account during bank hash" ));
     580             :     // }
     581             : 
     582             :     /* Update hash */
     583             : 
     584             :     // memcpy( acc_rec->meta->hash, task_info->acc_hash->hash, sizeof(fd_hash_t) );
     585             :     // acc_rec->meta->slot = slot_ctx->slot_bank.slot;
     586             : 
     587             :     /* Add account to "dirty keys" list, which will be added to the
     588             :        bank hash. */
     589             : 
     590           0 :     fd_pubkey_hash_pair_t * dirty_entry = &dirty_keys[dirty_key_cnt++];
     591           0 :     dirty_entry->rec = task_info->rec;
     592           0 :     dirty_entry->hash = (fd_hash_t const *)task_info->acc_hash->hash;
     593             : 
     594             :     // FD_TEST( err==0 );
     595           0 :   }
     596             : 
     597             :   /* Sort and hash "dirty keys" to the accounts delta hash. */
     598             : 
     599             : #ifdef VLOG
     600             :   for( ulong i = 0; i < dirty_key_cnt; ++i ) {
     601             :     FD_LOG_NOTICE(( "account delta hash X { \"key\":%ld, \"pubkey\":\"%s\", \"hash\":\"%s\" },",
     602             :                     i,
     603             :                     FD_BASE58_ENC_32_ALLOCA( dirty_keys[i].pubkey->key ),
     604             :                     FD_BASE58_ENC_32_ALLOCA( dirty_keys[i].hash->hash) ));
     605             : 
     606             :     /*
     607             :       pubkey
     608             :       slot
     609             :       lamports
     610             :       owner
     611             :       executable
     612             :       rent_epoch
     613             :       data_len
     614             :       data
     615             :       hash
     616             :     */
     617             :     // fd_pubkey_t current_owner;
     618             :     // fd_acc_mgr_get_owner( global->acc_mgr, global->funk_txn, &pairs[i].pubkey, &current_owner );
     619             :     // char encoded_owner[50];
     620             :     // fd_base58_encode_32((uchar *) &current_owner, 0, encoded_owner);
     621             :     int err = FD_ACC_MGR_SUCCESS;
     622             :     uchar * raw_acc_data = (uchar*) fd_acc_mgr_view_raw(slot_ctx->acc_mgr, slot_ctx->funk_txn, dirty_keys[i].pubkey, NULL, &err, NULL);
     623             :     if (NULL != raw_acc_data) {
     624             : 
     625             :       fd_account_meta_t * metadata = (fd_account_meta_t *)raw_acc_data;
     626             :       uchar *             acc_data = fd_account_get_data(metadata);
     627             :       char *              acc_data_str = fd_valloc_malloc(slot_ctx->valloc, 8, 5*metadata->dlen + 1);
     628             : 
     629             :       char * acc_data_str_cursor = acc_data_str;
     630             :       if (metadata->dlen > 0) {
     631             :         for( ulong j = 0; j < (metadata->dlen - 1); j++ ) {
     632             :           int x = sprintf(acc_data_str_cursor, "%u, ", acc_data[j]);
     633             :           acc_data_str_cursor += x;
     634             :         }
     635             :         sprintf(acc_data_str_cursor, "%u", acc_data[metadata->dlen - 1]);
     636             :       } else {
     637             :         *acc_data_str_cursor = 0;
     638             :       }
     639             : 
     640             :       FD_LOG_NOTICE(( "account_delta_hash_compare pubkey: (%s) slot: (%lu) lamports: (%lu), owner: (%s), executable: (%d), rent_epoch: (%lu), data_len: (%ld), hash: (%s) ",
     641             :                       FD_BASE58_ENC_32_ALLOCA( dirty_keys[i].pubkey->uc ),
     642             :                       slot_ctx->slot_bank.slot,
     643             :                       metadata->info.lamports,
     644             :                       FD_BASE58_ENC_32_ALLOCA( metadata->info.owner ),
     645             :                       metadata->info.executable,
     646             :                       metadata->info.rent_epoch,
     647             :                       metadata->dlen,
     648             :                       FD_BASE58_ENC_32_ALLOCA( dirty_keys[i].hash->hash ) ));
     649             : 
     650             :       fd_valloc_free(slot_ctx->valloc, acc_data_str);
     651             :     }
     652             :   }
     653             : #endif
     654             : 
     655           0 :   fd_valloc_free( slot_ctx->valloc, task_data.info );
     656           0 :   fd_valloc_free( slot_ctx->valloc, task_data.lthash_values );
     657           0 :   fd_valloc_free( slot_ctx->valloc, dirty_keys );
     658             : 
     659           0 :   return 0;
     660           0 : }
     661             : 
     662             : void const *
     663             : fd_hash_account( uchar                     hash[ static 32 ],
     664             :                  fd_lthash_value_t       * lthash,
     665             :                  fd_account_meta_t const * m,
     666             :                  uchar const               pubkey[ static 32 ],
     667           0 :                  uchar const             * data ) {
     668           0 :   ulong         lamports   = m->info.lamports;  /* >0UL */
     669           0 :   ulong         rent_epoch = m->info.rent_epoch;
     670           0 :   uchar         executable = m->info.executable & 0x1;
     671           0 :   uchar const * owner      = (uchar const *)m->info.owner;
     672             : 
     673           0 :   fd_blake3_t b3[1];
     674           0 :   fd_blake3_init  ( b3 );
     675           0 :   fd_blake3_append( b3, &lamports,   sizeof( ulong ) );
     676           0 :   fd_blake3_append( b3, &rent_epoch, sizeof( ulong ) );
     677           0 :   fd_blake3_append( b3, data,        m->dlen         );
     678           0 :   fd_blake3_append( b3, &executable, sizeof( uchar ) );
     679           0 :   fd_blake3_append( b3, owner,       32UL            );
     680           0 :   fd_blake3_append( b3, pubkey,      32UL            );
     681           0 :   if( NULL == lthash ) {
     682           0 :     fd_blake3_fini  ( b3, hash );
     683           0 :   } else {
     684           0 :     fd_blake3_fini_varlen( b3, lthash->bytes, FD_LTHASH_LEN_BYTES );
     685           0 :     fd_memcpy( hash, lthash->bytes, 32);
     686           0 :   }
     687             : 
     688           0 :   return hash;
     689           0 : }
     690             : 
     691             : void const *
     692             : fd_hash_account_current( uchar                      hash  [ static 32 ],
     693             :                          fd_lthash_value_t         *lthash,
     694             :                          fd_account_meta_t const   *account,
     695             :                          uchar const                pubkey[ static 32 ],
     696           0 :                          uchar const               *data ) {
     697           0 :   return fd_hash_account( hash, lthash, account, pubkey, data );
     698           0 : }
     699             : 
     700             : struct accounts_hash {
     701             :   fd_funk_rec_t * key;
     702             :   ulong  hash;
     703             : };
     704             : typedef struct accounts_hash accounts_hash_t;
     705             : 
     706             : #define MAP_NAME accounts_hash
     707           0 : #define MAP_KEY_T fd_funk_rec_t *
     708           0 : #define MAP_HASH_T ulong
     709           0 : #define MAP_KEY_EQUAL(k0,k1) ((NULL != k0) && (NULL != k1) && fd_funk_rec_key_eq( k0->pair.key, k1->pair.key ))
     710           0 : #define MAP_KEY_HASH(p) fd_funk_rec_key_hash( p->pair.key, 2887034UL )
     711             : #define MAP_KEY_EQUAL_IS_SLOW 1
     712           0 : #define MAP_KEY_NULL 0UL
     713           0 : #define MAP_KEY_INVAL(k) (NULL == k)
     714             : 
     715             : // #define MAP_KEY_COPY(kd,ks)   fd_funk_xid_key_pair_copy((kd),(ks))
     716             : 
     717           0 : #define MAP_T    accounts_hash_t
     718             : #include "../../util/tmpl/fd_map_dynamic.c"
     719             : 
     720             : static fd_pubkey_hash_pair_t *
     721             : fd_accounts_sorted_subrange( fd_funk_t         * funk, 
     722             :                              uint                range_idx,
     723             :                              uint                range_cnt, 
     724             :                              ulong             * num_pairs_out, 
     725             :                              fd_lthash_value_t * lthash_values, 
     726             :                              ulong               n0,
     727             :                              fd_valloc_t         valloc
     728           0 :  ) {
     729             : 
     730           0 :   fd_wksp_t *     wksp = fd_funk_wksp( funk );
     731           0 :   fd_funk_rec_t * rec_map  = fd_funk_rec_map( funk, wksp );
     732           0 :   ulong           num_iter_accounts = fd_funk_rec_map_key_max( rec_map );
     733           0 :   ulong           max_pairs = ( range_cnt == 1U ? num_iter_accounts : 2UL*num_iter_accounts/range_cnt ); /* Initial estimate */
     734           0 :   ulong           num_pairs = 0;
     735           0 :   fd_pubkey_hash_pair_t * pairs = fd_valloc_malloc( valloc, FD_PUBKEY_HASH_PAIR_ALIGN, max_pairs * sizeof(fd_pubkey_hash_pair_t) );
     736           0 :   FD_TEST(NULL != pairs);
     737           0 :   ulong           range_len = ULONG_MAX/range_cnt;
     738           0 :   ulong           range_min = range_len*range_idx;
     739           0 :   ulong           range_max = ( range_idx+1U < range_cnt ? range_min+range_len-1U : ULONG_MAX );
     740             : 
     741           0 :   fd_lthash_value_t accum;
     742           0 :   fd_lthash_zero(&accum);
     743             : 
     744           0 :   for( ulong i = num_iter_accounts; i; --i ) {
     745           0 :     fd_funk_rec_t const * rec = rec_map + (i-1UL);
     746           0 :     if ( ( rec->map_next >> 63 ) /* unused map entry */ ||
     747           0 :          !fd_funk_key_is_acc( rec->pair.key ) || /* not a solana record */
     748           0 :          ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) || /* this is a tombstone */
     749           0 :          ( rec->pair.xid->ul[0] | rec->pair.xid->ul[1] ) != 0 /* not root xid */ ) {
     750           0 :       continue;
     751           0 :     }
     752             : 
     753           0 :     ulong n = __builtin_bswap64(rec->pair.key->ul[0]);
     754           0 :     if( n < range_min || n > range_max ) {
     755           0 :       continue;
     756           0 :     }
     757             : 
     758           0 :     fd_account_meta_t * metadata = (fd_account_meta_t *) fd_funk_val_const( rec, wksp );
     759           0 :     int is_empty = (metadata->info.lamports == 0);
     760           0 :     if( is_empty ) {
     761           0 :       continue;
     762           0 :     }
     763             : 
     764           0 :     uchar hash[32];
     765           0 :     fd_lthash_value_t new_lthash_value;
     766           0 :     fd_lthash_zero(&new_lthash_value);
     767             : 
     768           0 :     fd_hash_account_current( (uchar *) hash, &new_lthash_value, metadata, rec->pair.key->uc, fd_account_get_data(metadata) );
     769           0 :     fd_lthash_add( &accum, &new_lthash_value );
     770             : 
     771           0 :     fd_hash_t * h = (fd_hash_t *) metadata->hash;
     772           0 :     if( FD_LIKELY( (h->ul[0] | h->ul[1] | h->ul[2] | h->ul[3]) != 0 ) ) {
     773           0 :       if( FD_UNLIKELY( fd_acc_exists( metadata ) && memcmp( metadata->hash, &hash, 32 ) != 0 ) ) {
     774           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 ) ));
     775           0 :       }
     776           0 :     } else
     777           0 :       fd_memcpy( metadata->hash, &hash, 32 );
     778             : 
     779           0 :     if( (metadata->info.executable & ~1) != 0 )
     780           0 :       continue;
     781             : 
     782           0 :     if( num_pairs == max_pairs ) {
     783             :       /* Try again with a larger array */
     784           0 :       fd_valloc_free( valloc, pairs );
     785           0 :       max_pairs *= 2;
     786           0 :       pairs = fd_valloc_malloc( valloc, FD_PUBKEY_HASH_PAIR_ALIGN, max_pairs * sizeof(fd_pubkey_hash_pair_t) );
     787           0 :       FD_TEST(NULL != pairs);
     788           0 :       num_pairs = 0;
     789           0 :       fd_lthash_zero(&accum);
     790           0 :       i = num_iter_accounts+1;
     791           0 :       continue;
     792           0 :     }
     793             : 
     794           0 :     fd_pubkey_hash_pair_t * pair = &pairs[num_pairs++];
     795           0 :     pair->rec = rec;
     796           0 :     pair->hash = (const fd_hash_t *)metadata->hash;
     797           0 :   }
     798             : 
     799           0 :   sort_pubkey_hash_pair_inplace( pairs, num_pairs );
     800             : 
     801             :   // FD_LOG_NOTICE(( "sorted_subrange %lx ... %lx => %lu pairs", range_min, range_max, num_pairs ));
     802           0 :   *num_pairs_out = num_pairs;
     803             : 
     804           0 :   fd_lthash_add( &lthash_values[n0], &accum  );
     805           0 :   return pairs;
     806           0 : }
     807             : 
     808             : struct fd_subrange_task_info {
     809             :   fd_funk_t * funk;
     810             :   ulong num_lists;
     811             :   fd_pubkey_hash_pair_list_t * lists;
     812             :   fd_lthash_value_t *lthash_values;
     813             :   fd_valloc_t valloc;
     814             : };
     815             : typedef struct fd_subrange_task_info fd_subrange_task_info_t;
     816             : 
     817             : static void
     818             : fd_accounts_sorted_subrange_task( void *tpool,
     819             :                                   ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
     820             :                                   void *args FD_PARAM_UNUSED,
     821             :                                   void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
     822             :                                   ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
     823             :                                   ulong m0, ulong m1 FD_PARAM_UNUSED,
     824           0 :                                   ulong n0, ulong n1 FD_PARAM_UNUSED) {
     825           0 :   fd_subrange_task_info_t * task_info = (fd_subrange_task_info_t *)tpool;
     826           0 :   fd_pubkey_hash_pair_list_t * list = task_info->lists + m0;
     827           0 :   list->pairs = fd_accounts_sorted_subrange( task_info->funk, (uint)m0, (uint)task_info->num_lists, &list->pairs_len, task_info->lthash_values, n0, task_info->valloc );
     828           0 : }
     829             : 
     830             : int
     831             : fd_accounts_hash( fd_funk_t          * funk,
     832             :                   fd_slot_bank_t     * slot_bank,
     833             :                   fd_valloc_t          valloc,
     834             :                   fd_tpool_t         * tpool,
     835           0 :                   fd_hash_t          * accounts_hash ) {
     836           0 :   FD_LOG_NOTICE(("accounts_hash start"));
     837             : 
     838           0 :   if( tpool == NULL || fd_tpool_worker_cnt( tpool ) <= 1U ) {
     839           0 :     ulong                   num_pairs = 0;
     840           0 :     fd_lthash_value_t *lthash_values = fd_valloc_malloc( valloc, FD_LTHASH_VALUE_ALIGN, FD_LTHASH_VALUE_FOOTPRINT );
     841           0 :     fd_lthash_zero(&lthash_values[0]);
     842             : 
     843           0 :     fd_pubkey_hash_pair_t * pairs = fd_accounts_sorted_subrange( funk, 0, 1, &num_pairs, lthash_values, 0, valloc );
     844           0 :     FD_TEST(NULL != pairs);
     845           0 :     fd_pubkey_hash_pair_list_t list1 = { .pairs = pairs, .pairs_len = num_pairs };
     846           0 :     fd_hash_account_deltas( &list1, 1, accounts_hash );
     847           0 :     fd_valloc_free( valloc, pairs );
     848             : 
     849           0 :     fd_lthash_value_t * acc = (fd_lthash_value_t *)fd_type_pun(slot_bank->lthash.lthash);
     850           0 :     fd_lthash_add( acc, &lthash_values[0] );
     851             : 
     852           0 :     fd_valloc_free( valloc, lthash_values );
     853           0 :   } else {
     854           0 :     ulong num_lists = fd_tpool_worker_cnt( tpool );
     855           0 :     FD_LOG_NOTICE(( "launching %lu hash tasks", num_lists ));
     856           0 :     fd_pubkey_hash_pair_list_t lists[num_lists];
     857             : 
     858           0 :     fd_lthash_value_t *lthash_values = fd_valloc_malloc( valloc, FD_LTHASH_VALUE_ALIGN, num_lists * FD_LTHASH_VALUE_FOOTPRINT );
     859           0 :     for( ulong i = 0; i < num_lists; i++ ) {
     860           0 :       fd_lthash_zero(&lthash_values[i]);
     861           0 :     }
     862             : 
     863           0 :     fd_subrange_task_info_t task_info = {
     864           0 :       .funk = funk,
     865           0 :       .num_lists = num_lists,
     866           0 :       .lists = lists,
     867           0 :       .lthash_values = lthash_values,
     868           0 :       .valloc = valloc };
     869           0 :     fd_tpool_exec_all_rrobin( tpool, 0UL, num_lists, fd_accounts_sorted_subrange_task, &task_info, NULL, NULL, 1, 0, num_lists );
     870           0 :     fd_hash_account_deltas( lists, num_lists, accounts_hash );
     871           0 :     for( ulong i = 0; i < num_lists; ++i ) {
     872           0 :       fd_valloc_free( valloc, lists[i].pairs );
     873           0 :     }
     874           0 :     fd_lthash_value_t * acc = (fd_lthash_value_t *)fd_type_pun(slot_bank->lthash.lthash);
     875           0 :     for( ulong i = 0; i < num_lists; i++ ) {
     876           0 :       fd_lthash_add( acc, &lthash_values[i] );
     877           0 :     }
     878             : 
     879           0 :     fd_valloc_free( valloc, lthash_values );
     880           0 :   }
     881           0 :   FD_LOG_NOTICE(("accounts_lthash %s", FD_LTHASH_ENC_32_ALLOCA( (fd_lthash_value_t *) slot_bank->lthash.lthash )));
     882             : 
     883             :   // fd_accounts_check_lthash( slot_ctx );
     884             : 
     885           0 :   FD_LOG_NOTICE(("accounts_hash %s", FD_BASE58_ENC_32_ALLOCA( accounts_hash->hash ) ));
     886             : 
     887           0 :   return 0;
     888           0 : }
     889             : 
     890             : int
     891           0 : fd_accounts_hash_inc_only( fd_exec_slot_ctx_t * slot_ctx, fd_hash_t *accounts_hash, fd_funk_txn_t * child_txn, ulong do_hash_verify ) {
     892           0 :   FD_LOG_NOTICE(("accounts_hash_inc_only start for txn %p, do_hash_verify=%s", (void *)child_txn, do_hash_verify ? "true" : "false" ));
     893             : 
     894           0 :   fd_funk_t *     funk = slot_ctx->acc_mgr->funk;
     895           0 :   fd_wksp_t *     wksp = fd_funk_wksp( funk );
     896           0 :   fd_funk_rec_t * rec_map  = fd_funk_rec_map( funk, wksp );
     897             : 
     898             :   // How many total records are we dealing with?
     899           0 :   ulong                   num_iter_accounts = fd_funk_rec_map_key_cnt( rec_map );
     900           0 :   ulong                   num_pairs = 0;
     901           0 :   fd_pubkey_hash_pair_t * pairs = fd_valloc_malloc( slot_ctx->valloc, FD_PUBKEY_HASH_PAIR_ALIGN, num_iter_accounts * sizeof(fd_pubkey_hash_pair_t) );
     902           0 :   FD_TEST(NULL != pairs);
     903             : 
     904           0 :   fd_blake3_t *b3 = NULL;
     905           0 :   fd_scratch_push();
     906             : 
     907           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)) {
     908           0 :     if ( !fd_funk_key_is_acc( rec->pair.key ) || ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) )
     909           0 :       continue;
     910             : 
     911           0 :     fd_account_meta_t * metadata = (fd_account_meta_t *) fd_funk_val_const( rec, wksp );
     912           0 :     int is_empty = (metadata->info.lamports == 0);
     913             : 
     914           0 :     if (is_empty) {
     915           0 :       pairs[num_pairs].rec = rec;
     916             : 
     917           0 :       fd_hash_t * hash = fd_scratch_alloc(alignof(fd_hash_t), sizeof(fd_hash_t));
     918           0 :       if (NULL == b3)
     919           0 :         b3 = fd_scratch_alloc(alignof(fd_blake3_t), sizeof(fd_blake3_t));
     920           0 :       fd_blake3_init  ( b3 );
     921           0 :       fd_blake3_append( b3, rec->pair.key->uc,   sizeof( fd_pubkey_t ) );
     922           0 :       fd_blake3_fini  ( b3, hash );
     923             : 
     924           0 :       pairs[num_pairs].hash = hash;
     925           0 :       num_pairs++;
     926           0 :       continue;
     927           0 :     } else {
     928           0 :       fd_hash_t *h = (fd_hash_t *) metadata->hash;
     929           0 :       if ((h->ul[0] | h->ul[1] | h->ul[2] | h->ul[3]) == 0) {
     930             :         // By the time we fall into this case, we can assume the ignore_slot feature is enabled...
     931           0 :         fd_hash_account_current( (uchar *) metadata->hash, NULL, metadata, rec->pair.key->uc, fd_account_get_data(metadata) );
     932           0 :       } else if( do_hash_verify ) {
     933           0 :         uchar hash[32];
     934             :         // ulong old_slot = slot_ctx->slot_bank.slot;
     935             :         // slot_ctx->slot_bank.slot = metadata->slot;
     936           0 :         fd_hash_account_current( (uchar *) &hash, NULL, metadata, rec->pair.key->uc, fd_account_get_data(metadata) );
     937             :         // slot_ctx->slot_bank.slot = old_slot;
     938           0 :         if ( fd_acc_exists( metadata ) && memcmp( metadata->hash, &hash, 32 ) != 0 ) {
     939           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 ) ));
     940           0 :         }
     941           0 :       }
     942           0 :     }
     943             : 
     944           0 :     if ((metadata->info.executable & ~1) != 0)
     945           0 :       continue;
     946             : 
     947           0 :     pairs[num_pairs].rec = rec;
     948           0 :     pairs[num_pairs].hash = (const fd_hash_t *)metadata->hash;
     949           0 :     num_pairs++;
     950           0 :   }
     951             : 
     952           0 :   sort_pubkey_hash_pair_inplace( pairs, num_pairs );
     953           0 :   fd_pubkey_hash_pair_list_t list1 = { .pairs = pairs, .pairs_len = num_pairs };
     954           0 :   fd_hash_account_deltas( &list1, 1, accounts_hash );
     955             : 
     956           0 :   fd_valloc_free( slot_ctx->valloc, pairs );
     957           0 :   fd_scratch_pop();
     958             : 
     959           0 :   FD_LOG_INFO(( "accounts_hash %s", FD_BASE58_ENC_32_ALLOCA( accounts_hash->hash) ));
     960             : 
     961           0 :   return 0;
     962           0 : }
     963             : 
     964             : int
     965             : fd_accounts_hash_inc_no_txn( fd_funk_t *                 funk, 
     966             :                              fd_valloc_t                 valloc, 
     967             :                              fd_hash_t *                 accounts_hash, 
     968             :                              fd_funk_rec_key_t const * * pubkeys,
     969             :                              ulong                       pubkeys_len,
     970           0 :                              ulong                       do_hash_verify ) {
     971           0 :   FD_LOG_NOTICE(( "accounts_hash_inc_no_txn" ));
     972             : 
     973           0 :   fd_wksp_t *     wksp    = fd_funk_wksp( funk );
     974           0 :   fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
     975             : 
     976             :   /* Pre-allocate the number of pubkey pairs that we are iterating over. */
     977             : 
     978           0 :   ulong                   num_iter_accounts = fd_funk_rec_map_key_cnt( rec_map );
     979           0 :   ulong                   num_pairs         = 0UL;
     980           0 :   fd_pubkey_hash_pair_t * pairs             = fd_valloc_malloc( valloc, 
     981           0 :                                                                 FD_PUBKEY_HASH_PAIR_ALIGN, 
     982           0 :                                                                 num_iter_accounts * sizeof(fd_pubkey_hash_pair_t) );
     983             :   
     984           0 :   if( FD_UNLIKELY( !pairs ) ) {
     985           0 :     FD_LOG_ERR(( "failed to allocate memory for pairs" ));
     986           0 :   }
     987             : 
     988           0 :   fd_blake3_t * b3 = NULL;
     989             : 
     990           0 :   FD_SCRATCH_SCOPE_BEGIN {
     991             : 
     992           0 :   for( ulong i=0UL; i<pubkeys_len; i++ ) {
     993           0 :     fd_funk_rec_t const * rec = fd_funk_rec_query( funk, NULL, pubkeys[i] );
     994             : 
     995           0 :     fd_account_meta_t * metadata = (fd_account_meta_t *) fd_funk_val_const( rec, wksp );
     996           0 :     int is_empty = (!metadata || metadata->info.lamports == 0);
     997             : 
     998           0 :     if( is_empty ) {
     999           0 :       pairs[num_pairs].rec = rec;
    1000             : 
    1001           0 :       fd_hash_t * hash = fd_scratch_alloc( alignof(fd_hash_t), sizeof(fd_hash_t) );
    1002           0 :       if( !b3 ) {
    1003           0 :         b3 = fd_scratch_alloc( alignof(fd_blake3_t), sizeof(fd_blake3_t) );
    1004           0 :       }
    1005           0 :       fd_blake3_init  ( b3 );
    1006           0 :       fd_blake3_append( b3, rec->pair.key->uc, sizeof(fd_pubkey_t) );
    1007           0 :       fd_blake3_fini  ( b3, hash );
    1008             : 
    1009           0 :       pairs[ num_pairs ].hash = hash;
    1010           0 :       num_pairs++;
    1011           0 :       continue;
    1012           0 :     } else {
    1013           0 :       fd_hash_t *h = (fd_hash_t*)metadata->hash;
    1014           0 :       if( !(h->ul[ 0 ] | h->ul[ 1 ] | h->ul[ 2 ] | h->ul[ 3 ]) ) {
    1015             :         // By the time we fall into this case, we can assume the ignore_slot feature is enabled...
    1016           0 :         fd_hash_account_current( (uchar*)metadata->hash, NULL, metadata, rec->pair.key->uc, fd_account_get_data( metadata ) );
    1017           0 :       } else if( do_hash_verify ) {
    1018           0 :         uchar hash[ FD_HASH_FOOTPRINT ];
    1019           0 :         fd_hash_account_current( (uchar*)&hash, NULL, metadata, rec->pair.key->uc, fd_account_get_data( metadata ) );
    1020           0 :         if( fd_acc_exists( metadata ) && memcmp( metadata->hash, &hash, FD_HASH_FOOTPRINT ) ) {
    1021           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) ));
    1022           0 :         }
    1023           0 :       }
    1024           0 :     }
    1025             : 
    1026           0 :     if( (metadata->info.executable & ~1) ) {
    1027           0 :       continue;
    1028           0 :     }
    1029             : 
    1030           0 :     pairs[ num_pairs ].rec = rec;
    1031           0 :     pairs[ num_pairs ].hash = (fd_hash_t const *)metadata->hash;
    1032           0 :     num_pairs++;
    1033           0 :   }
    1034             : 
    1035           0 :   sort_pubkey_hash_pair_inplace( pairs, num_pairs );
    1036           0 :   fd_pubkey_hash_pair_list_t list1 = { .pairs = pairs, .pairs_len = num_pairs };
    1037           0 :   fd_hash_account_deltas( &list1, 1, accounts_hash );
    1038             : 
    1039           0 :   fd_valloc_free( valloc, pairs );
    1040             : 
    1041           0 :   } FD_SCRATCH_SCOPE_END;
    1042             : 
    1043           0 :   FD_LOG_INFO(( "accounts_hash %s", FD_BASE58_ENC_32_ALLOCA( accounts_hash->hash) ));
    1044             : 
    1045           0 :   return 0;
    1046           0 : }
    1047             : 
    1048             : int
    1049           0 : fd_snapshot_hash( fd_exec_slot_ctx_t * slot_ctx, fd_tpool_t * tpool, fd_hash_t * accounts_hash, uint check_hash ) {
    1050           0 :   (void) check_hash;
    1051             : 
    1052           0 :   if( fd_should_snapshot_include_epoch_accounts_hash (slot_ctx) ) {
    1053           0 :     FD_LOG_NOTICE(( "snapshot is including epoch account hash" ));
    1054           0 :     fd_sha256_t h;
    1055           0 :     fd_hash_t hash;
    1056           0 :     fd_accounts_hash( slot_ctx->acc_mgr->funk, &slot_ctx->slot_bank, slot_ctx->valloc, tpool, &hash );
    1057             : 
    1058           0 :     fd_sha256_init( &h );
    1059           0 :     fd_sha256_append( &h, (uchar const *) hash.hash, sizeof( fd_hash_t ) );
    1060           0 :     fd_sha256_append( &h, (uchar const *) slot_ctx->slot_bank.epoch_account_hash.hash, sizeof( fd_hash_t ) );
    1061           0 :     fd_sha256_fini( &h, accounts_hash );
    1062             : 
    1063           0 :     return 0;
    1064           0 :   }
    1065           0 :   return fd_accounts_hash( slot_ctx->acc_mgr->funk, &slot_ctx->slot_bank, slot_ctx->valloc, tpool, accounts_hash );
    1066           0 : }
    1067             : 
    1068             : /* TODO: Combine with the above to get correct snapshot hash verification. */
    1069             : 
    1070             : int
    1071             : fd_snapshot_service_hash( fd_hash_t       * accounts_hash,
    1072             :                           fd_hash_t       * snapshot_hash,
    1073             :                           fd_slot_bank_t  * slot_bank,
    1074             :                           fd_epoch_bank_t * epoch_bank,
    1075             :                           fd_funk_t       * funk,
    1076             :                           fd_tpool_t      * tpool,
    1077           0 :                           fd_valloc_t       valloc ) {
    1078             : 
    1079           0 :   fd_sha256_t h;
    1080           0 :   fd_accounts_hash( funk, slot_bank, valloc, tpool, accounts_hash );
    1081             : 
    1082           0 :   int should_include_eah = epoch_bank->eah_stop_slot != ULONG_MAX && epoch_bank->eah_start_slot == ULONG_MAX;
    1083             : 
    1084           0 :   if( should_include_eah ) {
    1085           0 :     fd_sha256_init( &h );
    1086           0 :     fd_sha256_append( &h, (uchar const *) accounts_hash, sizeof( fd_hash_t ) );
    1087           0 :     fd_sha256_append( &h, (uchar const *) slot_bank->epoch_account_hash.hash, sizeof( fd_hash_t ) );
    1088           0 :     fd_sha256_fini( &h, snapshot_hash );
    1089           0 :   } else {
    1090           0 :     fd_memcpy( snapshot_hash, accounts_hash, sizeof(fd_hash_t) );
    1091           0 :   }
    1092             : 
    1093           0 :   return 0;
    1094           0 : }
    1095             : 
    1096             : int
    1097             : fd_snapshot_service_inc_hash( fd_hash_t *                 accounts_hash,
    1098             :                               fd_hash_t *                 snapshot_hash,
    1099             :                               fd_slot_bank_t *            slot_bank,
    1100             :                               fd_epoch_bank_t *           epoch_bank,
    1101             :                               fd_funk_t *                 funk,
    1102             :                               fd_funk_rec_key_t const * * pubkeys,
    1103             :                               ulong                       pubkeys_len,
    1104           0 :                               fd_valloc_t                 valloc ) {
    1105             : 
    1106           0 :   fd_sha256_t h;
    1107           0 :   fd_accounts_hash_inc_no_txn( funk, valloc, accounts_hash, pubkeys, pubkeys_len, 0UL );
    1108             : 
    1109           0 :   int should_include_eah = epoch_bank->eah_stop_slot != ULONG_MAX && epoch_bank->eah_start_slot == ULONG_MAX;
    1110             : 
    1111           0 :   if( should_include_eah ) {
    1112           0 :     fd_sha256_init( &h );
    1113           0 :     fd_sha256_append( &h, (uchar const *) accounts_hash, sizeof( fd_hash_t ) );
    1114           0 :     fd_sha256_append( &h, (uchar const *) slot_bank->epoch_account_hash.hash, sizeof( fd_hash_t ) );
    1115           0 :     fd_sha256_fini( &h, snapshot_hash );
    1116           0 :   } else {
    1117           0 :     fd_memcpy( snapshot_hash, accounts_hash, sizeof(fd_hash_t) );
    1118           0 :   }
    1119             : 
    1120           0 :   return 0;
    1121           0 : }
    1122             : 
    1123             : /* Re-computes the lthash from the current slot */
    1124             : void
    1125             : fd_accounts_check_lthash( fd_funk_t     *  funk,
    1126             :                           fd_funk_txn_t *  funk_txn,
    1127             :                           fd_slot_bank_t * slot_bank,
    1128           0 :                           fd_valloc_t      valloc ) {
    1129             : 
    1130           0 :   fd_wksp_t *     wksp = fd_funk_wksp( funk );
    1131           0 :   fd_funk_rec_t * rec_map  = fd_funk_rec_map( funk, wksp );
    1132           0 :   fd_funk_txn_t * txn_map  = fd_funk_txn_map( funk, wksp );
    1133             : 
    1134             :   // How many txns are we dealing with?
    1135           0 :   ulong txn_cnt = 1;
    1136           0 :   fd_funk_txn_t * txn = funk_txn;
    1137           0 :   while (NULL != txn) {
    1138           0 :     txn_cnt++;
    1139           0 :     txn = fd_funk_txn_parent( txn, txn_map );
    1140           0 :   }
    1141             : 
    1142           0 :   fd_funk_txn_t ** txns = fd_alloca_check(sizeof(fd_funk_txn_t *), sizeof(fd_funk_txn_t *) * txn_cnt);
    1143           0 :   if ( FD_UNLIKELY(NULL == txns))
    1144           0 :     FD_LOG_ERR(("Out of scratch space?"));
    1145             : 
    1146             :   // Lay it flat to make it easier to walk backwards up the chain from
    1147             :   // the root
    1148           0 :   txn = funk_txn;
    1149           0 :   ulong txn_idx = txn_cnt;
    1150           0 :   while (1) {
    1151           0 :     txns[--txn_idx] = txn;
    1152           0 :     if (NULL == txn)
    1153           0 :       break;
    1154           0 :     txn = fd_funk_txn_parent( txn, txn_map );
    1155           0 :   }
    1156             : 
    1157             :   // How many total records are we dealing with?
    1158           0 :   ulong           num_iter_accounts = fd_funk_rec_map_key_cnt( rec_map );
    1159             : 
    1160           0 :   int accounts_hash_slots = fd_ulong_find_msb(num_iter_accounts  ) + 1;
    1161             : 
    1162           0 :   FD_LOG_WARNING(("allocating memory for hash.  num_iter_accounts: %lu   slots: %d", num_iter_accounts, accounts_hash_slots));
    1163           0 :   void * hashmem = fd_valloc_malloc( valloc, accounts_hash_align(), accounts_hash_footprint(accounts_hash_slots));
    1164           0 :   FD_LOG_WARNING(("initializing memory for hash"));
    1165           0 :   accounts_hash_t * hash_map = accounts_hash_join(accounts_hash_new(hashmem, accounts_hash_slots));
    1166             : 
    1167           0 :   FD_LOG_WARNING(("copying in accounts"));
    1168             : 
    1169             :   // walk up the transactions...
    1170           0 :   for (ulong idx = 0; idx < txn_cnt; idx++) {
    1171           0 :     FD_LOG_WARNING(("txn idx %lu", idx));
    1172           0 :     for (fd_funk_rec_t const *rec = fd_funk_txn_first_rec( funk, txns[idx]);
    1173           0 :          NULL != rec;
    1174           0 :          rec = fd_funk_txn_next_rec(funk, rec)) {
    1175           0 :       if ( fd_funk_key_is_acc( rec->pair.key ) && !( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) {
    1176           0 :         accounts_hash_t * q = accounts_hash_query(hash_map, (fd_funk_rec_t *) rec, NULL);
    1177           0 :         if (NULL != q)
    1178           0 :           accounts_hash_remove(hash_map, q);
    1179           0 :         if (!(rec->flags & FD_FUNK_REC_FLAG_ERASE))
    1180           0 :           accounts_hash_insert(hash_map, (fd_funk_rec_t *) rec);
    1181           0 :       }
    1182           0 :     }
    1183           0 :   }
    1184             : 
    1185           0 :   FD_LOG_WARNING(("assumulating a new lthash"));
    1186             : 
    1187             :   // Initialize the accumulator to zero
    1188           0 :   fd_lthash_value_t acc_lthash;
    1189           0 :   fd_lthash_zero( &acc_lthash );
    1190             : 
    1191           0 :   ulong slot_cnt = accounts_hash_slot_cnt(hash_map);;
    1192           0 :   for( ulong slot_idx=0UL; slot_idx<slot_cnt; slot_idx++ ) {
    1193           0 :     accounts_hash_t *slot = &hash_map[slot_idx];
    1194           0 :     if (FD_UNLIKELY (NULL != slot->key)) {
    1195           0 :       void const * data = fd_funk_val_const( slot->key, wksp );
    1196           0 :       fd_account_meta_t * metadata = (fd_account_meta_t *)fd_type_pun_const( data );
    1197           0 :       if( FD_UNLIKELY(metadata->info.lamports != 0) ) {
    1198           0 :         uchar *             acc_data = fd_account_get_data(metadata);
    1199           0 :         uchar hash  [ 32 ];
    1200           0 :         fd_lthash_value_t new_lthash_value;
    1201           0 :         fd_lthash_zero(&new_lthash_value);
    1202           0 :         fd_hash_account_current( hash, &new_lthash_value, metadata, slot->key->pair.key[0].uc, acc_data );
    1203           0 :         fd_lthash_add( &acc_lthash, &new_lthash_value );
    1204             : 
    1205           0 :         if (fd_acc_exists( metadata ) && memcmp( metadata->hash, &hash, 32 ) != 0 ) {
    1206           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 ) ));
    1207           0 :         }
    1208           0 :       }
    1209           0 :     }
    1210           0 :   }
    1211             : 
    1212             :   // Compare the accumulator to the slot
    1213           0 :   fd_lthash_value_t * acc = (fd_lthash_value_t *)fd_type_pun_const( slot_bank->lthash.lthash );
    1214           0 :   if ( memcmp( acc, &acc_lthash, sizeof( fd_lthash_value_t ) ) == 0 ) {
    1215           0 :     FD_LOG_NOTICE(("accounts_lthash %s == %s", FD_LTHASH_ENC_32_ALLOCA (acc), FD_LTHASH_ENC_32_ALLOCA (&acc_lthash)));
    1216           0 :   } else {
    1217           0 :     FD_LOG_ERR(("accounts_lthash %s != %s", FD_LTHASH_ENC_32_ALLOCA (acc), FD_LTHASH_ENC_32_ALLOCA (&acc_lthash)));
    1218           0 :   }
    1219           0 : }

Generated by: LCOV version 1.14