Line data Source code
1 : #include "fd_vote_states.h"
2 : #include "../types/fd_types.h"
3 : #include "../runtime/program/fd_vote_program.h"
4 :
5 : #define POOL_NAME fd_vote_state_pool
6 51 : #define POOL_T fd_vote_state_ele_t
7 39 : #define POOL_NEXT next_
8 : #include "../../util/tmpl/fd_pool.c"
9 :
10 : #define MAP_NAME fd_vote_state_map
11 : #define MAP_KEY_T fd_pubkey_t
12 : #define MAP_ELE_T fd_vote_state_ele_t
13 6 : #define MAP_KEY vote_account
14 15 : #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 ))
15 30 : #define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) ))
16 15 : #define MAP_NEXT next_
17 : #include "../../util/tmpl/fd_map_chain.c"
18 :
19 : static fd_vote_state_ele_t *
20 42 : fd_vote_states_get_pool( fd_vote_states_t const * vote_states ) {
21 42 : return fd_vote_state_pool_join( (uchar *)vote_states + vote_states->pool_offset_ );
22 42 : }
23 :
24 : static fd_vote_state_map_t *
25 24 : fd_vote_states_get_map( fd_vote_states_t const * vote_states ) {
26 24 : return fd_vote_state_map_join( (uchar *)vote_states + vote_states->map_offset_ );
27 24 : }
28 :
29 : ulong
30 90 : fd_vote_states_align( void ) {
31 : /* The align of the struct should be the max of the align of the data
32 : structures that it contains. In this case, this is the map, the
33 : pool, and the struct itself. */
34 90 : return fd_ulong_max( fd_ulong_max( fd_vote_state_map_align(),
35 90 : fd_vote_state_pool_align() ), alignof(fd_vote_states_t) );
36 90 : }
37 :
38 : ulong
39 12 : fd_vote_states_footprint( ulong max_vote_accounts ) {
40 :
41 12 : ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( max_vote_accounts );
42 :
43 12 : ulong l = FD_LAYOUT_INIT;
44 12 : l = FD_LAYOUT_APPEND( l, fd_vote_states_align(), sizeof(fd_vote_states_t) );
45 12 : l = FD_LAYOUT_APPEND( l, fd_vote_state_pool_align(), fd_vote_state_pool_footprint( max_vote_accounts ) );
46 12 : l = FD_LAYOUT_APPEND( l, fd_vote_state_map_align(), fd_vote_state_map_footprint( map_chain_cnt ) );
47 12 : return FD_LAYOUT_FINI( l, fd_vote_states_align() );
48 12 : }
49 :
50 : void *
51 : fd_vote_states_new( void * mem,
52 : ulong max_vote_accounts,
53 9 : ulong seed ) {
54 9 : if( FD_UNLIKELY( !mem ) ) {
55 3 : FD_LOG_WARNING(( "NULL mem" ));
56 3 : return NULL;
57 3 : }
58 :
59 6 : if( FD_UNLIKELY( !max_vote_accounts ) ) {
60 3 : FD_LOG_WARNING(( "max_vote_accounts is 0" ));
61 3 : return NULL;
62 3 : }
63 :
64 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_vote_states_align() ) ) ) {
65 0 : FD_LOG_WARNING(( "misaligned mem" ));
66 0 : return NULL;
67 0 : }
68 :
69 3 : ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( max_vote_accounts );
70 :
71 3 : FD_SCRATCH_ALLOC_INIT( l, mem );
72 3 : fd_vote_states_t * vote_states = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_states_align(), sizeof(fd_vote_states_t) );
73 3 : void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_pool_align(), fd_vote_state_pool_footprint( max_vote_accounts ) );
74 3 : void * map_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_map_align(), fd_vote_state_map_footprint( map_chain_cnt ) );
75 :
76 3 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_vote_states_align() )!=(ulong)mem+fd_vote_states_footprint( max_vote_accounts ) ) ) {
77 0 : FD_LOG_WARNING(( "fd_vote_states_new: bad layout" ));
78 0 : return NULL;
79 0 : }
80 :
81 3 : vote_states->max_vote_accounts_ = max_vote_accounts;
82 3 : vote_states->pool_offset_ = (ulong)pool_mem - (ulong)mem;
83 3 : vote_states->map_offset_ = (ulong)map_mem - (ulong)mem;
84 :
85 3 : fd_vote_state_ele_t * vote_states_pool = fd_vote_state_pool_join( fd_vote_state_pool_new( pool_mem, max_vote_accounts ) );
86 3 : if( FD_UNLIKELY( !vote_states_pool ) ) {
87 0 : FD_LOG_WARNING(( "Failed to create vote states pool" ));
88 0 : return NULL;
89 0 : }
90 :
91 33 : for( ulong i=0UL; i<max_vote_accounts; i++ ) {
92 30 : fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_states_pool, i );
93 30 : vote_state->idx = i;
94 30 : }
95 :
96 3 : if( FD_UNLIKELY( !fd_vote_state_map_join( fd_vote_state_map_new( map_mem, map_chain_cnt, seed ) ) ) ) {
97 0 : FD_LOG_WARNING(( "Failed to create vote states map" ));
98 0 : return NULL;
99 0 : }
100 :
101 3 : FD_COMPILER_MFENCE();
102 3 : FD_VOLATILE( vote_states->magic ) = FD_VOTE_STATES_MAGIC;
103 3 : FD_COMPILER_MFENCE();
104 :
105 3 : return mem;
106 3 : }
107 :
108 : fd_vote_states_t *
109 9 : fd_vote_states_join( void * mem ) {
110 9 : if( FD_UNLIKELY( !mem ) ) {
111 3 : FD_LOG_WARNING(( "NULL mem" ));
112 3 : return NULL;
113 3 : }
114 :
115 6 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_vote_states_align() ) ) ) {
116 0 : FD_LOG_WARNING(( "misaligned mem" ));
117 0 : return NULL;
118 0 : }
119 :
120 6 : fd_vote_states_t * vote_states = (fd_vote_states_t *)mem;
121 :
122 6 : if( FD_UNLIKELY( vote_states->magic != FD_VOTE_STATES_MAGIC ) ) {
123 3 : FD_LOG_WARNING(( "Invalid vote states magic" ));
124 3 : return NULL;
125 3 : }
126 :
127 3 : ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( vote_states->max_vote_accounts_ );
128 3 : FD_SCRATCH_ALLOC_INIT( l, vote_states );
129 3 : vote_states = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_states_align(), sizeof(fd_vote_states_t) );
130 3 : void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_pool_align(), fd_vote_state_pool_footprint( vote_states->max_vote_accounts_ ) );
131 3 : void * map_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_map_align(), fd_vote_state_map_footprint( map_chain_cnt ) );
132 :
133 3 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_vote_states_align() )!=(ulong)mem+fd_vote_states_footprint( vote_states->max_vote_accounts_ ) ) ) {
134 0 : FD_LOG_WARNING(( "fd_vote_states_join: bad layout" ));
135 0 : return NULL;
136 0 : }
137 :
138 3 : if( FD_UNLIKELY( !fd_vote_state_pool_join( pool_mem ) ) ) {
139 0 : FD_LOG_WARNING(( "Failed to join vote states pool" ));
140 0 : return NULL;
141 0 : }
142 :
143 3 : if( FD_UNLIKELY( !fd_vote_state_map_join( map_mem ) ) ) {
144 0 : FD_LOG_WARNING(( "Failed to join vote states map" ));
145 0 : return NULL;
146 0 : }
147 :
148 3 : return vote_states;
149 3 : }
150 :
151 : fd_vote_state_ele_t *
152 : fd_vote_states_update( fd_vote_states_t * vote_states,
153 6 : fd_pubkey_t const * vote_account ) {
154 :
155 6 : fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
156 6 : fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states );
157 :
158 6 : if( FD_UNLIKELY( !vote_state_pool ) ) {
159 0 : FD_LOG_CRIT(( "unable to retrieve join to vote state pool" ));
160 0 : }
161 6 : if( FD_UNLIKELY( !vote_state_map ) ) {
162 0 : FD_LOG_CRIT(( "unable to retrieve join to vote state map" ));
163 0 : }
164 :
165 : /* First, handle the case where the vote state already exists
166 : and we just need to update the entry. The reason we do a const idx
167 : query is to allow fd_vote_states_update to be called while
168 : iterating over the map. It is unsafe to call
169 : fd_vote_state_map_ele_query() during iteration, but we only
170 : need to change fields which are not used for pool/map management. */
171 :
172 6 : ulong idx = fd_vote_state_map_idx_query_const(
173 6 : vote_state_map,
174 6 : vote_account,
175 6 : ULONG_MAX,
176 6 : vote_state_pool );
177 :
178 6 : if( idx!=ULONG_MAX ) {
179 0 : fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, idx );
180 0 : if( FD_UNLIKELY( !vote_state ) ) {
181 0 : FD_LOG_CRIT(( "unable to retrieve vote state" ));
182 0 : }
183 :
184 : /* TODO: can do something smarter where we only update the
185 : comission and the credits coresponding to the new epoch. */
186 0 : return vote_state;
187 0 : }
188 :
189 : /* If the vote state does not exist, we need to create a new entry. */
190 : /* Otherwise, try to acquire a new node and populate it. */
191 6 : if( FD_UNLIKELY( !fd_vote_state_pool_free( vote_state_pool ) ) ) {
192 0 : FD_LOG_CRIT(( "no free vote states in pool" ));
193 0 : }
194 :
195 6 : fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele_acquire( vote_state_pool );
196 6 : if( FD_UNLIKELY( !vote_state ) ) {
197 0 : FD_LOG_CRIT(( "unable to acquire vote state" ));
198 0 : }
199 :
200 6 : vote_state->vote_account = *vote_account;
201 6 : vote_state->stake = 0UL;
202 6 : vote_state->stake_t_2 = 0UL;
203 :
204 6 : if( FD_UNLIKELY( !fd_vote_state_map_ele_insert(
205 6 : vote_state_map,
206 6 : vote_state,
207 6 : vote_state_pool ) ) ) {
208 0 : FD_LOG_CRIT(( "unable to insert stake delegation into map" ));
209 0 : }
210 6 : return vote_state;
211 6 : }
212 :
213 : void
214 : fd_vote_states_remove( fd_vote_states_t * vote_states,
215 3 : fd_pubkey_t const * vote_account ) {
216 3 : fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
217 3 : fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states );
218 3 : if( FD_UNLIKELY( !vote_state_pool ) ) {
219 0 : FD_LOG_CRIT(( "unable to retrieve join to stake delegation pool" ));
220 0 : }
221 3 : if( FD_UNLIKELY( !vote_state_map ) ) {
222 0 : FD_LOG_CRIT(( "unable to retrieve join to stake delegation map" ));
223 0 : }
224 :
225 3 : ulong vote_state_idx = fd_vote_state_map_idx_query_const(
226 3 : vote_state_map,
227 3 : vote_account,
228 3 : ULONG_MAX,
229 3 : vote_state_pool );
230 3 : if( FD_UNLIKELY( vote_state_idx == ULONG_MAX ) ) {
231 : /* The vote state was not found, nothing to do. */
232 0 : return;
233 0 : }
234 :
235 3 : fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, vote_state_idx );
236 3 : if( FD_UNLIKELY( !vote_state ) ) {
237 0 : FD_LOG_CRIT(( "unable to retrieve vote state" ));
238 0 : }
239 :
240 3 : ulong idx = fd_vote_state_map_idx_remove( vote_state_map, vote_account, ULONG_MAX, vote_state_pool );
241 3 : if( FD_UNLIKELY( idx==ULONG_MAX ) ) {
242 0 : FD_LOG_CRIT(( "unable to remove vote state" ));
243 0 : }
244 :
245 : /* Set vote state's next_ pointer to the null idx. */
246 3 : vote_state->next_ = fd_vote_state_pool_idx_null( vote_state_pool );
247 :
248 3 : fd_vote_state_pool_idx_release( vote_state_pool, vote_state_idx );
249 3 : }
250 :
251 : fd_vote_state_ele_t *
252 : fd_vote_states_update_from_account( fd_vote_states_t * vote_states,
253 : fd_pubkey_t const * vote_account,
254 : uchar const * account_data,
255 0 : ulong account_data_len ) {
256 :
257 : /* TODO: Instead of doing this messy + unbounded decode, it should be
258 : replaced with a more efficient decode that just reads the fields
259 : we need directly. */
260 :
261 0 : fd_bincode_decode_ctx_t ctx = {
262 0 : .data = account_data,
263 0 : .dataend = account_data + account_data_len,
264 0 : };
265 :
266 0 : uchar __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))) vote_state_versioned[ FD_VOTE_STATE_VERSIONED_FOOTPRINT ];
267 :
268 0 : fd_vote_state_versioned_t * vsv = fd_vote_state_versioned_decode( vote_state_versioned, &ctx );
269 0 : if( FD_UNLIKELY( vsv==NULL ) ) {
270 0 : FD_LOG_CRIT(( "unable to decode vote state versioned" ));
271 0 : }
272 :
273 0 : fd_pubkey_t node_account;
274 0 : uchar commission;
275 0 : long last_vote_timestamp;
276 0 : ulong last_vote_slot;
277 :
278 0 : switch( vsv->discriminant ) {
279 0 : case fd_vote_state_versioned_enum_v0_23_5:
280 0 : node_account = vsv->inner.v0_23_5.node_pubkey;
281 0 : commission = vsv->inner.v0_23_5.commission;
282 0 : last_vote_timestamp = vsv->inner.v0_23_5.last_timestamp.timestamp;
283 0 : last_vote_slot = vsv->inner.v0_23_5.last_timestamp.slot;
284 0 : break;
285 0 : case fd_vote_state_versioned_enum_v1_14_11:
286 0 : node_account = vsv->inner.v1_14_11.node_pubkey;
287 0 : commission = vsv->inner.v1_14_11.commission;
288 0 : last_vote_timestamp = vsv->inner.v1_14_11.last_timestamp.timestamp;
289 0 : last_vote_slot = vsv->inner.v1_14_11.last_timestamp.slot;
290 0 : break;
291 0 : case fd_vote_state_versioned_enum_current:
292 0 : node_account = vsv->inner.current.node_pubkey;
293 0 : commission = vsv->inner.current.commission;
294 0 : last_vote_timestamp = vsv->inner.current.last_timestamp.timestamp;
295 0 : last_vote_slot = vsv->inner.current.last_timestamp.slot;
296 0 : break;
297 0 : default:
298 0 : __builtin_unreachable();
299 0 : }
300 :
301 0 : fd_vote_state_ele_t * vote_state = fd_vote_states_update( vote_states, vote_account );
302 :
303 0 : vote_state->node_account = node_account;
304 0 : vote_state->commission = commission;
305 0 : vote_state->last_vote_timestamp = last_vote_timestamp;
306 0 : vote_state->last_vote_slot = last_vote_slot;
307 :
308 0 : return vote_state;
309 0 : }
310 :
311 : void
312 3 : fd_vote_states_reset_stakes( fd_vote_states_t * vote_states ) {
313 3 : fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
314 3 : fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states );
315 3 : if( FD_UNLIKELY( !vote_state_pool ) ) {
316 0 : FD_LOG_CRIT(( "unable to retrieve join to vote state pool" ));
317 0 : }
318 3 : if( FD_UNLIKELY( !vote_state_map ) ) {
319 0 : FD_LOG_CRIT(( "unable to retrieve join to vote state map" ));
320 0 : }
321 :
322 3 : for( fd_vote_state_map_iter_t iter = fd_vote_state_map_iter_init( vote_state_map, vote_state_pool );
323 9 : !fd_vote_state_map_iter_done( iter, vote_state_map, vote_state_pool );
324 6 : iter = fd_vote_state_map_iter_next( iter, vote_state_map, vote_state_pool ) ) {
325 6 : ulong idx = fd_vote_state_map_iter_idx( iter, vote_state_map, vote_state_pool );
326 :
327 6 : fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, idx );
328 6 : if( FD_UNLIKELY( !vote_state ) ) {
329 0 : FD_LOG_CRIT(( "unable to retrieve vote state" ));
330 0 : }
331 :
332 6 : vote_state->stake = 0UL;
333 6 : vote_state->stake_t_2 = 0UL;
334 6 : }
335 3 : }
336 :
337 : fd_vote_state_ele_t *
338 : fd_vote_states_query( fd_vote_states_t const * vote_states,
339 12 : fd_pubkey_t const * vote_account ) {
340 :
341 : /* map_chain's _ele_query function isn't safe for concurrent access.
342 : The solution is to use the idx_query_const function, which is safe
343 : for concurrent access. The caller is still responsible for
344 : synchronizing concurrent writers to the fd_vote_state_ele_t. */
345 12 : ulong idx = fd_vote_state_map_idx_query_const(
346 12 : fd_vote_states_get_map( vote_states ),
347 12 : vote_account,
348 12 : ULONG_MAX,
349 12 : fd_vote_states_get_pool( vote_states ) );
350 12 : if( FD_UNLIKELY( idx==ULONG_MAX ) ) {
351 3 : return NULL;
352 3 : }
353 :
354 9 : fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( fd_vote_states_get_pool( vote_states ), idx );
355 9 : if( FD_UNLIKELY( !vote_state ) ) {
356 0 : FD_LOG_CRIT(( "unable to retrieve vote state" ));
357 0 : }
358 :
359 9 : return vote_state;
360 9 : }
361 :
362 : /* fd_vote_states_query_const is the same as fd_vote_states but instead
363 : returns a const pointer. */
364 :
365 : fd_vote_state_ele_t const *
366 : fd_vote_states_query_const( fd_vote_states_t const * vote_states,
367 0 : fd_pubkey_t const * vote_account ) {
368 0 : return fd_vote_state_map_ele_query_const(
369 0 : fd_vote_states_get_map( vote_states ),
370 0 : vote_account,
371 0 : NULL,
372 0 : fd_vote_states_get_pool( vote_states ) );
373 0 : }
374 :
375 : ulong
376 0 : fd_vote_states_max( fd_vote_states_t const * vote_states ) {
377 0 : return vote_states->max_vote_accounts_;
378 0 : }
379 :
380 : ulong
381 9 : fd_vote_states_cnt( fd_vote_states_t const * vote_states ) {
382 9 : return fd_vote_state_pool_used( fd_vote_states_get_pool( vote_states ) );
383 9 : }
384 :
385 : fd_vote_state_ele_t *
386 0 : fd_vote_states_iter_ele( fd_vote_states_iter_t * iter ) {
387 0 : ulong idx = fd_vote_state_map_iter_idx( iter->iter, iter->map, iter->pool );
388 0 : return fd_vote_state_pool_ele( iter->pool, idx );
389 0 : }
390 :
391 : fd_vote_states_iter_t *
392 : fd_vote_states_iter_init( fd_vote_states_iter_t * iter,
393 0 : fd_vote_states_t const * vote_states ) {
394 0 : if( FD_UNLIKELY( !iter ) ) {
395 0 : FD_LOG_CRIT(( "NULL iter_mem" ));
396 0 : }
397 0 : if( FD_UNLIKELY( !vote_states ) ) {
398 0 : FD_LOG_CRIT(( "NULL vote_states" ));
399 0 : }
400 :
401 0 : iter->map = fd_vote_states_get_map( vote_states );
402 0 : iter->pool = fd_vote_states_get_pool( vote_states );
403 0 : iter->iter = fd_vote_state_map_iter_init( iter->map, iter->pool );
404 :
405 0 : return iter;
406 0 : }
407 :
408 : int
409 0 : fd_vote_states_iter_done( fd_vote_states_iter_t * iter ) {
410 0 : return fd_vote_state_map_iter_done( iter->iter, iter->map, iter->pool );
411 0 : }
412 :
413 : void
414 0 : fd_vote_states_iter_next( fd_vote_states_iter_t * iter ) {
415 0 : iter->iter = fd_vote_state_map_iter_next( iter->iter, iter->map, iter->pool );
416 0 : }
|