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 84 : 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 84 : return fd_ulong_max( fd_ulong_max( fd_vote_state_map_align(),
35 84 : fd_vote_state_pool_align() ), alignof(fd_vote_states_t) );
36 84 : }
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 :
197 6 : vote_state->vote_account = *vote_account;
198 6 : vote_state->stake = 0UL;
199 6 : vote_state->stake_t_1 = 0UL;
200 6 : vote_state->stake_t_2 = 0UL;
201 :
202 6 : if( FD_UNLIKELY( !fd_vote_state_map_ele_insert(
203 6 : vote_state_map,
204 6 : vote_state,
205 6 : vote_state_pool ) ) ) {
206 0 : FD_LOG_CRIT(( "unable to insert stake delegation into map" ));
207 0 : }
208 6 : return vote_state;
209 6 : }
210 :
211 : void
212 : fd_vote_states_remove( fd_vote_states_t * vote_states,
213 3 : fd_pubkey_t const * vote_account ) {
214 3 : fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
215 3 : fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states );
216 3 : if( FD_UNLIKELY( !vote_state_pool ) ) {
217 0 : FD_LOG_CRIT(( "unable to retrieve join to stake delegation pool" ));
218 0 : }
219 3 : if( FD_UNLIKELY( !vote_state_map ) ) {
220 0 : FD_LOG_CRIT(( "unable to retrieve join to stake delegation map" ));
221 0 : }
222 :
223 3 : ulong vote_state_idx = fd_vote_state_map_idx_query_const(
224 3 : vote_state_map,
225 3 : vote_account,
226 3 : ULONG_MAX,
227 3 : vote_state_pool );
228 3 : if( FD_UNLIKELY( vote_state_idx == ULONG_MAX ) ) {
229 : /* The vote state was not found, nothing to do. */
230 0 : return;
231 0 : }
232 :
233 3 : fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, vote_state_idx );
234 3 : if( FD_UNLIKELY( !vote_state ) ) {
235 0 : FD_LOG_CRIT(( "unable to retrieve vote state" ));
236 0 : }
237 :
238 3 : ulong idx = fd_vote_state_map_idx_remove( vote_state_map, vote_account, ULONG_MAX, vote_state_pool );
239 3 : if( FD_UNLIKELY( idx==ULONG_MAX ) ) {
240 0 : FD_LOG_CRIT(( "unable to remove vote state" ));
241 0 : }
242 :
243 : /* Set vote state's next_ pointer to the null idx. */
244 3 : vote_state->next_ = fd_vote_state_pool_idx_null( vote_state_pool );
245 :
246 3 : fd_vote_state_pool_idx_release( vote_state_pool, vote_state_idx );
247 3 : }
248 :
249 : fd_vote_state_ele_t *
250 : fd_vote_states_update_from_account( fd_vote_states_t * vote_states,
251 : fd_pubkey_t const * vote_account,
252 : uchar const * account_data,
253 0 : ulong account_data_len ) {
254 :
255 : /* TODO: Instead of doing this messy + unbounded decode, it should be
256 : replaced with a more efficient decode that just reads the fields
257 : we need directly. */
258 :
259 0 : fd_bincode_decode_ctx_t ctx = {
260 0 : .data = account_data,
261 0 : .dataend = account_data + account_data_len,
262 0 : };
263 :
264 0 : uchar __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))) vote_state_versioned[ FD_VOTE_STATE_VERSIONED_FOOTPRINT ];
265 :
266 0 : fd_vote_state_versioned_t * vsv = fd_vote_state_versioned_decode( vote_state_versioned, &ctx );
267 0 : if( FD_UNLIKELY( vsv==NULL ) ) {
268 0 : FD_LOG_CRIT(( "unable to decode vote state versioned" ));
269 0 : }
270 :
271 0 : fd_pubkey_t node_account;
272 0 : uchar commission;
273 0 : long last_vote_timestamp;
274 0 : ulong last_vote_slot;
275 :
276 0 : switch( vsv->discriminant ) {
277 0 : case fd_vote_state_versioned_enum_v0_23_5:
278 0 : node_account = vsv->inner.v0_23_5.node_pubkey;
279 0 : commission = vsv->inner.v0_23_5.commission;
280 0 : last_vote_timestamp = vsv->inner.v0_23_5.last_timestamp.timestamp;
281 0 : last_vote_slot = vsv->inner.v0_23_5.last_timestamp.slot;
282 0 : break;
283 0 : case fd_vote_state_versioned_enum_v1_14_11:
284 0 : node_account = vsv->inner.v1_14_11.node_pubkey;
285 0 : commission = vsv->inner.v1_14_11.commission;
286 0 : last_vote_timestamp = vsv->inner.v1_14_11.last_timestamp.timestamp;
287 0 : last_vote_slot = vsv->inner.v1_14_11.last_timestamp.slot;
288 0 : break;
289 0 : case fd_vote_state_versioned_enum_current:
290 0 : node_account = vsv->inner.current.node_pubkey;
291 0 : commission = vsv->inner.current.commission;
292 0 : last_vote_timestamp = vsv->inner.current.last_timestamp.timestamp;
293 0 : last_vote_slot = vsv->inner.current.last_timestamp.slot;
294 0 : break;
295 0 : default:
296 0 : __builtin_unreachable();
297 0 : }
298 :
299 0 : fd_vote_state_ele_t * vote_state = fd_vote_states_update( vote_states, vote_account );
300 :
301 0 : vote_state->node_account = node_account;
302 0 : vote_state->commission = commission;
303 0 : vote_state->last_vote_timestamp = last_vote_timestamp;
304 0 : vote_state->last_vote_slot = last_vote_slot;
305 :
306 0 : return vote_state;
307 0 : }
308 :
309 : void
310 3 : fd_vote_states_reset_stakes( fd_vote_states_t * vote_states ) {
311 3 : fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
312 3 : fd_vote_state_map_t * vote_state_map = fd_vote_states_get_map( vote_states );
313 3 : if( FD_UNLIKELY( !vote_state_pool ) ) {
314 0 : FD_LOG_CRIT(( "unable to retrieve join to vote state pool" ));
315 0 : }
316 3 : if( FD_UNLIKELY( !vote_state_map ) ) {
317 0 : FD_LOG_CRIT(( "unable to retrieve join to vote state map" ));
318 0 : }
319 :
320 3 : for( fd_vote_state_map_iter_t iter = fd_vote_state_map_iter_init( vote_state_map, vote_state_pool );
321 9 : !fd_vote_state_map_iter_done( iter, vote_state_map, vote_state_pool );
322 6 : iter = fd_vote_state_map_iter_next( iter, vote_state_map, vote_state_pool ) ) {
323 6 : ulong idx = fd_vote_state_map_iter_idx( iter, vote_state_map, vote_state_pool );
324 :
325 6 : fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, idx );
326 6 : if( FD_UNLIKELY( !vote_state ) ) {
327 0 : FD_LOG_CRIT(( "unable to retrieve vote state" ));
328 0 : }
329 :
330 6 : vote_state->stake = 0UL;
331 6 : }
332 3 : }
333 :
334 : fd_vote_state_ele_t *
335 : fd_vote_states_query( fd_vote_states_t const * vote_states,
336 12 : fd_pubkey_t const * vote_account ) {
337 :
338 : /* map_chain's _ele_query function isn't safe for concurrent access.
339 : The solution is to use the idx_query_const function, which is safe
340 : for concurrent access. The caller is still responsible for
341 : synchronizing concurrent writers to the fd_vote_state_ele_t. */
342 12 : ulong idx = fd_vote_state_map_idx_query_const(
343 12 : fd_vote_states_get_map( vote_states ),
344 12 : vote_account,
345 12 : ULONG_MAX,
346 12 : fd_vote_states_get_pool( vote_states ) );
347 12 : if( FD_UNLIKELY( idx==ULONG_MAX ) ) {
348 3 : return NULL;
349 3 : }
350 :
351 9 : fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( fd_vote_states_get_pool( vote_states ), idx );
352 9 : if( FD_UNLIKELY( !vote_state ) ) {
353 0 : FD_LOG_CRIT(( "unable to retrieve vote state" ));
354 0 : }
355 :
356 9 : return vote_state;
357 9 : }
358 :
359 : /* fd_vote_states_query_const is the same as fd_vote_states but instead
360 : returns a const pointer. */
361 :
362 : fd_vote_state_ele_t const *
363 : fd_vote_states_query_const( fd_vote_states_t const * vote_states,
364 0 : fd_pubkey_t const * vote_account ) {
365 0 : return fd_vote_state_map_ele_query_const(
366 0 : fd_vote_states_get_map( vote_states ),
367 0 : vote_account,
368 0 : NULL,
369 0 : fd_vote_states_get_pool( vote_states ) );
370 0 : }
371 :
372 : ulong
373 0 : fd_vote_states_max( fd_vote_states_t const * vote_states ) {
374 0 : return vote_states->max_vote_accounts_;
375 0 : }
376 :
377 : ulong
378 9 : fd_vote_states_cnt( fd_vote_states_t const * vote_states ) {
379 9 : return fd_vote_state_pool_used( fd_vote_states_get_pool( vote_states ) );
380 9 : }
381 :
382 : fd_vote_state_ele_t *
383 0 : fd_vote_states_iter_ele( fd_vote_states_iter_t * iter ) {
384 0 : ulong idx = fd_vote_state_map_iter_idx( iter->iter, iter->map, iter->pool );
385 0 : return fd_vote_state_pool_ele( iter->pool, idx );
386 0 : }
387 :
388 : fd_vote_states_iter_t *
389 : fd_vote_states_iter_init( fd_vote_states_iter_t * iter,
390 0 : fd_vote_states_t const * vote_states ) {
391 0 : if( FD_UNLIKELY( !iter ) ) {
392 0 : FD_LOG_CRIT(( "NULL iter_mem" ));
393 0 : }
394 0 : if( FD_UNLIKELY( !vote_states ) ) {
395 0 : FD_LOG_CRIT(( "NULL vote_states" ));
396 0 : }
397 :
398 0 : iter->map = fd_vote_states_get_map( vote_states );
399 0 : iter->pool = fd_vote_states_get_pool( vote_states );
400 0 : iter->iter = fd_vote_state_map_iter_init( iter->map, iter->pool );
401 :
402 0 : return iter;
403 0 : }
404 :
405 : int
406 0 : fd_vote_states_iter_done( fd_vote_states_iter_t * iter ) {
407 0 : return fd_vote_state_map_iter_done( iter->iter, iter->map, iter->pool );
408 0 : }
409 :
410 : void
411 0 : fd_vote_states_iter_next( fd_vote_states_iter_t * iter ) {
412 0 : iter->iter = fd_vote_state_map_iter_next( iter->iter, iter->map, iter->pool );
413 0 : }
|