LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_hashes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 902 0.0 %
Date: 2025-08-05 05:04:49 Functions: 0 23 0.0 %

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

Generated by: LCOV version 1.14