Line data Source code
1 : #include "fd_sysvar_recent_hashes.h" 2 : #include "../fd_acc_mgr.h" 3 : #include "fd_sysvar.h" 4 : #include "../context/fd_exec_slot_ctx.h" 5 : #include "../fd_system_ids.h" 6 : 7 : /* Skips fd_types encoding preflight checks and directly serializes the 8 : blockhash queue into a buffer representing account data for the 9 : recent blockhashes sysvar. */ 10 : 11 : static void 12 : encode_rbh_from_blockhash_queue( fd_exec_slot_ctx_t * slot_ctx, 13 453 : uchar out_mem[ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] ) { 14 453 : fd_blockhashes_t const * bhq = fd_bank_block_hash_queue_query( slot_ctx->bank ); 15 : 16 453 : ulong queue_sz = fd_blockhash_deq_cnt( bhq->d.deque ); 17 453 : ulong out_max = fd_ulong_min( queue_sz, FD_SYSVAR_RECENT_HASHES_CAP ); 18 : 19 453 : uchar * enc = out_mem; 20 453 : fd_memcpy( enc, &out_max, sizeof(ulong) ); 21 : 22 453 : enc += sizeof(ulong); 23 : 24 : /* Iterate over blockhash queue and encode the recent blockhashes. 25 : We can do direct memcpying and avoid redundant checks from fd_types 26 : encoders since the enc buffer is already sized out to the 27 : worst-case bound. */ 28 453 : ulong out_idx = 0UL; 29 453 : for( fd_blockhash_deq_iter_t iter = fd_blockhash_deq_iter_init_rev( bhq->d.deque ); 30 34428 : out_idx<FD_SYSVAR_RECENT_HASHES_CAP && 31 34428 : !fd_blockhash_deq_iter_done_rev( bhq->d.deque, iter ); 32 33975 : out_idx++, iter = fd_blockhash_deq_iter_prev( bhq->d.deque, iter ) ) { 33 33975 : fd_blockhash_info_t const * n = fd_blockhash_deq_iter_ele_const( bhq->d.deque, iter ); 34 33975 : fd_memcpy( enc, n->hash.uc, 32 ); 35 33975 : FD_STORE( ulong, enc+32, n->fee_calculator.lamports_per_signature ); 36 33975 : enc += 40; 37 33975 : } 38 453 : } 39 : 40 : void 41 3 : fd_sysvar_recent_hashes_init( fd_exec_slot_ctx_t * slot_ctx ) { 42 3 : uchar enc[ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] = {0}; 43 3 : encode_rbh_from_blockhash_queue( slot_ctx, enc ); 44 3 : fd_sysvar_account_update( slot_ctx, &fd_sysvar_recent_block_hashes_id, enc, FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ); 45 3 : } 46 : 47 : // https://github.com/anza-xyz/agave/blob/e8750ba574d9ac7b72e944bc1227dc7372e3a490/accounts-db/src/blockhash_queue.rs#L113 48 : static void 49 : register_blockhash( fd_exec_slot_ctx_t * slot_ctx, 50 450 : fd_hash_t const * hash ) { 51 450 : fd_blockhashes_t * bhq = fd_bank_block_hash_queue_modify( slot_ctx->bank ); 52 450 : fd_blockhash_info_t * bh = fd_blockhashes_push_new( bhq, hash ); 53 450 : bh->fee_calculator = (fd_fee_calculator_t){ 54 450 : .lamports_per_signature = fd_bank_lamports_per_signature_get( slot_ctx->bank ) 55 450 : }; 56 450 : } 57 : 58 : /* This implementation is more consistent with Agave's bank implementation for updating the block hashes sysvar: 59 : 1. Update the block hash queue with the latest poh 60 : 2. Take the first 150 blockhashes from the queue (or fewer if there are) 61 : 3. Manually serialize the recent blockhashes 62 : 4. Set the sysvar account with the new data */ 63 : void 64 450 : fd_sysvar_recent_hashes_update( fd_exec_slot_ctx_t * slot_ctx ) { 65 450 : register_blockhash( slot_ctx, fd_bank_poh_query( slot_ctx->bank ) ); 66 : 67 450 : uchar enc[ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] = {0}; 68 450 : encode_rbh_from_blockhash_queue( slot_ctx, enc ); 69 450 : fd_sysvar_account_update( slot_ctx, &fd_sysvar_recent_block_hashes_id, enc, sizeof(enc) ); 70 450 : } 71 : 72 : fd_recent_block_hashes_t * 73 0 : fd_sysvar_recent_hashes_read( fd_funk_t * funk, fd_funk_txn_t * funk_txn, fd_spad_t * spad ) { 74 0 : FD_TXN_ACCOUNT_DECL( acc ); 75 0 : int err = fd_txn_account_init_from_funk_readonly( acc, &fd_sysvar_recent_block_hashes_id, funk, funk_txn ); 76 0 : if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) 77 0 : return NULL; 78 : 79 0 : fd_bincode_decode_ctx_t ctx = { 80 0 : .data = fd_txn_account_get_data( acc ), 81 0 : .dataend = fd_txn_account_get_data( acc ) + fd_txn_account_get_data_len( acc ), 82 0 : }; 83 : 84 : /* This check is needed as a quirk of the fuzzer. If a sysvar account 85 : exists in the accounts database, but doesn't have any lamports, 86 : this means that the account does not exist. This wouldn't happen 87 : in a real execution environment. */ 88 0 : if( FD_UNLIKELY( fd_txn_account_get_lamports( acc )==0UL ) ) { 89 0 : return NULL; 90 0 : } 91 : 92 0 : ulong total_sz = 0; 93 0 : err = fd_recent_block_hashes_decode_footprint( &ctx, &total_sz ); 94 0 : if( FD_UNLIKELY( err ) ) { 95 0 : return NULL; 96 0 : } 97 : 98 0 : uchar * mem = fd_spad_alloc( spad, fd_recent_block_hashes_align(), total_sz ); 99 0 : if( FD_UNLIKELY( !mem ) ) { 100 0 : FD_LOG_CRIT(( "fd_spad_alloc failed" )); 101 0 : } 102 : 103 : /* This would never happen in a real cluster, this is a workaround 104 : for fuzz-generated cases where sysvar accounts are not funded. */ 105 0 : if( FD_UNLIKELY( fd_txn_account_get_lamports( acc ) == 0 ) ) { 106 0 : return NULL; 107 0 : } 108 : 109 0 : return fd_recent_block_hashes_decode( mem, &ctx ); 110 0 : }