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_txn_account.h"
5 : #include "../fd_system_ids.h"
6 : #include "../../accdb/fd_accdb_impl_v1.h"
7 :
8 : static void
9 : write_epoch_rewards( fd_bank_t * bank,
10 : fd_accdb_user_t * accdb,
11 : fd_funk_txn_xid_t const * xid,
12 : fd_capture_ctx_t * capture_ctx,
13 0 : fd_sysvar_epoch_rewards_t * epoch_rewards ) {
14 0 : ulong sz = fd_sysvar_epoch_rewards_size( epoch_rewards );
15 0 : uchar enc[sz];
16 0 : fd_memset( enc, 0, sz );
17 0 : fd_bincode_encode_ctx_t ctx = {
18 0 : .data = enc,
19 0 : .dataend = enc + sz
20 0 : };
21 0 : if( FD_UNLIKELY( fd_sysvar_epoch_rewards_encode( epoch_rewards, &ctx ) ) ) {
22 0 : FD_LOG_ERR(( "fd_sysvar_epoch_rewards_encode failed" ));
23 0 : }
24 :
25 0 : fd_sysvar_account_update( bank, accdb, xid, capture_ctx, &fd_sysvar_epoch_rewards_id, enc, sz );
26 0 : }
27 :
28 : fd_sysvar_epoch_rewards_t *
29 : fd_sysvar_epoch_rewards_read( fd_funk_t * funk,
30 : fd_funk_txn_xid_t const * xid,
31 0 : fd_sysvar_epoch_rewards_t * out ) {
32 0 : fd_txn_account_t acc[1];
33 0 : int err = fd_txn_account_init_from_funk_readonly( acc, &fd_sysvar_epoch_rewards_id, funk, xid );
34 0 : if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
35 0 : return NULL;
36 0 : }
37 :
38 : /* This check is needed as a quirk of the fuzzer. If a sysvar account
39 : exists in the accounts database, but doesn't have any lamports,
40 : this means that the account does not exist. This wouldn't happen
41 : in a real execution environment. */
42 0 : if( FD_UNLIKELY( fd_txn_account_get_lamports( acc )==0UL ) ) {
43 0 : return NULL;
44 0 : }
45 :
46 0 : return fd_bincode_decode_static(
47 0 : sysvar_epoch_rewards, out,
48 0 : fd_txn_account_get_data( acc ),
49 0 : fd_txn_account_get_data_len( acc ),
50 0 : &err );
51 0 : }
52 :
53 : /* Since there are multiple sysvar epoch rewards updates within a single slot,
54 : we need to ensure that the cache stays updated after each change (versus with other
55 : sysvars which only get updated once per slot and then synced up after) */
56 : void
57 : fd_sysvar_epoch_rewards_distribute( fd_bank_t * bank,
58 : fd_accdb_user_t * accdb,
59 : fd_funk_txn_xid_t const * xid,
60 : fd_capture_ctx_t * capture_ctx,
61 0 : ulong distributed ) {
62 0 : fd_funk_t * funk = fd_accdb_user_v1_funk( accdb );
63 0 : fd_sysvar_epoch_rewards_t epoch_rewards[1];
64 0 : if( FD_UNLIKELY( !fd_sysvar_epoch_rewards_read( funk, xid, epoch_rewards ) ) ) {
65 0 : FD_LOG_ERR(( "failed to read sysvar epoch rewards" ));
66 0 : }
67 :
68 0 : if( FD_UNLIKELY( !epoch_rewards->active ) ) {
69 0 : FD_LOG_ERR(( "sysvar epoch rewards is not active" ));
70 0 : }
71 :
72 0 : if( FD_UNLIKELY( fd_ulong_sat_add( epoch_rewards->distributed_rewards, distributed ) > epoch_rewards->total_rewards ) ) {
73 0 : FD_LOG_ERR(( "distributed rewards overflow" ));
74 0 : }
75 :
76 0 : epoch_rewards->distributed_rewards += distributed;
77 :
78 0 : write_epoch_rewards( bank, accdb, xid, capture_ctx, epoch_rewards );
79 0 : }
80 :
81 : void
82 : fd_sysvar_epoch_rewards_set_inactive( fd_bank_t * bank,
83 : fd_accdb_user_t * accdb,
84 : fd_funk_txn_xid_t const * xid,
85 0 : fd_capture_ctx_t * capture_ctx ) {
86 0 : fd_funk_t * funk = fd_accdb_user_v1_funk( accdb );
87 0 : fd_sysvar_epoch_rewards_t epoch_rewards[1];
88 0 : if( FD_UNLIKELY( !fd_sysvar_epoch_rewards_read( funk, xid, epoch_rewards ) ) ) {
89 0 : FD_LOG_ERR(( "failed to read sysvar epoch rewards" ));
90 0 : }
91 :
92 0 : if( FD_UNLIKELY( epoch_rewards->total_rewards < epoch_rewards->distributed_rewards ) ) {
93 0 : FD_LOG_ERR(( "distributed rewards overflow" ));
94 0 : }
95 :
96 0 : epoch_rewards->active = 0;
97 :
98 0 : write_epoch_rewards( bank, accdb, xid, capture_ctx, epoch_rewards );
99 0 : }
100 :
101 : /* Create EpochRewards sysvar with calculated rewards
102 :
103 : https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/runtime/src/bank/partitioned_epoch_rewards/sysvar.rs#L25 */
104 : void
105 : fd_sysvar_epoch_rewards_init( fd_bank_t * bank,
106 : fd_accdb_user_t * accdb,
107 : fd_funk_txn_xid_t const * xid,
108 : fd_capture_ctx_t * capture_ctx,
109 : ulong distributed_rewards,
110 : ulong distribution_starting_block_height,
111 : ulong num_partitions,
112 : ulong total_rewards,
113 : uint128 total_points,
114 0 : fd_hash_t const * last_blockhash ) {
115 0 : fd_sysvar_epoch_rewards_t epoch_rewards = {
116 0 : .distribution_starting_block_height = distribution_starting_block_height,
117 0 : .num_partitions = num_partitions,
118 0 : .total_points = { .ud=total_points },
119 0 : .total_rewards = total_rewards,
120 0 : .distributed_rewards = distributed_rewards,
121 0 : .active = 1,
122 0 : .parent_blockhash = *last_blockhash
123 0 : };
124 :
125 0 : if( FD_UNLIKELY( epoch_rewards.total_rewards<distributed_rewards ) ) {
126 0 : FD_LOG_ERR(( "total rewards overflow" ));
127 0 : }
128 :
129 0 : write_epoch_rewards( bank, accdb, xid, capture_ctx, &epoch_rewards );
130 0 : }
|