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 ) {
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->slot_bank.prev_banks_hash = slot_ctx->slot_bank.banks_hash;
223 0 : slot_ctx->slot_bank.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 :
230 0 : fd_hash_account_deltas(&list1, 1, &slot_ctx->account_delta_hash );
231 :
232 0 : fd_sha256_t sha;
233 0 : fd_sha256_init( &sha );
234 0 : fd_sha256_append( &sha, (uchar const *) &slot_ctx->slot_bank.banks_hash, sizeof( fd_hash_t ) );
235 0 : fd_sha256_append( &sha, (uchar const *) &slot_ctx->account_delta_hash, sizeof( fd_hash_t ) );
236 0 : fd_sha256_append( &sha, (uchar const *) &slot_ctx->signature_cnt, sizeof( ulong ) );
237 0 : fd_sha256_append( &sha, (uchar const *) &slot_ctx->slot_bank.poh, sizeof( fd_hash_t ) );
238 :
239 0 : fd_sha256_fini( &sha, hash->hash );
240 :
241 0 : if (fd_should_include_epoch_accounts_hash(slot_ctx)) {
242 0 : fd_sha256_init( &sha );
243 0 : fd_sha256_append( &sha, (uchar const *) &hash->hash, sizeof( fd_hash_t ) );
244 :
245 0 : fd_sha256_append( &sha, (uchar const *) &slot_ctx->slot_bank.epoch_account_hash.hash, sizeof( fd_hash_t ) );
246 :
247 0 : fd_sha256_fini( &sha, hash->hash );
248 0 : }
249 :
250 0 : if( capture_ctx != NULL && capture_ctx->capture != NULL ) {
251 0 : fd_solcap_write_bank_preimage(
252 0 : capture_ctx->capture,
253 0 : hash->hash,
254 0 : slot_ctx->slot_bank.prev_banks_hash.hash,
255 0 : slot_ctx->account_delta_hash.hash,
256 0 : &slot_ctx->slot_bank.poh.hash,
257 0 : slot_ctx->signature_cnt );
258 0 : }
259 :
260 0 : FD_LOG_NOTICE(( "\n\n[Replay]\n"
261 0 : "slot: %lu\n"
262 0 : "bank hash: %s\n"
263 0 : "parent bank hash: %s\n"
264 0 : "accounts_delta: %s\n"
265 0 : "lthash: %s\n"
266 0 : "signature_count: %lu\n"
267 0 : "last_blockhash: %s\n",
268 0 : slot_ctx->slot_bank.slot,
269 0 : FD_BASE58_ENC_32_ALLOCA( hash->hash ),
270 0 : FD_BASE58_ENC_32_ALLOCA( slot_ctx->slot_bank.prev_banks_hash.hash ),
271 0 : FD_BASE58_ENC_32_ALLOCA( slot_ctx->account_delta_hash.hash ),
272 0 : FD_LTHASH_ENC_32_ALLOCA( (fd_lthash_value_t *) slot_ctx->slot_bank.lthash.lthash ),
273 0 : slot_ctx->signature_cnt,
274 0 : FD_BASE58_ENC_32_ALLOCA( slot_ctx->slot_bank.poh.hash ) ));
275 0 : }
276 :
277 : struct fd_accounts_hash_task_info {
278 : fd_exec_slot_ctx_t * slot_ctx;
279 : fd_pubkey_t acc_pubkey[1];
280 : fd_hash_t acc_hash[1];
281 : fd_funk_rec_t const * rec;
282 : uint should_erase;
283 : uint hash_changed;
284 : };
285 : typedef struct fd_accounts_hash_task_info fd_accounts_hash_task_info_t;
286 :
287 : struct fd_accounts_hash_task_data {
288 : struct fd_accounts_hash_task_info *info;
289 : ulong info_sz;
290 : fd_lthash_value_t *lthash_values;
291 : };
292 : typedef struct fd_accounts_hash_task_data fd_accounts_hash_task_data_t;
293 :
294 : static void
295 : fd_account_hash_task( void *tpool,
296 : ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
297 : void *args FD_PARAM_UNUSED,
298 : void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
299 : ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
300 : ulong m0, ulong m1 FD_PARAM_UNUSED,
301 0 : ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED) {
302 0 : fd_accounts_hash_task_info_t * task_info = ((fd_accounts_hash_task_data_t *)tpool)->info + m0;
303 0 : fd_exec_slot_ctx_t * slot_ctx = task_info->slot_ctx;
304 0 : int err = 0;
305 0 : fd_funk_txn_t const * txn_out = NULL;
306 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 );
307 0 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS || !acc_meta ) ) {
308 0 : FD_LOG_WARNING(( "failed to view account during bank hash" ));
309 0 : return;
310 0 : }
311 :
312 0 : fd_account_meta_t * acc_meta_parent = NULL;
313 0 : if( NULL != txn_out ) {
314 0 : fd_funk_t * funk = slot_ctx->acc_mgr->funk;
315 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
316 0 : fd_funk_txn_t * txn_map = fd_funk_txn_map( funk, wksp );
317 0 : txn_out = fd_funk_txn_parent( (fd_funk_txn_t *) txn_out, txn_map );
318 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);
319 0 : }
320 :
321 0 : fd_lthash_value_t * acc = &(((fd_accounts_hash_task_data_t *)tpool)->lthash_values[n0]);
322 :
323 0 : if( FD_UNLIKELY(acc_meta->info.lamports == 0) ) {
324 0 : fd_memset( task_info->acc_hash->hash, 0, FD_HASH_FOOTPRINT );
325 :
326 : /* If we erase records instantly, this causes problems with the
327 : iterator. Instead, we will store away the record and erase
328 : it later where appropriate. */
329 0 : task_info->should_erase = 1;
330 : /* In the exceedingly unlikely event that the account's old hash is
331 : actually 0, this would cause the account not to be included in
332 : the bank hash. */
333 0 : if( memcmp( task_info->acc_hash->hash, acc_meta->hash, sizeof(fd_hash_t) ) != 0 ) {
334 0 : task_info->hash_changed = 1;
335 0 : }
336 0 : } else {
337 0 : uchar * acc_data = fd_account_get_data((fd_account_meta_t *) acc_meta);
338 0 : fd_pubkey_t const * acc_key = fd_funk_key_to_acc( task_info->rec->pair.key );
339 0 : fd_lthash_value_t new_lthash_value;
340 0 : fd_lthash_zero(&new_lthash_value);
341 0 : fd_hash_account_current( task_info->acc_hash->hash, &new_lthash_value, acc_meta, acc_key->key, acc_data );
342 :
343 0 : if( memcmp( task_info->acc_hash->hash, acc_meta->hash, sizeof(fd_hash_t) ) != 0 ) {
344 0 : task_info->hash_changed = 1;
345 0 : fd_lthash_add( acc, &new_lthash_value);
346 0 : }
347 0 : }
348 :
349 0 : if( FD_LIKELY(task_info->hash_changed && ((NULL != acc_meta_parent) && (acc_meta_parent->info.lamports != 0) ) ) ) {
350 0 : uchar * acc_data = fd_account_get_data(acc_meta_parent);
351 0 : fd_pubkey_t const * acc_key = fd_funk_key_to_acc( task_info->rec->pair.key );
352 0 : fd_lthash_value_t old_lthash_value;
353 0 : fd_lthash_zero(&old_lthash_value);
354 0 : fd_hash_t old_hash;
355 :
356 0 : fd_hash_account_current( old_hash.hash, &old_lthash_value, acc_meta_parent, acc_key->key, acc_data );
357 0 : fd_lthash_sub( acc, &old_lthash_value );
358 0 : }
359 :
360 0 : if( acc_meta->slot == slot_ctx->slot_bank.slot ) {
361 0 : task_info->hash_changed = 1;
362 0 : }
363 0 : }
364 :
365 : void
366 : fd_collect_modified_accounts( fd_exec_slot_ctx_t * slot_ctx,
367 0 : fd_accounts_hash_task_data_t *task_data ) {
368 0 : fd_acc_mgr_t * acc_mgr = slot_ctx->acc_mgr;
369 0 : fd_funk_t * funk = acc_mgr->funk;
370 0 : fd_funk_txn_t * txn = slot_ctx->funk_txn;
371 :
372 0 : ulong rec_cnt = 0;
373 0 : for( fd_funk_rec_t const * rec = fd_funk_txn_first_rec( funk, txn );
374 0 : NULL != rec;
375 0 : rec = fd_funk_txn_next_rec( funk, rec ) ) {
376 :
377 0 : if( !fd_funk_key_is_acc( rec->pair.key ) )
378 0 : continue;
379 :
380 0 : fd_pubkey_t const * pubkey = fd_funk_key_to_acc( rec->pair.key );
381 :
382 0 : if (((pubkey->ul[0] == 0) & (pubkey->ul[1] == 0) & (pubkey->ul[2] == 0) & (pubkey->ul[3] == 0)))
383 0 : FD_LOG_WARNING(( "null pubkey (system program?) showed up as modified" ));
384 :
385 0 : rec_cnt++;
386 0 : }
387 :
388 0 : task_data->info = fd_valloc_malloc( slot_ctx->valloc, 8UL, rec_cnt * sizeof(fd_accounts_hash_task_info_t) );
389 :
390 : /* Iterate over accounts that have been changed in the current
391 : database transaction. */
392 0 : ulong task_info_idx = 0;
393 0 : for( fd_funk_rec_t const * rec = fd_funk_txn_first_rec( funk, txn );
394 0 : NULL != rec;
395 0 : rec = fd_funk_txn_next_rec( funk, rec ) ) {
396 :
397 0 : fd_pubkey_t const * acc_key = fd_funk_key_to_acc( rec->pair.key );
398 :
399 0 : if( !fd_funk_key_is_acc( rec->pair.key ) )
400 0 : continue;
401 :
402 0 : fd_accounts_hash_task_info_t * task_info = &task_data->info[task_info_idx++];
403 :
404 0 : *task_info->acc_pubkey = *acc_key;
405 0 : task_info->slot_ctx = slot_ctx;
406 0 : task_info->hash_changed = 0;
407 0 : task_info->should_erase = 0;
408 0 : }
409 :
410 0 : task_data->info_sz = task_info_idx;
411 0 : }
412 :
413 : int
414 : fd_update_hash_bank_tpool( fd_exec_slot_ctx_t * slot_ctx,
415 : fd_capture_ctx_t * capture_ctx,
416 : fd_hash_t * hash,
417 : ulong signature_cnt,
418 0 : fd_tpool_t * tpool ) {
419 0 : fd_acc_mgr_t * acc_mgr = slot_ctx->acc_mgr;
420 0 : fd_funk_t * funk = acc_mgr->funk;
421 0 : fd_funk_txn_t * txn = slot_ctx->funk_txn;
422 :
423 : /* Collect list of changed accounts to be added to bank hash */
424 0 : fd_accounts_hash_task_data_t task_data;
425 :
426 0 : ulong wcnt = fd_tpool_worker_cnt( tpool );
427 0 : task_data.lthash_values = fd_valloc_malloc( slot_ctx->valloc, FD_LTHASH_VALUE_ALIGN, wcnt * FD_LTHASH_VALUE_FOOTPRINT );
428 0 : for( ulong i = 0; i < wcnt; i++ ) {
429 0 : fd_lthash_zero(&task_data.lthash_values[i]);
430 0 : }
431 :
432 : /* Find accounts which might have changed */
433 0 : fd_collect_modified_accounts( slot_ctx, &task_data);
434 :
435 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 );
436 0 : ulong dirty_key_cnt = 0;
437 :
438 : /* Find accounts which have changed */
439 0 : fd_tpool_exec_all_rrobin( tpool, 0, wcnt, fd_account_hash_task, &task_data, NULL, NULL, 1, 0, task_data.info_sz );
440 :
441 : // Apply the lthash changes to the bank lthash
442 0 : fd_lthash_value_t * acc = (fd_lthash_value_t *)fd_type_pun(slot_ctx->slot_bank.lthash.lthash);
443 0 : for( ulong i = 0; i < wcnt; i++ ) {
444 0 : fd_lthash_add( acc, &task_data.lthash_values[i] );
445 0 : }
446 :
447 0 : for( ulong i = 0; i < task_data.info_sz; i++ ) {
448 0 : fd_accounts_hash_task_info_t * task_info = &task_data.info[i];
449 : /* Upgrade to writable record */
450 0 : if( !task_info->hash_changed ) {
451 0 : continue;
452 0 : }
453 :
454 0 : FD_BORROWED_ACCOUNT_DECL(acc_rec);
455 0 : acc_rec->const_rec = task_info->rec;
456 :
457 0 : fd_pubkey_t const * acc_key = fd_funk_key_to_acc( task_info->rec->pair.key );
458 0 : int err = fd_acc_mgr_modify( acc_mgr, txn, acc_key, 0, 0UL, acc_rec);
459 0 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
460 0 : FD_LOG_ERR(( "failed to modify account during bank hash" ));
461 0 : }
462 :
463 : /* Update hash */
464 :
465 0 : memcpy( acc_rec->meta->hash, task_info->acc_hash->hash, sizeof(fd_hash_t) );
466 0 : acc_rec->meta->slot = slot_ctx->slot_bank.slot;
467 :
468 : /* Add account to "dirty keys" list, which will be added to the
469 : bank hash. */
470 :
471 0 : fd_pubkey_hash_pair_t * dirty_entry = &dirty_keys[dirty_key_cnt++];
472 0 : dirty_entry->rec = task_info->rec;
473 0 : dirty_entry->hash = (fd_hash_t const *)acc_rec->meta->hash;
474 :
475 0 : char acc_key_string[ FD_BASE58_ENCODED_32_SZ ];
476 0 : fd_acct_addr_cstr( acc_key_string, (uchar const*)acc_key );
477 0 : char owner_string[ FD_BASE58_ENCODED_32_SZ ];
478 0 : fd_acct_addr_cstr( owner_string, acc_rec->meta->info.owner );
479 :
480 0 : FD_LOG_DEBUG(( "fd_acc_mgr_update_hash: %s "
481 0 : "slot: %lu "
482 0 : "lamports: %lu "
483 0 : "owner: %s "
484 0 : "executable: %s, "
485 0 : "rent_epoch: %lu, "
486 0 : "data_len: %lu",
487 0 : acc_key_string,
488 0 : slot_ctx->slot_bank.slot,
489 0 : acc_rec->meta->info.lamports,
490 0 : owner_string,
491 0 : acc_rec->meta->info.executable ? "true" : "false",
492 0 : acc_rec->meta->info.rent_epoch,
493 0 : acc_rec->meta->dlen ));
494 :
495 0 : if( capture_ctx != NULL && capture_ctx->capture != NULL ) {
496 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);
497 0 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
498 0 : FD_LOG_WARNING(( "failed to view account during capture" ));
499 0 : continue;
500 0 : }
501 :
502 0 : uchar const * acc_data = (uchar *)acc_meta + acc_meta->hlen;
503 :
504 0 : err = fd_solcap_write_account(
505 0 : capture_ctx->capture,
506 0 : acc_key->uc,
507 0 : &acc_rec->meta->info,
508 0 : acc_data,
509 0 : acc_rec->meta->dlen,
510 0 : task_info->acc_hash->hash );
511 0 : FD_TEST( err==0 );
512 0 : }
513 0 : }
514 :
515 : /* Sort and hash "dirty keys" to the accounts delta hash. */
516 :
517 : // FD_LOG_DEBUG(("slot %ld, dirty %ld", slot_ctx->slot_bank.slot, dirty_key_cnt));
518 :
519 0 : slot_ctx->signature_cnt = signature_cnt;
520 0 : fd_hash_bank( slot_ctx, capture_ctx, hash, dirty_keys, dirty_key_cnt);
521 :
522 0 : for( ulong i = 0; i < task_data.info_sz; i++ ) {
523 0 : fd_accounts_hash_task_info_t * task_info = &task_data.info[i];
524 : /* Upgrade to writable record */
525 0 : if( FD_LIKELY( !task_info->should_erase ) ) {
526 0 : continue;
527 0 : }
528 :
529 : /* All removed recs should be stored with the slot from the funk txn. */
530 0 : fd_funk_rec_remove( funk, fd_funk_rec_modify(funk, task_info->rec), task_info->rec->pair.xid->ul[0] );
531 0 : }
532 :
533 : // Sanity-check LT Hash
534 : // fd_accounts_check_lthash( slot_ctx );
535 :
536 0 : fd_valloc_free( slot_ctx->valloc, task_data.info );
537 0 : fd_valloc_free( slot_ctx->valloc, task_data.lthash_values );
538 0 : fd_valloc_free( slot_ctx->valloc, dirty_keys );
539 :
540 0 : return FD_EXECUTOR_INSTR_SUCCESS;
541 0 : }
542 :
543 : int
544 : fd_print_account_hashes( fd_exec_slot_ctx_t * slot_ctx,
545 0 : fd_tpool_t * tpool ) {
546 :
547 : // fd_acc_mgr_t * acc_mgr = slot_ctx->acc_mgr;
548 : // fd_funk_txn_t * txn = slot_ctx->funk_txn;
549 :
550 : /* Collect list of changed accounts to be added to bank hash */
551 0 : fd_accounts_hash_task_data_t task_data;
552 :
553 0 : fd_collect_modified_accounts( slot_ctx, &task_data );
554 :
555 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 );
556 0 : ulong dirty_key_cnt = 0;
557 :
558 0 : ulong wcnt = fd_tpool_worker_cnt( tpool );
559 0 : task_data.lthash_values = fd_valloc_malloc( slot_ctx->valloc, FD_LTHASH_VALUE_ALIGN, wcnt * FD_LTHASH_VALUE_FOOTPRINT );
560 0 : for( ulong i = 0; i < wcnt; i++ ) {
561 0 : fd_lthash_zero(&task_data.lthash_values[i]);
562 0 : }
563 :
564 : /* Find accounts which have changed */
565 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 );
566 :
567 0 : for( ulong i = 0; i < task_data.info_sz; i++ ) {
568 0 : fd_accounts_hash_task_info_t * task_info = &task_data.info[i];
569 : /* Upgrade to writable record */
570 0 : if( !task_info->hash_changed ) {
571 0 : continue;
572 0 : }
573 :
574 0 : FD_BORROWED_ACCOUNT_DECL(acc_rec);
575 0 : acc_rec->const_rec = task_info->rec;
576 :
577 : // int err = fd_acc_mgr_modify( acc_mgr, txn, acc_key, 0, 0UL, acc_rec);
578 : // if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
579 : // FD_LOG_ERR(( "failed to modify account during bank hash" ));
580 : // }
581 :
582 : /* Update hash */
583 :
584 : // memcpy( acc_rec->meta->hash, task_info->acc_hash->hash, sizeof(fd_hash_t) );
585 : // acc_rec->meta->slot = slot_ctx->slot_bank.slot;
586 :
587 : /* Add account to "dirty keys" list, which will be added to the
588 : bank hash. */
589 :
590 0 : fd_pubkey_hash_pair_t * dirty_entry = &dirty_keys[dirty_key_cnt++];
591 0 : dirty_entry->rec = task_info->rec;
592 0 : dirty_entry->hash = (fd_hash_t const *)task_info->acc_hash->hash;
593 :
594 : // FD_TEST( err==0 );
595 0 : }
596 :
597 : /* Sort and hash "dirty keys" to the accounts delta hash. */
598 :
599 : #ifdef VLOG
600 : for( ulong i = 0; i < dirty_key_cnt; ++i ) {
601 : FD_LOG_NOTICE(( "account delta hash X { \"key\":%ld, \"pubkey\":\"%s\", \"hash\":\"%s\" },",
602 : i,
603 : FD_BASE58_ENC_32_ALLOCA( dirty_keys[i].pubkey->key ),
604 : FD_BASE58_ENC_32_ALLOCA( dirty_keys[i].hash->hash) ));
605 :
606 : /*
607 : pubkey
608 : slot
609 : lamports
610 : owner
611 : executable
612 : rent_epoch
613 : data_len
614 : data
615 : hash
616 : */
617 : // fd_pubkey_t current_owner;
618 : // fd_acc_mgr_get_owner( global->acc_mgr, global->funk_txn, &pairs[i].pubkey, ¤t_owner );
619 : // char encoded_owner[50];
620 : // fd_base58_encode_32((uchar *) ¤t_owner, 0, encoded_owner);
621 : int err = FD_ACC_MGR_SUCCESS;
622 : 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);
623 : if (NULL != raw_acc_data) {
624 :
625 : fd_account_meta_t * metadata = (fd_account_meta_t *)raw_acc_data;
626 : uchar * acc_data = fd_account_get_data(metadata);
627 : char * acc_data_str = fd_valloc_malloc(slot_ctx->valloc, 8, 5*metadata->dlen + 1);
628 :
629 : char * acc_data_str_cursor = acc_data_str;
630 : if (metadata->dlen > 0) {
631 : for( ulong j = 0; j < (metadata->dlen - 1); j++ ) {
632 : int x = sprintf(acc_data_str_cursor, "%u, ", acc_data[j]);
633 : acc_data_str_cursor += x;
634 : }
635 : sprintf(acc_data_str_cursor, "%u", acc_data[metadata->dlen - 1]);
636 : } else {
637 : *acc_data_str_cursor = 0;
638 : }
639 :
640 : 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) ",
641 : FD_BASE58_ENC_32_ALLOCA( dirty_keys[i].pubkey->uc ),
642 : slot_ctx->slot_bank.slot,
643 : metadata->info.lamports,
644 : FD_BASE58_ENC_32_ALLOCA( metadata->info.owner ),
645 : metadata->info.executable,
646 : metadata->info.rent_epoch,
647 : metadata->dlen,
648 : FD_BASE58_ENC_32_ALLOCA( dirty_keys[i].hash->hash ) ));
649 :
650 : fd_valloc_free(slot_ctx->valloc, acc_data_str);
651 : }
652 : }
653 : #endif
654 :
655 0 : fd_valloc_free( slot_ctx->valloc, task_data.info );
656 0 : fd_valloc_free( slot_ctx->valloc, task_data.lthash_values );
657 0 : fd_valloc_free( slot_ctx->valloc, dirty_keys );
658 :
659 0 : return 0;
660 0 : }
661 :
662 : void const *
663 : fd_hash_account( uchar hash[ static 32 ],
664 : fd_lthash_value_t * lthash,
665 : fd_account_meta_t const * m,
666 : uchar const pubkey[ static 32 ],
667 0 : uchar const * data ) {
668 0 : ulong lamports = m->info.lamports; /* >0UL */
669 0 : ulong rent_epoch = m->info.rent_epoch;
670 0 : uchar executable = m->info.executable & 0x1;
671 0 : uchar const * owner = (uchar const *)m->info.owner;
672 :
673 0 : fd_blake3_t b3[1];
674 0 : fd_blake3_init ( b3 );
675 0 : fd_blake3_append( b3, &lamports, sizeof( ulong ) );
676 0 : fd_blake3_append( b3, &rent_epoch, sizeof( ulong ) );
677 0 : fd_blake3_append( b3, data, m->dlen );
678 0 : fd_blake3_append( b3, &executable, sizeof( uchar ) );
679 0 : fd_blake3_append( b3, owner, 32UL );
680 0 : fd_blake3_append( b3, pubkey, 32UL );
681 0 : if( NULL == lthash ) {
682 0 : fd_blake3_fini ( b3, hash );
683 0 : } else {
684 0 : fd_blake3_fini_varlen( b3, lthash->bytes, FD_LTHASH_LEN_BYTES );
685 0 : fd_memcpy( hash, lthash->bytes, 32);
686 0 : }
687 :
688 0 : return hash;
689 0 : }
690 :
691 : void const *
692 : fd_hash_account_current( uchar hash [ static 32 ],
693 : fd_lthash_value_t *lthash,
694 : fd_account_meta_t const *account,
695 : uchar const pubkey[ static 32 ],
696 0 : uchar const *data ) {
697 0 : return fd_hash_account( hash, lthash, account, pubkey, data );
698 0 : }
699 :
700 : struct accounts_hash {
701 : fd_funk_rec_t * key;
702 : ulong hash;
703 : };
704 : typedef struct accounts_hash accounts_hash_t;
705 :
706 : #define MAP_NAME accounts_hash
707 0 : #define MAP_KEY_T fd_funk_rec_t *
708 0 : #define MAP_HASH_T ulong
709 0 : #define MAP_KEY_EQUAL(k0,k1) ((NULL != k0) && (NULL != k1) && fd_funk_rec_key_eq( k0->pair.key, k1->pair.key ))
710 0 : #define MAP_KEY_HASH(p) fd_funk_rec_key_hash( p->pair.key, 2887034UL )
711 : #define MAP_KEY_EQUAL_IS_SLOW 1
712 0 : #define MAP_KEY_NULL 0UL
713 0 : #define MAP_KEY_INVAL(k) (NULL == k)
714 :
715 : // #define MAP_KEY_COPY(kd,ks) fd_funk_xid_key_pair_copy((kd),(ks))
716 :
717 0 : #define MAP_T accounts_hash_t
718 : #include "../../util/tmpl/fd_map_dynamic.c"
719 :
720 : static fd_pubkey_hash_pair_t *
721 : fd_accounts_sorted_subrange( fd_funk_t * funk,
722 : uint range_idx,
723 : uint range_cnt,
724 : ulong * num_pairs_out,
725 : fd_lthash_value_t * lthash_values,
726 : ulong n0,
727 : fd_valloc_t valloc
728 0 : ) {
729 :
730 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
731 0 : fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
732 0 : ulong num_iter_accounts = fd_funk_rec_map_key_max( rec_map );
733 0 : ulong max_pairs = ( range_cnt == 1U ? num_iter_accounts : 2UL*num_iter_accounts/range_cnt ); /* Initial estimate */
734 0 : ulong num_pairs = 0;
735 0 : fd_pubkey_hash_pair_t * pairs = fd_valloc_malloc( valloc, FD_PUBKEY_HASH_PAIR_ALIGN, max_pairs * sizeof(fd_pubkey_hash_pair_t) );
736 0 : FD_TEST(NULL != pairs);
737 0 : ulong range_len = ULONG_MAX/range_cnt;
738 0 : ulong range_min = range_len*range_idx;
739 0 : ulong range_max = ( range_idx+1U < range_cnt ? range_min+range_len-1U : ULONG_MAX );
740 :
741 0 : fd_lthash_value_t accum;
742 0 : fd_lthash_zero(&accum);
743 :
744 0 : for( ulong i = num_iter_accounts; i; --i ) {
745 0 : fd_funk_rec_t const * rec = rec_map + (i-1UL);
746 0 : if ( ( rec->map_next >> 63 ) /* unused map entry */ ||
747 0 : !fd_funk_key_is_acc( rec->pair.key ) || /* not a solana record */
748 0 : ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) || /* this is a tombstone */
749 0 : ( rec->pair.xid->ul[0] | rec->pair.xid->ul[1] ) != 0 /* not root xid */ ) {
750 0 : continue;
751 0 : }
752 :
753 0 : ulong n = __builtin_bswap64(rec->pair.key->ul[0]);
754 0 : if( n < range_min || n > range_max ) {
755 0 : continue;
756 0 : }
757 :
758 0 : fd_account_meta_t * metadata = (fd_account_meta_t *) fd_funk_val_const( rec, wksp );
759 0 : int is_empty = (metadata->info.lamports == 0);
760 0 : if( is_empty ) {
761 0 : continue;
762 0 : }
763 :
764 0 : uchar hash[32];
765 0 : fd_lthash_value_t new_lthash_value;
766 0 : fd_lthash_zero(&new_lthash_value);
767 :
768 0 : fd_hash_account_current( (uchar *) hash, &new_lthash_value, metadata, rec->pair.key->uc, fd_account_get_data(metadata) );
769 0 : fd_lthash_add( &accum, &new_lthash_value );
770 :
771 0 : fd_hash_t * h = (fd_hash_t *) metadata->hash;
772 0 : if( FD_LIKELY( (h->ul[0] | h->ul[1] | h->ul[2] | h->ul[3]) != 0 ) ) {
773 0 : if( FD_UNLIKELY( fd_acc_exists( metadata ) && memcmp( metadata->hash, &hash, 32 ) != 0 ) ) {
774 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 ) ));
775 0 : }
776 0 : } else
777 0 : fd_memcpy( metadata->hash, &hash, 32 );
778 :
779 0 : if( (metadata->info.executable & ~1) != 0 )
780 0 : continue;
781 :
782 0 : if( num_pairs == max_pairs ) {
783 : /* Try again with a larger array */
784 0 : fd_valloc_free( valloc, pairs );
785 0 : max_pairs *= 2;
786 0 : pairs = fd_valloc_malloc( valloc, FD_PUBKEY_HASH_PAIR_ALIGN, max_pairs * sizeof(fd_pubkey_hash_pair_t) );
787 0 : FD_TEST(NULL != pairs);
788 0 : num_pairs = 0;
789 0 : fd_lthash_zero(&accum);
790 0 : i = num_iter_accounts+1;
791 0 : continue;
792 0 : }
793 :
794 0 : fd_pubkey_hash_pair_t * pair = &pairs[num_pairs++];
795 0 : pair->rec = rec;
796 0 : pair->hash = (const fd_hash_t *)metadata->hash;
797 0 : }
798 :
799 0 : sort_pubkey_hash_pair_inplace( pairs, num_pairs );
800 :
801 : // FD_LOG_NOTICE(( "sorted_subrange %lx ... %lx => %lu pairs", range_min, range_max, num_pairs ));
802 0 : *num_pairs_out = num_pairs;
803 :
804 0 : fd_lthash_add( <hash_values[n0], &accum );
805 0 : return pairs;
806 0 : }
807 :
808 : struct fd_subrange_task_info {
809 : fd_funk_t * funk;
810 : ulong num_lists;
811 : fd_pubkey_hash_pair_list_t * lists;
812 : fd_lthash_value_t *lthash_values;
813 : fd_valloc_t valloc;
814 : };
815 : typedef struct fd_subrange_task_info fd_subrange_task_info_t;
816 :
817 : static void
818 : fd_accounts_sorted_subrange_task( void *tpool,
819 : ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
820 : void *args FD_PARAM_UNUSED,
821 : void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
822 : ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
823 : ulong m0, ulong m1 FD_PARAM_UNUSED,
824 0 : ulong n0, ulong n1 FD_PARAM_UNUSED) {
825 0 : fd_subrange_task_info_t * task_info = (fd_subrange_task_info_t *)tpool;
826 0 : fd_pubkey_hash_pair_list_t * list = task_info->lists + m0;
827 0 : list->pairs = fd_accounts_sorted_subrange( task_info->funk, (uint)m0, (uint)task_info->num_lists, &list->pairs_len, task_info->lthash_values, n0, task_info->valloc );
828 0 : }
829 :
830 : int
831 : fd_accounts_hash( fd_funk_t * funk,
832 : fd_slot_bank_t * slot_bank,
833 : fd_valloc_t valloc,
834 : fd_tpool_t * tpool,
835 0 : fd_hash_t * accounts_hash ) {
836 0 : FD_LOG_NOTICE(("accounts_hash start"));
837 :
838 0 : if( tpool == NULL || fd_tpool_worker_cnt( tpool ) <= 1U ) {
839 0 : ulong num_pairs = 0;
840 0 : fd_lthash_value_t *lthash_values = fd_valloc_malloc( valloc, FD_LTHASH_VALUE_ALIGN, FD_LTHASH_VALUE_FOOTPRINT );
841 0 : fd_lthash_zero(<hash_values[0]);
842 :
843 0 : fd_pubkey_hash_pair_t * pairs = fd_accounts_sorted_subrange( funk, 0, 1, &num_pairs, lthash_values, 0, valloc );
844 0 : FD_TEST(NULL != pairs);
845 0 : fd_pubkey_hash_pair_list_t list1 = { .pairs = pairs, .pairs_len = num_pairs };
846 0 : fd_hash_account_deltas( &list1, 1, accounts_hash );
847 0 : fd_valloc_free( valloc, pairs );
848 :
849 0 : fd_lthash_value_t * acc = (fd_lthash_value_t *)fd_type_pun(slot_bank->lthash.lthash);
850 0 : fd_lthash_add( acc, <hash_values[0] );
851 :
852 0 : fd_valloc_free( valloc, lthash_values );
853 0 : } else {
854 0 : ulong num_lists = fd_tpool_worker_cnt( tpool );
855 0 : FD_LOG_NOTICE(( "launching %lu hash tasks", num_lists ));
856 0 : fd_pubkey_hash_pair_list_t lists[num_lists];
857 :
858 0 : fd_lthash_value_t *lthash_values = fd_valloc_malloc( valloc, FD_LTHASH_VALUE_ALIGN, num_lists * FD_LTHASH_VALUE_FOOTPRINT );
859 0 : for( ulong i = 0; i < num_lists; i++ ) {
860 0 : fd_lthash_zero(<hash_values[i]);
861 0 : }
862 :
863 0 : fd_subrange_task_info_t task_info = {
864 0 : .funk = funk,
865 0 : .num_lists = num_lists,
866 0 : .lists = lists,
867 0 : .lthash_values = lthash_values,
868 0 : .valloc = valloc };
869 0 : fd_tpool_exec_all_rrobin( tpool, 0UL, num_lists, fd_accounts_sorted_subrange_task, &task_info, NULL, NULL, 1, 0, num_lists );
870 0 : fd_hash_account_deltas( lists, num_lists, accounts_hash );
871 0 : for( ulong i = 0; i < num_lists; ++i ) {
872 0 : fd_valloc_free( valloc, lists[i].pairs );
873 0 : }
874 0 : fd_lthash_value_t * acc = (fd_lthash_value_t *)fd_type_pun(slot_bank->lthash.lthash);
875 0 : for( ulong i = 0; i < num_lists; i++ ) {
876 0 : fd_lthash_add( acc, <hash_values[i] );
877 0 : }
878 :
879 0 : fd_valloc_free( valloc, lthash_values );
880 0 : }
881 0 : FD_LOG_NOTICE(("accounts_lthash %s", FD_LTHASH_ENC_32_ALLOCA( (fd_lthash_value_t *) slot_bank->lthash.lthash )));
882 :
883 : // fd_accounts_check_lthash( slot_ctx );
884 :
885 0 : FD_LOG_NOTICE(("accounts_hash %s", FD_BASE58_ENC_32_ALLOCA( accounts_hash->hash ) ));
886 :
887 0 : return 0;
888 0 : }
889 :
890 : int
891 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 ) {
892 0 : FD_LOG_NOTICE(("accounts_hash_inc_only start for txn %p, do_hash_verify=%s", (void *)child_txn, do_hash_verify ? "true" : "false" ));
893 :
894 0 : fd_funk_t * funk = slot_ctx->acc_mgr->funk;
895 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
896 0 : fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
897 :
898 : // How many total records are we dealing with?
899 0 : ulong num_iter_accounts = fd_funk_rec_map_key_cnt( rec_map );
900 0 : ulong num_pairs = 0;
901 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) );
902 0 : FD_TEST(NULL != pairs);
903 :
904 0 : fd_blake3_t *b3 = NULL;
905 0 : fd_scratch_push();
906 :
907 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)) {
908 0 : if ( !fd_funk_key_is_acc( rec->pair.key ) || ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) )
909 0 : continue;
910 :
911 0 : fd_account_meta_t * metadata = (fd_account_meta_t *) fd_funk_val_const( rec, wksp );
912 0 : int is_empty = (metadata->info.lamports == 0);
913 :
914 0 : if (is_empty) {
915 0 : pairs[num_pairs].rec = rec;
916 :
917 0 : fd_hash_t * hash = fd_scratch_alloc(alignof(fd_hash_t), sizeof(fd_hash_t));
918 0 : if (NULL == b3)
919 0 : b3 = fd_scratch_alloc(alignof(fd_blake3_t), sizeof(fd_blake3_t));
920 0 : fd_blake3_init ( b3 );
921 0 : fd_blake3_append( b3, rec->pair.key->uc, sizeof( fd_pubkey_t ) );
922 0 : fd_blake3_fini ( b3, hash );
923 :
924 0 : pairs[num_pairs].hash = hash;
925 0 : num_pairs++;
926 0 : continue;
927 0 : } else {
928 0 : fd_hash_t *h = (fd_hash_t *) metadata->hash;
929 0 : if ((h->ul[0] | h->ul[1] | h->ul[2] | h->ul[3]) == 0) {
930 : // By the time we fall into this case, we can assume the ignore_slot feature is enabled...
931 0 : fd_hash_account_current( (uchar *) metadata->hash, NULL, metadata, rec->pair.key->uc, fd_account_get_data(metadata) );
932 0 : } else if( do_hash_verify ) {
933 0 : uchar hash[32];
934 : // ulong old_slot = slot_ctx->slot_bank.slot;
935 : // slot_ctx->slot_bank.slot = metadata->slot;
936 0 : fd_hash_account_current( (uchar *) &hash, NULL, metadata, rec->pair.key->uc, fd_account_get_data(metadata) );
937 : // slot_ctx->slot_bank.slot = old_slot;
938 0 : if ( fd_acc_exists( metadata ) && memcmp( metadata->hash, &hash, 32 ) != 0 ) {
939 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 ) ));
940 0 : }
941 0 : }
942 0 : }
943 :
944 0 : if ((metadata->info.executable & ~1) != 0)
945 0 : continue;
946 :
947 0 : pairs[num_pairs].rec = rec;
948 0 : pairs[num_pairs].hash = (const fd_hash_t *)metadata->hash;
949 0 : num_pairs++;
950 0 : }
951 :
952 0 : sort_pubkey_hash_pair_inplace( pairs, num_pairs );
953 0 : fd_pubkey_hash_pair_list_t list1 = { .pairs = pairs, .pairs_len = num_pairs };
954 0 : fd_hash_account_deltas( &list1, 1, accounts_hash );
955 :
956 0 : fd_valloc_free( slot_ctx->valloc, pairs );
957 0 : fd_scratch_pop();
958 :
959 0 : FD_LOG_INFO(( "accounts_hash %s", FD_BASE58_ENC_32_ALLOCA( accounts_hash->hash) ));
960 :
961 0 : return 0;
962 0 : }
963 :
964 : int
965 : fd_accounts_hash_inc_no_txn( fd_funk_t * funk,
966 : fd_valloc_t valloc,
967 : fd_hash_t * accounts_hash,
968 : fd_funk_rec_key_t const * * pubkeys,
969 : ulong pubkeys_len,
970 0 : ulong do_hash_verify ) {
971 0 : FD_LOG_NOTICE(( "accounts_hash_inc_no_txn" ));
972 :
973 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
974 0 : fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
975 :
976 : /* Pre-allocate the number of pubkey pairs that we are iterating over. */
977 :
978 0 : ulong num_iter_accounts = fd_funk_rec_map_key_cnt( rec_map );
979 0 : ulong num_pairs = 0UL;
980 0 : fd_pubkey_hash_pair_t * pairs = fd_valloc_malloc( valloc,
981 0 : FD_PUBKEY_HASH_PAIR_ALIGN,
982 0 : num_iter_accounts * sizeof(fd_pubkey_hash_pair_t) );
983 :
984 0 : if( FD_UNLIKELY( !pairs ) ) {
985 0 : FD_LOG_ERR(( "failed to allocate memory for pairs" ));
986 0 : }
987 :
988 0 : fd_blake3_t * b3 = NULL;
989 :
990 0 : FD_SCRATCH_SCOPE_BEGIN {
991 :
992 0 : for( ulong i=0UL; i<pubkeys_len; i++ ) {
993 0 : fd_funk_rec_t const * rec = fd_funk_rec_query( funk, NULL, pubkeys[i] );
994 :
995 0 : fd_account_meta_t * metadata = (fd_account_meta_t *) fd_funk_val_const( rec, wksp );
996 0 : int is_empty = (!metadata || metadata->info.lamports == 0);
997 :
998 0 : if( is_empty ) {
999 0 : pairs[num_pairs].rec = rec;
1000 :
1001 0 : fd_hash_t * hash = fd_scratch_alloc( alignof(fd_hash_t), sizeof(fd_hash_t) );
1002 0 : if( !b3 ) {
1003 0 : b3 = fd_scratch_alloc( alignof(fd_blake3_t), sizeof(fd_blake3_t) );
1004 0 : }
1005 0 : fd_blake3_init ( b3 );
1006 0 : fd_blake3_append( b3, rec->pair.key->uc, sizeof(fd_pubkey_t) );
1007 0 : fd_blake3_fini ( b3, hash );
1008 :
1009 0 : pairs[ num_pairs ].hash = hash;
1010 0 : num_pairs++;
1011 0 : continue;
1012 0 : } else {
1013 0 : fd_hash_t *h = (fd_hash_t*)metadata->hash;
1014 0 : if( !(h->ul[ 0 ] | h->ul[ 1 ] | h->ul[ 2 ] | h->ul[ 3 ]) ) {
1015 : // By the time we fall into this case, we can assume the ignore_slot feature is enabled...
1016 0 : fd_hash_account_current( (uchar*)metadata->hash, NULL, metadata, rec->pair.key->uc, fd_account_get_data( metadata ) );
1017 0 : } else if( do_hash_verify ) {
1018 0 : uchar hash[ FD_HASH_FOOTPRINT ];
1019 0 : fd_hash_account_current( (uchar*)&hash, NULL, metadata, rec->pair.key->uc, fd_account_get_data( metadata ) );
1020 0 : if( fd_acc_exists( metadata ) && memcmp( metadata->hash, &hash, FD_HASH_FOOTPRINT ) ) {
1021 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) ));
1022 0 : }
1023 0 : }
1024 0 : }
1025 :
1026 0 : if( (metadata->info.executable & ~1) ) {
1027 0 : continue;
1028 0 : }
1029 :
1030 0 : pairs[ num_pairs ].rec = rec;
1031 0 : pairs[ num_pairs ].hash = (fd_hash_t const *)metadata->hash;
1032 0 : num_pairs++;
1033 0 : }
1034 :
1035 0 : sort_pubkey_hash_pair_inplace( pairs, num_pairs );
1036 0 : fd_pubkey_hash_pair_list_t list1 = { .pairs = pairs, .pairs_len = num_pairs };
1037 0 : fd_hash_account_deltas( &list1, 1, accounts_hash );
1038 :
1039 0 : fd_valloc_free( valloc, pairs );
1040 :
1041 0 : } FD_SCRATCH_SCOPE_END;
1042 :
1043 0 : FD_LOG_INFO(( "accounts_hash %s", FD_BASE58_ENC_32_ALLOCA( accounts_hash->hash) ));
1044 :
1045 0 : return 0;
1046 0 : }
1047 :
1048 : int
1049 0 : fd_snapshot_hash( fd_exec_slot_ctx_t * slot_ctx, fd_tpool_t * tpool, fd_hash_t * accounts_hash, uint check_hash ) {
1050 0 : (void) check_hash;
1051 :
1052 0 : if( fd_should_snapshot_include_epoch_accounts_hash (slot_ctx) ) {
1053 0 : FD_LOG_NOTICE(( "snapshot is including epoch account hash" ));
1054 0 : fd_sha256_t h;
1055 0 : fd_hash_t hash;
1056 0 : fd_accounts_hash( slot_ctx->acc_mgr->funk, &slot_ctx->slot_bank, slot_ctx->valloc, tpool, &hash );
1057 :
1058 0 : fd_sha256_init( &h );
1059 0 : fd_sha256_append( &h, (uchar const *) hash.hash, sizeof( fd_hash_t ) );
1060 0 : fd_sha256_append( &h, (uchar const *) slot_ctx->slot_bank.epoch_account_hash.hash, sizeof( fd_hash_t ) );
1061 0 : fd_sha256_fini( &h, accounts_hash );
1062 :
1063 0 : return 0;
1064 0 : }
1065 0 : return fd_accounts_hash( slot_ctx->acc_mgr->funk, &slot_ctx->slot_bank, slot_ctx->valloc, tpool, accounts_hash );
1066 0 : }
1067 :
1068 : /* TODO: Combine with the above to get correct snapshot hash verification. */
1069 :
1070 : int
1071 : fd_snapshot_service_hash( fd_hash_t * accounts_hash,
1072 : fd_hash_t * snapshot_hash,
1073 : fd_slot_bank_t * slot_bank,
1074 : fd_epoch_bank_t * epoch_bank,
1075 : fd_funk_t * funk,
1076 : fd_tpool_t * tpool,
1077 0 : fd_valloc_t valloc ) {
1078 :
1079 0 : fd_sha256_t h;
1080 0 : fd_accounts_hash( funk, slot_bank, valloc, tpool, accounts_hash );
1081 :
1082 0 : int should_include_eah = epoch_bank->eah_stop_slot != ULONG_MAX && epoch_bank->eah_start_slot == ULONG_MAX;
1083 :
1084 0 : if( should_include_eah ) {
1085 0 : fd_sha256_init( &h );
1086 0 : fd_sha256_append( &h, (uchar const *) accounts_hash, sizeof( fd_hash_t ) );
1087 0 : fd_sha256_append( &h, (uchar const *) slot_bank->epoch_account_hash.hash, sizeof( fd_hash_t ) );
1088 0 : fd_sha256_fini( &h, snapshot_hash );
1089 0 : } else {
1090 0 : fd_memcpy( snapshot_hash, accounts_hash, sizeof(fd_hash_t) );
1091 0 : }
1092 :
1093 0 : return 0;
1094 0 : }
1095 :
1096 : int
1097 : fd_snapshot_service_inc_hash( fd_hash_t * accounts_hash,
1098 : fd_hash_t * snapshot_hash,
1099 : fd_slot_bank_t * slot_bank,
1100 : fd_epoch_bank_t * epoch_bank,
1101 : fd_funk_t * funk,
1102 : fd_funk_rec_key_t const * * pubkeys,
1103 : ulong pubkeys_len,
1104 0 : fd_valloc_t valloc ) {
1105 :
1106 0 : fd_sha256_t h;
1107 0 : fd_accounts_hash_inc_no_txn( funk, valloc, accounts_hash, pubkeys, pubkeys_len, 0UL );
1108 :
1109 0 : int should_include_eah = epoch_bank->eah_stop_slot != ULONG_MAX && epoch_bank->eah_start_slot == ULONG_MAX;
1110 :
1111 0 : if( should_include_eah ) {
1112 0 : fd_sha256_init( &h );
1113 0 : fd_sha256_append( &h, (uchar const *) accounts_hash, sizeof( fd_hash_t ) );
1114 0 : fd_sha256_append( &h, (uchar const *) slot_bank->epoch_account_hash.hash, sizeof( fd_hash_t ) );
1115 0 : fd_sha256_fini( &h, snapshot_hash );
1116 0 : } else {
1117 0 : fd_memcpy( snapshot_hash, accounts_hash, sizeof(fd_hash_t) );
1118 0 : }
1119 :
1120 0 : return 0;
1121 0 : }
1122 :
1123 : /* Re-computes the lthash from the current slot */
1124 : void
1125 : fd_accounts_check_lthash( fd_funk_t * funk,
1126 : fd_funk_txn_t * funk_txn,
1127 : fd_slot_bank_t * slot_bank,
1128 0 : fd_valloc_t valloc ) {
1129 :
1130 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
1131 0 : fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
1132 0 : fd_funk_txn_t * txn_map = fd_funk_txn_map( funk, wksp );
1133 :
1134 : // How many txns are we dealing with?
1135 0 : ulong txn_cnt = 1;
1136 0 : fd_funk_txn_t * txn = funk_txn;
1137 0 : while (NULL != txn) {
1138 0 : txn_cnt++;
1139 0 : txn = fd_funk_txn_parent( txn, txn_map );
1140 0 : }
1141 :
1142 0 : fd_funk_txn_t ** txns = fd_alloca_check(sizeof(fd_funk_txn_t *), sizeof(fd_funk_txn_t *) * txn_cnt);
1143 0 : if ( FD_UNLIKELY(NULL == txns))
1144 0 : FD_LOG_ERR(("Out of scratch space?"));
1145 :
1146 : // Lay it flat to make it easier to walk backwards up the chain from
1147 : // the root
1148 0 : txn = funk_txn;
1149 0 : ulong txn_idx = txn_cnt;
1150 0 : while (1) {
1151 0 : txns[--txn_idx] = txn;
1152 0 : if (NULL == txn)
1153 0 : break;
1154 0 : txn = fd_funk_txn_parent( txn, txn_map );
1155 0 : }
1156 :
1157 : // How many total records are we dealing with?
1158 0 : ulong num_iter_accounts = fd_funk_rec_map_key_cnt( rec_map );
1159 :
1160 0 : int accounts_hash_slots = fd_ulong_find_msb(num_iter_accounts ) + 1;
1161 :
1162 0 : FD_LOG_WARNING(("allocating memory for hash. num_iter_accounts: %lu slots: %d", num_iter_accounts, accounts_hash_slots));
1163 0 : void * hashmem = fd_valloc_malloc( valloc, accounts_hash_align(), accounts_hash_footprint(accounts_hash_slots));
1164 0 : FD_LOG_WARNING(("initializing memory for hash"));
1165 0 : accounts_hash_t * hash_map = accounts_hash_join(accounts_hash_new(hashmem, accounts_hash_slots));
1166 :
1167 0 : FD_LOG_WARNING(("copying in accounts"));
1168 :
1169 : // walk up the transactions...
1170 0 : for (ulong idx = 0; idx < txn_cnt; idx++) {
1171 0 : FD_LOG_WARNING(("txn idx %lu", idx));
1172 0 : for (fd_funk_rec_t const *rec = fd_funk_txn_first_rec( funk, txns[idx]);
1173 0 : NULL != rec;
1174 0 : rec = fd_funk_txn_next_rec(funk, rec)) {
1175 0 : if ( fd_funk_key_is_acc( rec->pair.key ) && !( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) {
1176 0 : accounts_hash_t * q = accounts_hash_query(hash_map, (fd_funk_rec_t *) rec, NULL);
1177 0 : if (NULL != q)
1178 0 : accounts_hash_remove(hash_map, q);
1179 0 : if (!(rec->flags & FD_FUNK_REC_FLAG_ERASE))
1180 0 : accounts_hash_insert(hash_map, (fd_funk_rec_t *) rec);
1181 0 : }
1182 0 : }
1183 0 : }
1184 :
1185 0 : FD_LOG_WARNING(("assumulating a new lthash"));
1186 :
1187 : // Initialize the accumulator to zero
1188 0 : fd_lthash_value_t acc_lthash;
1189 0 : fd_lthash_zero( &acc_lthash );
1190 :
1191 0 : ulong slot_cnt = accounts_hash_slot_cnt(hash_map);;
1192 0 : for( ulong slot_idx=0UL; slot_idx<slot_cnt; slot_idx++ ) {
1193 0 : accounts_hash_t *slot = &hash_map[slot_idx];
1194 0 : if (FD_UNLIKELY (NULL != slot->key)) {
1195 0 : void const * data = fd_funk_val_const( slot->key, wksp );
1196 0 : fd_account_meta_t * metadata = (fd_account_meta_t *)fd_type_pun_const( data );
1197 0 : if( FD_UNLIKELY(metadata->info.lamports != 0) ) {
1198 0 : uchar * acc_data = fd_account_get_data(metadata);
1199 0 : uchar hash [ 32 ];
1200 0 : fd_lthash_value_t new_lthash_value;
1201 0 : fd_lthash_zero(&new_lthash_value);
1202 0 : fd_hash_account_current( hash, &new_lthash_value, metadata, slot->key->pair.key[0].uc, acc_data );
1203 0 : fd_lthash_add( &acc_lthash, &new_lthash_value );
1204 :
1205 0 : if (fd_acc_exists( metadata ) && memcmp( metadata->hash, &hash, 32 ) != 0 ) {
1206 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 ) ));
1207 0 : }
1208 0 : }
1209 0 : }
1210 0 : }
1211 :
1212 : // Compare the accumulator to the slot
1213 0 : fd_lthash_value_t * acc = (fd_lthash_value_t *)fd_type_pun_const( slot_bank->lthash.lthash );
1214 0 : if ( memcmp( acc, &acc_lthash, sizeof( fd_lthash_value_t ) ) == 0 ) {
1215 0 : FD_LOG_NOTICE(("accounts_lthash %s == %s", FD_LTHASH_ENC_32_ALLOCA (acc), FD_LTHASH_ENC_32_ALLOCA (&acc_lthash)));
1216 0 : } else {
1217 0 : FD_LOG_ERR(("accounts_lthash %s != %s", FD_LTHASH_ENC_32_ALLOCA (acc), FD_LTHASH_ENC_32_ALLOCA (&acc_lthash)));
1218 0 : }
1219 0 : }
|