Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_fd_runtime_stack_h
2 : #define HEADER_fd_src_flamenco_runtime_fd_runtime_stack_h
3 :
4 : #include "sysvar/fd_sysvar_clock.h"
5 : #include "program/fd_builtin_programs.h"
6 : #include "../leaders/fd_leaders_base.h"
7 : #include "../../ballet/sbpf/fd_sbpf_loader.h"
8 :
9 : /* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/points.rs#L27 */
10 : struct fd_calculated_stake_points {
11 : fd_w_u128_t points;
12 : ulong new_credits_observed;
13 : uint vote_idx; /* Caches this delegation's vote_rewards_map index. UINT_MAX if the
14 : vote account is not in the rewards map. Not populated during
15 : recalculation due to lack of the points phase. */
16 : uchar force_credits_update_with_skipped_reward;
17 : };
18 : typedef struct fd_calculated_stake_points fd_calculated_stake_points_t;
19 :
20 : /* https://github.com/anza-xyz/agave/blob/cbc8320d35358da14d79ebcada4dfb6756ffac79/programs/stake/src/rewards.rs#L24 */
21 : struct fd_calculated_stake_rewards {
22 : ulong staker_rewards;
23 : ulong voter_rewards;
24 : ulong new_credits_observed;
25 : uchar success;
26 : };
27 : typedef struct fd_calculated_stake_rewards fd_calculated_stake_rewards_t;
28 :
29 : /* fd_vote_ele and fd_vote_ele_map are used to temporarily cache
30 : computed fields for vote accounts during epoch boundary stake
31 : and rewards calculations. */
32 :
33 : struct fd_vote_rewards {
34 : fd_pubkey_t pubkey;
35 : ulong vote_rewards;
36 : uint next;
37 : ushort commission;
38 : };
39 : typedef struct fd_vote_rewards fd_vote_rewards_t;
40 :
41 : #define MAP_NAME fd_vote_rewards_map
42 : #define MAP_KEY_T fd_pubkey_t
43 : #define MAP_ELE_T fd_vote_rewards_t
44 336 : #define MAP_KEY pubkey
45 474 : #define MAP_KEY_EQ(k0,k1) (!memcmp( k0, k1, sizeof(fd_pubkey_t) ))
46 732 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash( (seed)^FD_LOAD( ulong, ((uchar const *)(key))+24UL ) ))
47 933 : #define MAP_NEXT next
48 2025 : #define MAP_IDX_T uint
49 : #include "../../util/tmpl/fd_map_chain.c"
50 :
51 : struct fd_stake_accum {
52 : fd_pubkey_t pubkey;
53 : ulong stake; /* effective stake in the current epoch */
54 : ulong reward_stake; /* effective stake in the rewarded epoch */
55 : uint next;
56 : };
57 : typedef struct fd_stake_accum fd_stake_accum_t;
58 :
59 : #define MAP_NAME fd_stake_accum_map
60 : #define MAP_KEY_T fd_pubkey_t
61 : #define MAP_ELE_T fd_stake_accum_t
62 390 : #define MAP_KEY pubkey
63 105 : #define MAP_KEY_EQ(k0,k1) (!memcmp( k0, k1, sizeof(fd_pubkey_t) ))
64 804 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash( (seed)^FD_LOAD( ulong, ((uchar const *)(key))+24UL ) ))
65 855 : #define MAP_NEXT next
66 2250 : #define MAP_IDX_T uint
67 : #include "../../util/tmpl/fd_map_chain.c"
68 :
69 : /* fd_runtime_stack_t serves as stack memory to store temporary data
70 : for the runtime. This object should only be used and owned by the
71 : replay tile and is used for short-lived allocations for the runtime,
72 : more specifically, for slot level calculations. */
73 : struct fd_runtime_stack {
74 :
75 : ulong max_vote_accounts;
76 : ulong max_staked_vote_accounts;
77 : ulong max_stake_accounts;
78 :
79 : struct {
80 : /* Staging memory to sort vote accounts by last vote timestamp for
81 : clock sysvar calculation. */
82 : ts_est_ele_t * staked_ts;
83 : } clock_ts;
84 :
85 : struct {
86 : /* Staging memory for bpf migration. This is used to store and
87 : stage various accounts which is required for deploying a new BPF
88 : program at the epoch boundary.
89 :
90 : TODO: These are only used by the replay tile on epoch boundaries
91 : and don't need to be in the per-exec stacks. Additionally, we
92 : could just acquire these buffers out of the account database
93 : directly to share them across tiles using the existing flexible
94 : buffer management. */
95 : fd_tmp_account_t source;
96 : fd_tmp_account_t program_account;
97 : fd_tmp_account_t new_target_program;
98 : fd_tmp_account_t new_target_program_data;
99 : fd_tmp_account_t empty;
100 :
101 : /* Staging memory for ELF validation during BPF program
102 : migrations. */
103 : struct {
104 : uchar rodata [ FD_RUNTIME_ACC_SZ_MAX ] __attribute__((aligned(FD_SBPF_PROG_RODATA_ALIGN)));
105 : uchar sbpf_footprint[ FD_SBPF_PROGRAM_FOOTPRINT ] __attribute__((aligned(alignof(fd_sbpf_program_t))));
106 : uchar programdata [ FD_RUNTIME_ACC_SZ_MAX ] __attribute__((aligned(FD_ACCOUNT_REC_ALIGN)));
107 : } progcache_validate;
108 : } bpf_migration;
109 :
110 : struct {
111 : fd_calculated_stake_points_t * stake_points_result;
112 :
113 : fd_calculated_stake_rewards_t * stake_rewards_result;
114 :
115 : fd_stake_accum_t * stake_accum;
116 : fd_stake_accum_map_t * stake_accum_map;
117 :
118 : fd_vote_rewards_t * vote_ele;
119 : fd_vote_rewards_map_t * vote_map;
120 :
121 : ulong total_rewards;
122 : ulong distributed_rewards;
123 : fd_w_u128_t total_points;
124 :
125 : ulong stake_rewards_cnt;
126 :
127 : /* Staging memory used for calculating and sorting vote account
128 : stake weights for the leader schedule calculation. */
129 : fd_vote_stake_weight_t * stake_weights;
130 : fd_stake_weight_t * id_weights;
131 :
132 : } stakes;
133 :
134 : struct {
135 : fd_vote_stake_weight_t stake_weights[ MAX_STAKE_WEIGHTS ];
136 : ulong stake_weights_cnt;
137 :
138 : fd_stake_weight_t id_weights[ MAX_STAKE_WEIGHTS ];
139 : ulong id_weights_cnt;
140 :
141 : fd_vote_stake_weight_t next_stake_weights[ MAX_STAKE_WEIGHTS ];
142 : ulong next_stake_weights_cnt;
143 :
144 : fd_stake_weight_t next_id_weights[ MAX_STAKE_WEIGHTS ];
145 : ulong next_id_weights_cnt;
146 : } epoch_weights;
147 : };
148 : typedef struct fd_runtime_stack fd_runtime_stack_t;
149 :
150 : FD_FN_CONST static inline ulong
151 633 : fd_runtime_stack_align( void ) {
152 633 : return 128UL;
153 633 : }
154 :
155 : FD_FN_PURE static inline ulong
156 : fd_runtime_stack_footprint( ulong max_vote_accounts,
157 : ulong max_staked_vote_accounts,
158 189 : ulong max_stake_accounts ) {
159 189 : ulong vote_chain_cnt = fd_vote_rewards_map_chain_cnt_est( max_vote_accounts );
160 189 : ulong stake_chain_cnt = fd_stake_accum_map_chain_cnt_est( max_staked_vote_accounts );
161 189 : ulong l = FD_LAYOUT_INIT;
162 189 : l = FD_LAYOUT_APPEND( l, alignof(fd_runtime_stack_t), sizeof(fd_runtime_stack_t) );
163 189 : l = FD_LAYOUT_APPEND( l, alignof(ts_est_ele_t), sizeof(ts_est_ele_t) * max_vote_accounts );
164 189 : l = FD_LAYOUT_APPEND( l, alignof(fd_vote_stake_weight_t), sizeof(fd_vote_stake_weight_t) * max_vote_accounts );
165 189 : l = FD_LAYOUT_APPEND( l, alignof(fd_stake_weight_t), sizeof(fd_stake_weight_t) * max_vote_accounts );
166 189 : l = FD_LAYOUT_APPEND( l, 128UL, sizeof(fd_vote_rewards_t) * max_vote_accounts );
167 189 : l = FD_LAYOUT_APPEND( l, fd_vote_rewards_map_align(), fd_vote_rewards_map_footprint( vote_chain_cnt ) );
168 189 : l = FD_LAYOUT_APPEND( l, 128UL, sizeof(fd_stake_accum_t) * max_staked_vote_accounts );
169 189 : l = FD_LAYOUT_APPEND( l, fd_stake_accum_map_align(), fd_stake_accum_map_footprint( stake_chain_cnt ) );
170 189 : l = FD_LAYOUT_APPEND( l, alignof(fd_calculated_stake_points_t), sizeof(fd_calculated_stake_points_t) * max_stake_accounts );
171 189 : l = FD_LAYOUT_APPEND( l, alignof(fd_calculated_stake_rewards_t),sizeof(fd_calculated_stake_rewards_t) * max_stake_accounts );
172 189 : return FD_LAYOUT_FINI( l, fd_runtime_stack_align() );
173 189 : }
174 :
175 : static inline void *
176 : fd_runtime_stack_new( void * shmem,
177 : ulong max_vote_accounts,
178 : ulong max_staked_vote_accounts,
179 : ulong max_stake_accounts,
180 66 : ulong seed ) {
181 66 : if( FD_UNLIKELY( !shmem ) ) return NULL;
182 66 : ulong vote_chain_cnt = fd_vote_rewards_map_chain_cnt_est( max_vote_accounts );
183 66 : ulong stake_chain_cnt = fd_stake_accum_map_chain_cnt_est( max_staked_vote_accounts );
184 66 : FD_SCRATCH_ALLOC_INIT( l, shmem );
185 66 : fd_runtime_stack_t * runtime_stack = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_runtime_stack_t), sizeof(fd_runtime_stack_t) );
186 66 : ts_est_ele_t * staked_ts = FD_SCRATCH_ALLOC_APPEND( l, alignof(ts_est_ele_t), sizeof(ts_est_ele_t) * max_vote_accounts );
187 66 : fd_vote_stake_weight_t * stake_weights = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_vote_stake_weight_t), sizeof(fd_vote_stake_weight_t) * max_vote_accounts );
188 66 : fd_stake_weight_t * id_weights = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_stake_weight_t), sizeof(fd_stake_weight_t) * max_vote_accounts );
189 66 : fd_vote_rewards_t * vote_ele = FD_SCRATCH_ALLOC_APPEND( l, 128UL, sizeof(fd_vote_rewards_t) * max_vote_accounts );
190 66 : void * vote_map_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_rewards_map_align(), fd_vote_rewards_map_footprint( vote_chain_cnt ) );
191 66 : fd_stake_accum_t * stake_accum = FD_SCRATCH_ALLOC_APPEND( l, 128UL, sizeof(fd_stake_accum_t) * max_staked_vote_accounts );
192 66 : void * stake_accum_map_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_accum_map_align(), fd_stake_accum_map_footprint( stake_chain_cnt ) );
193 66 : fd_calculated_stake_points_t * stake_points_result = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_calculated_stake_points_t), sizeof(fd_calculated_stake_points_t) * max_stake_accounts );
194 66 : fd_calculated_stake_rewards_t * stake_rewards_result = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_calculated_stake_rewards_t), sizeof(fd_calculated_stake_rewards_t) * max_stake_accounts );
195 66 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_runtime_stack_align() )!=(ulong)shmem + fd_runtime_stack_footprint( max_vote_accounts, max_staked_vote_accounts, max_stake_accounts ) ) ) {
196 0 : FD_LOG_WARNING(( "fd_runtime_stack_new: bad layout" ));
197 0 : return NULL;
198 0 : }
199 :
200 66 : runtime_stack->max_vote_accounts = max_vote_accounts;
201 66 : runtime_stack->max_staked_vote_accounts = max_staked_vote_accounts;
202 66 : runtime_stack->max_stake_accounts = max_stake_accounts;
203 66 : runtime_stack->clock_ts.staked_ts = staked_ts;
204 66 : runtime_stack->stakes.stake_weights = stake_weights;
205 66 : runtime_stack->stakes.id_weights = id_weights;
206 66 : runtime_stack->stakes.vote_ele = vote_ele;
207 66 : runtime_stack->stakes.stake_points_result = stake_points_result;
208 66 : runtime_stack->stakes.stake_rewards_result = stake_rewards_result;
209 66 : runtime_stack->stakes.stake_accum = stake_accum;
210 :
211 66 : runtime_stack->stakes.stake_accum_map = fd_stake_accum_map_join( fd_stake_accum_map_new( stake_accum_map_mem, stake_chain_cnt, seed ) );
212 66 : if( FD_UNLIKELY( !runtime_stack->stakes.stake_accum_map ) ) {
213 0 : FD_LOG_WARNING(( "fd_runtime_stack_new: bad map" ));
214 0 : return NULL;
215 0 : }
216 :
217 66 : runtime_stack->stakes.vote_map = fd_vote_rewards_map_join( fd_vote_rewards_map_new( vote_map_mem, vote_chain_cnt, seed ) );
218 66 : if( FD_UNLIKELY( !runtime_stack->stakes.vote_map ) ) {
219 0 : FD_LOG_WARNING(( "fd_runtime_stack_new: bad map" ));
220 0 : return NULL;
221 0 : }
222 :
223 66 : return shmem;
224 66 : }
225 :
226 : FD_FN_CONST static inline fd_runtime_stack_t *
227 66 : fd_runtime_stack_join( void * shruntime_stack ) {
228 66 : return (fd_runtime_stack_t *)shruntime_stack;
229 66 : }
230 :
231 : #endif /* HEADER_fd_src_flamenco_runtime_fd_runtime_stack_h */
|