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