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, <_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( <_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 : <_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 : <_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(<hash_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( <hash_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, <hash_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 : }
|