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

Generated by: LCOV version 1.14