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