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