Line data Source code
1 : #include <stdio.h> 2 : #include "../fd_acc_mgr.h" 3 : #include "../fd_hashes.h" 4 : #include "fd_sysvar.h" 5 : #include "../fd_runtime.h" 6 : #include "../fd_system_ids.h" 7 : #include "../context/fd_exec_slot_ctx.h" 8 : 9 0 : #define FD_RECENT_BLOCKHASHES_ACCOUNT_MAX_SIZE sizeof(ulong) + FD_RECENT_BLOCKHASHES_MAX_ENTRIES * (sizeof(fd_hash_t) + sizeof(ulong)) 10 : 11 : // run --ledger /home/jsiegel/test-ledger --db /home/jsiegel/funk --cmd accounts --accounts /home/jsiegel/test-ledger/accounts/ --pages 15 --index-max 120000000 --start-slot 2 --end-slot 2 --start-id 35 --end-id 37 12 : // run --ledger /home/jsiegel/test-ledger --db /home/jsiegel/funk --cmd replay --pages 15 --index-max 120000000 --start-slot 0 --end-slot 3 13 : 14 : // {meta = {write_version_obsolete = 137, 15 : // data_len = 6008, pubkey = "\006\247\325\027\031,V\216\340\212\204_sҗ\210\317\003\\1E\262\032\263D\330\006.\251@\000"}, info = {lamports = 42706560, rent_epoch = 0, owner = "\006\247\325\027\030u\367)\307=\223@\217!a \006~،v\340\214(\177\301\224`\000\000\000", executable = 0 '\000', padding = "K\000\f\376\177\000"}, hash = {value = "\302Q\316\035qTY\347\352]\260\335\213\224R\227ԯ\366R\273\063H\345֑c\377\207/k\275"}} 16 : 17 : // owner: Sysvar1111111111111111111111111111111111111 pubkey: SysvarRecentB1ockHashes11111111111111111111 hash: E5YSehyvJ7xXcNnQjWCH9UhMJ1dxDBJ1RuuPh1Y3RZgg file: /home/jsiegel/test-ledger/accounts//2.37 18 : // {blockhash = JCidNXtcMXMWQwMDM3ZQq5pxaw3hQpNbeHg1KcstjuF4, fee_calculator={lamports_per_signature = 5000}} 19 : // {blockhash = GQN3oV8G1Ra3GCX76dE1YYJ6UjMyDreNCEWM4tZ39zj1, fee_calculator={lamports_per_signature = 5000}} 20 : // {blockhash = Ha5DVgnD1xSA8oQc337jtA3atEfQ4TFX1ajeZG1Y2tUx, fee_calculator={lamports_per_signature = 0}} 21 : 22 : /* Skips fd_types encoding preflight checks and directly serializes the blockhash queue into a buffer representing 23 : account data for the recent blockhashes sysvar. */ 24 : static void 25 0 : encode_rbh_from_blockhash_queue( fd_exec_slot_ctx_t * slot_ctx, uchar * enc ) { 26 : /* recent_blockhashes_account::update_account's `take` call takes at most 150 elements 27 : https://github.com/anza-xyz/agave/blob/v2.1.6/runtime/src/bank/recent_blockhashes_account.rs#L15-L28 */ 28 0 : fd_block_hash_queue_t const * queue = &slot_ctx->slot_bank.block_hash_queue; 29 0 : ulong queue_sz = fd_hash_hash_age_pair_t_map_size( queue->ages_pool, queue->ages_root ); 30 0 : ulong hashes_len = fd_ulong_min( queue_sz, FD_RECENT_BLOCKHASHES_MAX_ENTRIES ); 31 0 : fd_memcpy( enc, &hashes_len, sizeof(ulong) ); 32 0 : enc += sizeof(ulong); 33 : 34 : /* Iterate over blockhash queue and encode the recent blockhashes. We can do direct memcpying 35 : and avoid redundant checks from fd_types encoders since the enc buffer is already sized out to 36 : the worst-case bound. */ 37 0 : fd_hash_hash_age_pair_t_mapnode_t const * nn; 38 0 : for( fd_hash_hash_age_pair_t_mapnode_t const * n = fd_hash_hash_age_pair_t_map_minimum_const( queue->ages_pool, queue->ages_root ); n; n = nn ) { 39 0 : nn = fd_hash_hash_age_pair_t_map_successor_const( queue->ages_pool, n ); 40 0 : ulong enc_idx = queue->last_hash_index - n->elem.val.hash_index; 41 0 : if( enc_idx>=hashes_len ) { 42 0 : continue; 43 0 : } 44 0 : fd_hash_t hash = n->elem.key; 45 0 : ulong lps = n->elem.val.fee_calculator.lamports_per_signature; 46 : 47 0 : fd_memcpy( enc + enc_idx * (FD_HASH_FOOTPRINT + sizeof(ulong)), &hash, FD_HASH_FOOTPRINT ); 48 0 : fd_memcpy( enc + enc_idx * (FD_HASH_FOOTPRINT + sizeof(ulong)) + sizeof(fd_hash_t), &lps, sizeof(ulong) ); 49 0 : } 50 0 : } 51 : 52 : // https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/fee_calculator.rs#L110 53 : void 54 : fd_sysvar_recent_hashes_init( fd_exec_slot_ctx_t * slot_ctx, 55 0 : fd_spad_t * runtime_spad ) { 56 : 57 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) { 58 : 59 0 : if( slot_ctx->slot_bank.slot != 0 ) { 60 0 : return; 61 0 : } 62 : 63 0 : ulong sz = FD_RECENT_BLOCKHASHES_ACCOUNT_MAX_SIZE; 64 0 : uchar * enc = fd_spad_alloc( runtime_spad, FD_SPAD_ALIGN, sz ); 65 0 : fd_memset( enc, 0, sz ); 66 0 : encode_rbh_from_blockhash_queue( slot_ctx, enc ); 67 0 : fd_sysvar_set( slot_ctx, fd_sysvar_owner_id.key, &fd_sysvar_recent_block_hashes_id, enc, sz, slot_ctx->slot_bank.slot ); 68 : 69 0 : } FD_SPAD_FRAME_END; 70 0 : } 71 : 72 : // https://github.com/anza-xyz/agave/blob/e8750ba574d9ac7b72e944bc1227dc7372e3a490/accounts-db/src/blockhash_queue.rs#L113 73 : static void 74 0 : register_blockhash( fd_exec_slot_ctx_t * slot_ctx, fd_hash_t const * hash ) { 75 0 : fd_block_hash_queue_t * queue = &slot_ctx->slot_bank.block_hash_queue; 76 : // https://github.com/anza-xyz/agave/blob/e8750ba574d9ac7b72e944bc1227dc7372e3a490/accounts-db/src/blockhash_queue.rs#L114 77 0 : queue->last_hash_index++; 78 0 : if ( fd_hash_hash_age_pair_t_map_size( queue->ages_pool, queue->ages_root ) >= queue->max_age ) { 79 0 : fd_hash_hash_age_pair_t_mapnode_t * nn; 80 0 : for ( fd_hash_hash_age_pair_t_mapnode_t * n = fd_hash_hash_age_pair_t_map_minimum( queue->ages_pool, queue->ages_root ); n; n = nn ) { 81 0 : nn = fd_hash_hash_age_pair_t_map_successor( queue->ages_pool, n ); 82 : /* NOTE: Yes, this check is incorrect. It should be >= which caps the blockhash queue at max_age 83 : entries, but instead max_age + 1 entries are allowed to exist in the queue at once. This mimics 84 : Agave to stay conformant with their implementation. 85 : https://github.com/anza-xyz/agave/blob/e8750ba574d9ac7b72e944bc1227dc7372e3a490/accounts-db/src/blockhash_queue.rs#L109 */ 86 0 : if ( queue->last_hash_index - n->elem.val.hash_index > queue->max_age ) { 87 0 : fd_hash_hash_age_pair_t_map_remove( queue->ages_pool, &queue->ages_root, n ); 88 0 : fd_hash_hash_age_pair_t_map_release( queue->ages_pool, n ); 89 0 : } 90 0 : } 91 0 : } 92 : 93 0 : fd_hash_hash_age_pair_t_mapnode_t * node = fd_hash_hash_age_pair_t_map_acquire( queue->ages_pool ); 94 0 : node->elem = (fd_hash_hash_age_pair_t){ 95 0 : .key = *hash, 96 0 : .val = (fd_hash_age_t){ .hash_index = queue->last_hash_index, .fee_calculator = (fd_fee_calculator_t){.lamports_per_signature = slot_ctx->slot_bank.lamports_per_signature}, .timestamp = (ulong)fd_log_wallclock() } 97 0 : }; 98 : // https://github.com/anza-xyz/agave/blob/e8750ba574d9ac7b72e944bc1227dc7372e3a490/accounts-db/src/blockhash_queue.rs#L121-L128 99 0 : fd_hash_hash_age_pair_t_map_insert( slot_ctx->slot_bank.block_hash_queue.ages_pool, &slot_ctx->slot_bank.block_hash_queue.ages_root, node ); 100 : // https://github.com/anza-xyz/agave/blob/e8750ba574d9ac7b72e944bc1227dc7372e3a490/accounts-db/src/blockhash_queue.rs#L130 101 0 : fd_memcpy( queue->last_hash, hash, sizeof(fd_hash_t) ); 102 0 : } 103 : 104 : /* This implementation is more consistent with Agave's bank implementation for updating the block hashes sysvar: 105 : 1. Update the block hash queue with the latest poh 106 : 2. Take the first 150 blockhashes from the queue (or fewer if there are) 107 : 3. Manually serialize the recent blockhashes 108 : 4. Set the sysvar account with the new data */ 109 : void 110 0 : fd_sysvar_recent_hashes_update( fd_exec_slot_ctx_t * slot_ctx, fd_spad_t * runtime_spad ) { 111 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) { 112 : /* Update the blockhash queue */ 113 0 : register_blockhash( slot_ctx, &slot_ctx->slot_bank.poh ); 114 : 115 : /* Derive the new sysvar recent blockhashes from the blockhash queue */ 116 0 : ulong sz = FD_RECENT_BLOCKHASHES_ACCOUNT_MAX_SIZE; 117 0 : uchar * enc = fd_spad_alloc( runtime_spad, FD_SPAD_ALIGN, sz ); 118 0 : uchar * enc_start = enc; 119 0 : fd_memset( enc, 0, sz ); 120 : 121 : /* Encode the recent blockhashes */ 122 0 : encode_rbh_from_blockhash_queue( slot_ctx, enc ); 123 : 124 : /* Set the sysvar from the encoded data */ 125 0 : fd_sysvar_set( slot_ctx, 126 0 : fd_sysvar_owner_id.key, 127 0 : &fd_sysvar_recent_block_hashes_id, 128 0 : enc_start, 129 0 : sz, 130 0 : slot_ctx->slot_bank.slot ); 131 0 : } FD_SPAD_FRAME_END; 132 0 : }