Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_rewards_fd_reward_epoch_delegated_stakes_h
2 : #define HEADER_fd_src_flamenco_rewards_fd_reward_epoch_delegated_stakes_h
3 :
4 : #include "../features/fd_features.h"
5 : #include "../runtime/fd_accdb_svm.h"
6 : #include "../runtime/fd_bank.h"
7 : #include "../runtime/fd_pubkey_utils.h"
8 : #include "../runtime/fd_runtime_stack_tmpl.h"
9 : #include "../runtime/fd_system_ids.h"
10 : #include "../runtime/sysvar/fd_sysvar_rent.h"
11 :
12 318 : #define FD_REWARD_EPOCH_STAKE_MAX_CNT (FD_RUNTIME_MAX_VAT_VOTE_ACCOUNTS)
13 1050 : #define FD_REWARD_EPOCH_STAKE_HEADER_SZ (16UL)
14 1050 : #define FD_REWARD_EPOCH_STAKE_ENTRY_SZ (40UL)
15 318 : #define FD_REWARD_EPOCH_STAKE_MAX_SZ (FD_REWARD_EPOCH_STAKE_HEADER_SZ + FD_REWARD_EPOCH_STAKE_ENTRY_SZ*FD_REWARD_EPOCH_STAKE_MAX_CNT)
16 :
17 : /* https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/runtime/src/alpenglow_epoch_type.rs#L12-L42 */
18 : struct fd_reward_epoch_stake {
19 : fd_pubkey_t vote_pubkey;
20 : ulong delegated_stake;
21 : };
22 : typedef struct fd_reward_epoch_stake fd_reward_epoch_stake_t;
23 :
24 : #define SORT_NAME fd_reward_epoch_stake_sort
25 156 : #define SORT_KEY_T fd_reward_epoch_stake_t
26 78 : #define SORT_BEFORE(a,b) (memcmp( (a).vote_pubkey.uc, (b).vote_pubkey.uc, sizeof(fd_pubkey_t) )<0)
27 : #include "../../util/tmpl/fd_sort.c"
28 :
29 : static inline fd_pubkey_t
30 336 : fd_reward_epoch_stakes_account_address( void ) {
31 336 : fd_feature_id_t const * feature_id = &ids[ offsetof( fd_features_t, alpenglow )>>3 ];
32 336 : fd_pubkey_t const * program_id = &feature_id->id;
33 336 : uchar const seed[] = "reward_epoch_delegated_stakes";
34 336 : uchar const * seeds[1] = { seed };
35 336 : ulong seed_szs[1] = { sizeof(seed)-1UL };
36 336 : fd_pubkey_t address[1];
37 336 : uchar bump_seed;
38 336 : uint custom_err = 0U;
39 336 : FD_TEST( !fd_pubkey_find_program_address( program_id, 1UL, seeds, seed_szs,
40 336 : address, &bump_seed, &custom_err ) );
41 336 : return *address;
42 336 : }
43 :
44 : /* https://github.com/anza-xyz/agave/blob/v4.3.0-beta.0/runtime/src/alpenglow_epoch_type.rs#L70-L132 */
45 : static inline void
46 : fd_reward_epoch_stakes_set( fd_bank_t * bank,
47 : fd_accdb_t * accdb,
48 : fd_capture_ctx_t * capture_ctx,
49 : ulong rewarded_epoch,
50 318 : fd_runtime_stack_t * runtime_stack ) {
51 318 : fd_stake_accum_t * pool = runtime_stack->stakes.stake_accum;
52 318 : fd_stake_accum_map_t * map = runtime_stack->stakes.stake_accum_map;
53 :
54 318 : fd_reward_epoch_stake_t entries[ FD_REWARD_EPOCH_STAKE_MAX_CNT ];
55 318 : ulong entries_cnt = 0UL;
56 :
57 318 : fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
58 318 : ushort fork_id = bank->vote_stakes_fork_id;
59 :
60 318 : uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) vote_iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
61 318 : for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_iter_init( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_1, vote_iter_mem );
62 714 : !fd_vote_stakes_iter_done( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_1, iter );
63 396 : fd_vote_stakes_iter_next( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_1, iter ) ) {
64 396 : FD_TEST( entries_cnt<FD_REWARD_EPOCH_STAKE_MAX_CNT );
65 396 : fd_vote_stakes_ele_t ele[1];
66 396 : fd_vote_stakes_iter_ele( vote_stakes, fork_id, FD_VOTE_STAKES_ITER_T_1, iter, ele );
67 396 : entries[ entries_cnt ].vote_pubkey = ele->pubkey;
68 396 : fd_stake_accum_t const * accumulated = fd_stake_accum_map_ele_query_const(
69 396 : map, &entries[ entries_cnt ].vote_pubkey, NULL, pool );
70 396 : FD_TEST( accumulated );
71 396 : entries[ entries_cnt ].delegated_stake = accumulated->reward_stake;
72 396 : entries_cnt++;
73 396 : }
74 318 : fd_reward_epoch_stake_sort_inplace( entries, entries_cnt );
75 :
76 318 : uchar data[ FD_REWARD_EPOCH_STAKE_MAX_SZ ];
77 318 : FD_STORE( ulong, data, rewarded_epoch );
78 318 : FD_STORE( ulong, data+8UL, entries_cnt );
79 714 : for( ulong i=0UL; i<entries_cnt; i++ ) {
80 396 : uchar * dst = data+FD_REWARD_EPOCH_STAKE_HEADER_SZ+i*FD_REWARD_EPOCH_STAKE_ENTRY_SZ;
81 396 : fd_memcpy( dst, entries[i].vote_pubkey.uc, sizeof(fd_pubkey_t) );
82 396 : FD_STORE( ulong, dst+32UL, entries[i].delegated_stake );
83 396 : }
84 318 : ulong data_sz = FD_REWARD_EPOCH_STAKE_HEADER_SZ+entries_cnt*FD_REWARD_EPOCH_STAKE_ENTRY_SZ;
85 :
86 318 : fd_pubkey_t address = fd_reward_epoch_stakes_account_address();
87 318 : fd_accdb_svm_update_t update[1];
88 318 : fd_acc_t acc = fd_accdb_svm_open_rw( bank, accdb, update, &address, 1 );
89 318 : acc.lamports = fd_ulong_max( fd_rent_exempt_minimum_balance( &bank->f.rent, FD_REWARD_EPOCH_STAKE_MAX_SZ ), 1UL );
90 318 : fd_memcpy( acc.owner, fd_solana_system_program_id.uc, sizeof(fd_pubkey_t) );
91 318 : acc.executable = 0;
92 318 : acc.data_len = data_sz;
93 318 : fd_memcpy( acc.data, data, data_sz );
94 318 : fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &acc, update );
95 318 : }
96 :
97 : static inline void
98 : fd_reward_epoch_stakes_restore( fd_bank_t * bank,
99 : fd_accdb_t * accdb,
100 : ulong rewarded_epoch,
101 18 : fd_runtime_stack_t * runtime_stack ) {
102 18 : fd_pubkey_t address = fd_reward_epoch_stakes_account_address();
103 18 : fd_acc_t acc = fd_accdb_read_one( accdb, bank->accdb_fork_id, address.uc );
104 18 : if( FD_UNLIKELY( !acc.lamports ||
105 18 : memcmp( acc.owner, fd_solana_system_program_id.uc, sizeof(fd_pubkey_t) ) ||
106 18 : acc.data_len<FD_REWARD_EPOCH_STAKE_HEADER_SZ ||
107 18 : (acc.data_len-FD_REWARD_EPOCH_STAKE_HEADER_SZ)%FD_REWARD_EPOCH_STAKE_ENTRY_SZ ) ) {
108 0 : FD_LOG_CRIT(( "missing reward epoch delegated stakes for Alpenglow epoch %lu", rewarded_epoch ));
109 0 : }
110 :
111 18 : ulong epoch = FD_LOAD( ulong, acc.data );
112 18 : ulong cnt = FD_LOAD( ulong, acc.data+8UL );
113 18 : if( FD_UNLIKELY( epoch!=rewarded_epoch ||
114 18 : cnt>FD_REWARD_EPOCH_STAKE_MAX_CNT ||
115 18 : cnt>runtime_stack->max_staked_vote_accounts ||
116 18 : acc.data_len!=FD_REWARD_EPOCH_STAKE_HEADER_SZ+cnt*FD_REWARD_EPOCH_STAKE_ENTRY_SZ ) ) {
117 0 : FD_LOG_CRIT(( "invalid reward epoch delegated stakes for Alpenglow epoch %lu", rewarded_epoch ));
118 0 : }
119 :
120 18 : fd_stake_accum_t * pool = runtime_stack->stakes.stake_accum;
121 18 : fd_stake_accum_map_t * map = runtime_stack->stakes.stake_accum_map;
122 18 : fd_stake_accum_map_reset( map );
123 36 : for( ulong i=0UL; i<cnt; i++ ) {
124 18 : uchar const * src = acc.data+FD_REWARD_EPOCH_STAKE_HEADER_SZ+i*FD_REWARD_EPOCH_STAKE_ENTRY_SZ;
125 18 : fd_stake_accum_t * ele = &pool[i];
126 18 : fd_memcpy( ele->pubkey.uc, src, sizeof(fd_pubkey_t) );
127 18 : ele->stake = 0UL;
128 18 : ele->reward_stake = FD_LOAD( ulong, src+32UL );
129 18 : if( FD_UNLIKELY( fd_stake_accum_map_ele_query( map, &ele->pubkey, NULL, pool ) ) ) {
130 0 : FD_LOG_CRIT(( "duplicate vote account in reward epoch delegated stakes" ));
131 0 : }
132 18 : fd_stake_accum_map_ele_insert( map, ele, pool );
133 18 : }
134 18 : fd_accdb_unread_one( accdb, &acc );
135 18 : }
136 :
137 : #endif /* HEADER_fd_src_flamenco_rewards_fd_reward_epoch_delegated_stakes_h */
|