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

Generated by: LCOV version 1.14