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