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