Line data Source code
1 : #include "fd_sysvar_epoch_rewards.h" 2 : #include "fd_sysvar.h" 3 : #include "../fd_acc_mgr.h" 4 : #include "../fd_borrowed_account.h" 5 : #include "../fd_system_ids.h" 6 : #include "../context/fd_exec_slot_ctx.h" 7 : #include "../context/fd_exec_epoch_ctx.h" 8 : 9 : static void 10 1845 : write_epoch_rewards( fd_exec_slot_ctx_t * slot_ctx, fd_sysvar_epoch_rewards_t * epoch_rewards ) { 11 1845 : ulong sz = fd_sysvar_epoch_rewards_size( epoch_rewards ); 12 1845 : uchar enc[sz]; 13 1845 : fd_memset( enc, 0, sz ); 14 1845 : fd_bincode_encode_ctx_t ctx; 15 1845 : ctx.data = enc; 16 1845 : ctx.dataend = enc + sz; 17 1845 : if ( fd_sysvar_epoch_rewards_encode( epoch_rewards, &ctx ) ) { 18 0 : FD_LOG_ERR(("fd_sysvar_epoch_rewards_encode failed")); 19 0 : } 20 : 21 1845 : fd_sysvar_set( slot_ctx, fd_sysvar_owner_id.key, &fd_sysvar_epoch_rewards_id, enc, sz, slot_ctx->slot_bank.slot ); 22 1845 : } 23 : 24 : fd_sysvar_epoch_rewards_t * 25 : fd_sysvar_epoch_rewards_read( 26 : fd_sysvar_epoch_rewards_t * result, 27 : fd_exec_slot_ctx_t const * slot_ctx 28 0 : ) { 29 0 : fd_sysvar_epoch_rewards_t const * ret = fd_sysvar_cache_epoch_rewards( slot_ctx->sysvar_cache ); 30 0 : if( FD_UNLIKELY( NULL != ret ) ) { 31 0 : fd_memcpy(result, ret, sizeof(fd_sysvar_epoch_rewards_t)); 32 0 : return result; 33 0 : } 34 : 35 0 : FD_BORROWED_ACCOUNT_DECL(acc); 36 0 : int err = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &fd_sysvar_epoch_rewards_id, acc ); 37 0 : if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) { 38 0 : return NULL; 39 0 : } 40 : 41 0 : fd_bincode_decode_ctx_t decode = 42 0 : { .data = acc->const_data, 43 0 : .dataend = acc->const_data + acc->const_meta->dlen, 44 0 : .valloc = {0} /* valloc not required */ }; 45 : 46 0 : if( FD_UNLIKELY( fd_sysvar_epoch_rewards_decode( result, &decode )!=FD_BINCODE_SUCCESS ) ) 47 0 : return NULL; 48 : 49 0 : return result; 50 0 : } 51 : 52 : /* Since there are multiple sysvar epoch rewards updates within a single slot, 53 : we need to ensure that the cache stays updated after each change (versus with other 54 : sysvars which only get updated once per slot and then synced up after) */ 55 : void 56 : fd_sysvar_epoch_rewards_distribute( 57 : fd_exec_slot_ctx_t * slot_ctx, 58 : ulong distributed 59 0 : ) { 60 0 : fd_sysvar_epoch_rewards_t epoch_rewards[1]; 61 0 : if ( FD_UNLIKELY( fd_sysvar_epoch_rewards_read( epoch_rewards, slot_ctx ) == NULL ) ) { 62 0 : FD_LOG_ERR(( "failed to read sysvar epoch rewards" )); 63 0 : } 64 0 : FD_TEST( epoch_rewards->active ); 65 : 66 0 : FD_TEST( fd_ulong_sat_add( epoch_rewards->distributed_rewards, distributed ) <= epoch_rewards->total_rewards ); 67 : 68 0 : epoch_rewards->distributed_rewards += distributed; 69 : 70 0 : write_epoch_rewards( slot_ctx, epoch_rewards ); 71 : 72 : /* Sync the epoch rewards sysvar cache entry with the account */ 73 0 : fd_sysvar_cache_restore_epoch_rewards( slot_ctx->sysvar_cache, slot_ctx->acc_mgr, slot_ctx->funk_txn ); 74 0 : } 75 : 76 : void 77 : fd_sysvar_epoch_rewards_set_inactive( 78 : fd_exec_slot_ctx_t * slot_ctx 79 0 : ) { 80 0 : fd_sysvar_epoch_rewards_t epoch_rewards[1]; 81 0 : if ( FD_UNLIKELY( fd_sysvar_epoch_rewards_read( epoch_rewards, slot_ctx ) == NULL ) ) { 82 0 : FD_LOG_ERR(( "failed to read sysvar epoch rewards" )); 83 0 : } 84 : 85 0 : if ( FD_LIKELY( FD_FEATURE_ACTIVE( slot_ctx, partitioned_epoch_rewards_superfeature ) ) ) { 86 0 : FD_TEST( epoch_rewards->total_rewards >= epoch_rewards->distributed_rewards ); 87 0 : } else { 88 0 : FD_TEST( epoch_rewards->total_rewards == epoch_rewards->distributed_rewards ); 89 0 : } 90 : 91 : 92 0 : epoch_rewards->active = 0; 93 : 94 0 : write_epoch_rewards( slot_ctx, epoch_rewards ); 95 : 96 : /* Sync the epoch rewards sysvar cache entry with the account */ 97 0 : fd_sysvar_cache_restore_epoch_rewards( slot_ctx->sysvar_cache, slot_ctx->acc_mgr, slot_ctx->funk_txn ); 98 0 : } 99 : 100 : /* Create EpochRewards syavar with calculated rewards 101 : 102 : https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/sysvar.rs#L25 */ 103 : void 104 : fd_sysvar_epoch_rewards_init( 105 : fd_exec_slot_ctx_t * slot_ctx, 106 : ulong total_rewards, 107 : ulong distributed_rewards, 108 : ulong distribution_starting_block_height, 109 : ulong num_partitions, 110 : fd_point_value_t point_value, 111 : const fd_hash_t * last_blockhash 112 1845 : ) { 113 1845 : FD_TEST( total_rewards >= distributed_rewards ); 114 : 115 1845 : fd_sysvar_epoch_rewards_t epoch_rewards = { 116 1845 : .distribution_starting_block_height = distribution_starting_block_height, 117 1845 : .num_partitions = num_partitions, 118 1845 : .total_points = point_value.points, 119 1845 : .total_rewards = total_rewards, 120 1845 : .distributed_rewards = distributed_rewards, 121 1845 : .active = 1 122 1845 : }; 123 : 124 : /* On clusters where partitioned_epoch_rewards_superfeature is enabled, we should use point_value.rewards. 125 : On other clusters, including those where enable_partitioned_epoch_reward is enabled, we should use total_rewards. 126 : 127 : https://github.com/anza-xyz/agave/blob/b9c9ecccbb05d9da774d600bdbef2cf210c57fa8/runtime/src/bank/partitioned_epoch_rewards/sysvar.rs#L36-L43 */ 128 1845 : if ( FD_LIKELY( FD_FEATURE_ACTIVE( slot_ctx, partitioned_epoch_rewards_superfeature ) ) ) { 129 9 : epoch_rewards.total_rewards = point_value.rewards; 130 9 : } 131 : 132 1845 : fd_memcpy( &epoch_rewards.parent_blockhash, last_blockhash, FD_HASH_FOOTPRINT ); 133 : 134 1845 : write_epoch_rewards( slot_ctx, &epoch_rewards ); 135 1845 : }