Line data Source code
1 : #include "fd_vote_codec_tmpl.h"
2 : #include "../../../../ballet/utf8/fd_utf8.h"
3 : #include "../../../../ballet/txn/fd_compact_u16.h"
4 : #include "../../../../ballet/txn/fd_txn.h"
5 :
6 : /**********************************************************************/
7 : /* Unified bincode (de)serialization macros. */
8 : /* All macros operate on a cursor (uchar const ** data, ulong * sz). */
9 : /* CHECK returns 1 on failure (non-zero == error). */
10 : /**********************************************************************/
11 :
12 731364 : #define CHECK( cond ) do { \
13 731364 : if( FD_UNLIKELY( !(cond) ) ) return 1; \
14 731364 : } while( 0 )
15 :
16 9390 : #define CHECK_RET_NULL( cond ) do { \
17 9390 : if( FD_UNLIKELY( !(cond) ) ) return NULL; \
18 9390 : } while( 0 )
19 :
20 303 : #define CHECK_U64_MUL_OVERFLOW( a, b ) do { \
21 303 : ulong _dummy; \
22 303 : CHECK( !__builtin_umull_overflow( (a), (b), &_dummy ) ); \
23 303 : } while( 0 )
24 :
25 : /**********************************************************************/
26 : /* Read macros */
27 : /**********************************************************************/
28 :
29 102999 : #define READ_BYTES( dst, n, data, sz ) do { \
30 102999 : CHECK( (n)<=(*(sz)) ); \
31 102999 : fd_memcpy( (dst), *(data), (n) ); \
32 102791 : *(data) += (n); \
33 102791 : *(sz) -= (n); \
34 102791 : } while( 0 )
35 :
36 434 : #define SKIP_BYTES( n, data, sz ) do { \
37 434 : CHECK( (n)<=(*(sz)) ); \
38 434 : *(data) += (n); \
39 434 : *(sz) -= (n); \
40 434 : } while( 0 )
41 :
42 81777 : #define READ_U8( dst, data, sz ) do { \
43 81777 : CHECK( 1UL<=(*(sz)) ); \
44 81777 : (dst) = FD_LOAD( uchar, *(data) ); \
45 81750 : *(data) += 1UL; \
46 81750 : *(sz) -= 1UL; \
47 81750 : } while( 0 )
48 :
49 2516 : #define READ_U16( dst, data, sz ) do { \
50 2516 : CHECK( 2UL<=(*(sz)) ); \
51 2516 : (dst) = FD_LOAD( ushort, *(data) ); \
52 2516 : *(data) += 2UL; \
53 2516 : *(sz) -= 2UL; \
54 2516 : } while( 0 )
55 :
56 86321 : #define READ_U32( dst, data, sz ) do { \
57 86321 : CHECK( 4UL<=(*(sz)) ); \
58 86321 : (dst) = FD_LOAD( uint, *(data) ); \
59 86234 : *(data) += 4UL; \
60 86234 : *(sz) -= 4UL; \
61 86234 : } while( 0 )
62 :
63 0 : #define READ_I32( dst, data, sz ) do { \
64 0 : CHECK( 4UL<=(*(sz)) ); \
65 0 : (dst) = FD_LOAD( int, *(data) ); \
66 0 : *(data) += 4UL; \
67 0 : *(sz) -= 4UL; \
68 0 : } while( 0 )
69 :
70 124639 : #define READ_U64( dst, data, sz ) do { \
71 124639 : CHECK( 8UL<=(*(sz)) ); \
72 124639 : (dst) = FD_LOAD( ulong, *(data) ); \
73 124580 : *(data) += 8UL; \
74 124580 : *(sz) -= 8UL; \
75 124580 : } while( 0 )
76 :
77 1170 : #define READ_I64( dst, data, sz ) do { \
78 1170 : CHECK( 8UL<=(*(sz)) ); \
79 1170 : (dst) = FD_LOAD( long, *(data) ); \
80 1159 : *(data) += 8UL; \
81 1159 : *(sz) -= 8UL; \
82 1159 : } while( 0 )
83 :
84 : #define READ_HASH( dst, data, sz ) \
85 16226 : READ_BYTES( (dst).uc, 32UL, data, sz )
86 :
87 12361 : #define READ_PUBKEY READ_HASH
88 :
89 8677 : #define READ_BOOL( dst, data, sz ) do { \
90 8677 : READ_U8( dst, data, sz ); \
91 8677 : CHECK( (dst)==0 || (dst)==1 ); \
92 8662 : } while( 0 )
93 :
94 7243 : #define READ_OPTION READ_BOOL
95 :
96 1127 : #define READ_ENUM( dst, n, data, sz ) do { \
97 1127 : CHECK( 4UL<=(*(sz)) ); \
98 1127 : (dst) = FD_LOAD( uint, *(data) ); \
99 1125 : CHECK( (dst)<(n) ); \
100 1125 : *(data) += 4UL; \
101 1117 : *(sz) -= 4UL; \
102 1117 : } while( 0 )
103 :
104 : /* Varint decoder for u64 (LEB128). */
105 31917 : #define READ_U64_VARINT( dst, data, sz ) do { \
106 31917 : ulong _val = 0UL; \
107 31917 : uint _shift = 0U; \
108 57974 : for(;;) { \
109 57974 : CHECK( 1UL<=(*(sz)) ); \
110 57974 : uchar _byte = FD_LOAD( uchar, *(data) ); \
111 57955 : *(data) += 1UL; \
112 57955 : *(sz) -= 1UL; \
113 57955 : _val |= (ulong)(_byte & 0x7F) << _shift; \
114 57955 : if( FD_LIKELY( !(_byte & 0x80) ) ) { \
115 31898 : CHECK( (_val>>_shift)==(ulong)_byte ); /* last byte not truncated */ \
116 31898 : CHECK( _byte || !_shift ); /* no trailing zero bytes */ \
117 31898 : (dst) = _val; \
118 31891 : break; \
119 31898 : } \
120 57955 : _shift += 7U; \
121 26057 : CHECK( _shift<64U ); \
122 26057 : } \
123 31917 : } while( 0 )
124 :
125 1085 : #define READ_COMPACT_U16( dst, data, sz ) do { \
126 1085 : ulong _n = fd_cu16_dec( *(data), *(sz), &(dst) ); \
127 1085 : CHECK( _n ); \
128 1085 : *(data) += _n; \
129 1012 : *(sz) -= _n; \
130 1012 : } while( 0 )
131 :
132 : /**********************************************************************/
133 : /* Write macros */
134 : /**********************************************************************/
135 :
136 36372 : #define WRITE_BYTES( src, n, out, out_sz ) do { \
137 36372 : CHECK( (n)<=(*(out_sz)) ); \
138 36372 : fd_memcpy( *(out), (src), (n) ); \
139 36372 : *(out) += (n); \
140 36372 : *(out_sz) -= (n); \
141 36372 : } while( 0 )
142 :
143 17396 : #define WRITE_U8( val, out, out_sz ) do { \
144 17396 : CHECK( 1UL<=(*(out_sz)) ); \
145 17396 : FD_STORE( uchar, *(out), (val) ); \
146 17396 : *(out) += 1UL; \
147 17396 : *(out_sz) -= 1UL; \
148 17396 : } while( 0 )
149 :
150 2172 : #define WRITE_U16( val, out, out_sz ) do { \
151 2172 : CHECK( 2UL<=(*(out_sz)) ); \
152 2172 : FD_STORE( ushort, *(out), (val) ); \
153 2172 : *(out) += 2UL; \
154 2172 : *(out_sz) -= 2UL; \
155 2172 : } while( 0 )
156 :
157 16314 : #define WRITE_U32( val, out, out_sz ) do { \
158 16314 : CHECK( 4UL<=(*(out_sz)) ); \
159 16314 : FD_STORE( uint, *(out), (val) ); \
160 16314 : *(out) += 4UL; \
161 16314 : *(out_sz) -= 4UL; \
162 16314 : } while( 0 )
163 :
164 21301 : #define WRITE_U64( val, out, out_sz ) do { \
165 21301 : CHECK( 8UL<=(*(out_sz)) ); \
166 21301 : FD_STORE( ulong, *(out), (val) ); \
167 21301 : *(out) += 8UL; \
168 21301 : *(out_sz) -= 8UL; \
169 21301 : } while( 0 )
170 :
171 : #define WRITE_I64( val, out, out_sz ) do { \
172 : CHECK( 8UL<=(*(out_sz)) ); \
173 : FD_STORE( long, *(out), (val) ); \
174 : *(out) += 8UL; \
175 : *(out_sz) -= 8UL; \
176 : } while( 0 )
177 :
178 : #define WRITE_PUBKEY( src, out, out_sz ) \
179 5545 : WRITE_BYTES( (src).uc, 32UL, out, out_sz )
180 :
181 : #define WRITE_BOOL( val, out, out_sz ) \
182 2172 : WRITE_U8( (uchar)!!(val), out, out_sz )
183 :
184 1086 : #define WRITE_OPTION WRITE_BOOL
185 :
186 : /**********************************************************************/
187 : /* Vote account state -- deserialization helpers */
188 : /* Each returns 0 on success, 1 on failure. */
189 : /**********************************************************************/
190 :
191 : /* Votes (is_v1_14_11=1 for v1_14_11 Vec<Lockout>, 0 for v3/v4
192 : Vec<LandedVote>) */
193 : static int
194 : deser_votes( fd_landed_vote_t * votes,
195 : int is_v1_14_11,
196 : uchar const ** ptr,
197 2948 : ulong * rem ) {
198 2948 : ulong votes_len;
199 2948 : READ_U64( votes_len, ptr, rem );
200 2943 : CHECK( votes_len<=MAX_LOCKOUT_HISTORY );
201 :
202 44934 : for( ulong i=0UL; i<votes_len; i++ ) {
203 42037 : fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy( votes );
204 42037 : if( is_v1_14_11 ) {
205 3223 : elem->latency = 0; /* Unused field for v1_14_11 */
206 38814 : } else {
207 38814 : READ_U8( elem->latency, ptr, rem );
208 38814 : }
209 42037 : READ_U64( elem->lockout.slot, ptr, rem );
210 42037 : READ_U32( elem->lockout.confirmation_count, ptr, rem );
211 42037 : }
212 2897 : return 0;
213 2897 : }
214 :
215 : /* Root slot (Option<u64>) */
216 : static int
217 : deser_root_slot( uchar * has_root_slot,
218 : ulong * root_slot,
219 : uchar const ** ptr,
220 2896 : ulong * rem ) {
221 2896 : uchar opt;
222 2896 : READ_OPTION( opt, ptr, rem );
223 2884 : *has_root_slot = opt;
224 2884 : if( opt ) {
225 1374 : READ_U64( *root_slot, ptr, rem );
226 1374 : }
227 2879 : return 0;
228 2884 : }
229 :
230 : /* Authorized voters (BTreeMap<u64, Pubkey>) */
231 : static int
232 : deser_authorized_voters( fd_vote_authorized_voter_t * pool,
233 : fd_vote_authorized_voters_treap_t * treap,
234 : uchar const ** ptr,
235 2880 : ulong * rem ) {
236 2880 : ulong authorized_voters_len;
237 2880 : READ_U64( authorized_voters_len, ptr, rem );
238 2874 : CHECK( authorized_voters_len<=MAX_AUTHORIZED_VOTERS );
239 5602 : for( ulong i=0UL; i<authorized_voters_len; i++ ) {
240 2865 : fd_vote_authorized_voter_t * voter = fd_vote_authorized_voters_pool_ele_acquire( pool );
241 2865 : READ_U64( voter->epoch, ptr, rem );
242 2865 : READ_PUBKEY( voter->pubkey, ptr, rem );
243 2865 : voter->prio = voter->pubkey.uc[0];
244 :
245 : /* Check for existing entries, overwrite if exists */
246 2865 : fd_vote_authorized_voter_t * existing_voter = fd_vote_authorized_voters_treap_ele_query( treap, voter->epoch, pool );
247 2865 : if( FD_UNLIKELY( existing_voter ) ) {
248 2 : fd_vote_authorized_voters_treap_ele_remove( treap, existing_voter, pool );
249 2 : fd_vote_authorized_voters_pool_ele_release( pool, existing_voter );
250 2 : }
251 :
252 2865 : fd_vote_authorized_voters_treap_ele_insert( treap, voter, pool );
253 2865 : }
254 2737 : return 0;
255 2737 : }
256 :
257 : /* Prior voters (fixed-size circular buffer of 32 entries)
258 : We can directly memcpy the entire buffer because the wire
259 : format is identical to the in-memory representation of the
260 : array. */
261 : static int
262 : deser_prior_voters( fd_vote_prior_voters_t * prior_voters,
263 : uchar const ** ptr,
264 1483 : ulong * rem ) {
265 1483 : READ_BYTES( prior_voters->buf, PRIOR_VOTERS_MAX*sizeof(fd_vote_prior_voter_t), ptr, rem );
266 1434 : READ_U64( prior_voters->idx, ptr, rem );
267 1434 : READ_BOOL( prior_voters->is_empty, ptr, rem );
268 1429 : return 0;
269 1434 : }
270 :
271 : /* Epoch credits (Vec<EpochCredits>) */
272 : static int
273 : deser_epoch_credits( fd_vote_epoch_credits_t * epoch_credits,
274 : uchar const ** ptr,
275 2684 : ulong * rem ) {
276 2684 : ulong epoch_credits_len;
277 2684 : READ_U64( epoch_credits_len, ptr, rem );
278 2682 : CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
279 83827 : for( ulong i=0UL; i<epoch_credits_len; i++ ) {
280 81151 : fd_vote_epoch_credits_t * elem = deq_fd_vote_epoch_credits_t_push_tail_nocopy( epoch_credits );
281 81151 : READ_BYTES( elem, sizeof(fd_vote_epoch_credits_t), ptr, rem );
282 81151 : }
283 2676 : return 0;
284 2678 : }
285 :
286 : /**********************************************************************/
287 : /* Vote account state -- serialization helpers */
288 : /* Each returns 0 on success, 1 on failure. */
289 : /**********************************************************************/
290 :
291 : /* Votes (is_v1_14_11=1 for v1_14_11 Vec<Lockout>, 0 for v3/v4
292 : Vec<LandedVote>) */
293 : static int
294 : ser_votes( fd_landed_vote_t const * votes,
295 : int is_v1_14_11,
296 : uchar ** out,
297 1086 : ulong * out_sz ) {
298 1086 : if( FD_UNLIKELY( votes==NULL ) ) {
299 0 : WRITE_U64( 0UL, out, out_sz );
300 0 : return 0;
301 0 : }
302 :
303 1086 : ulong votes_len = deq_fd_landed_vote_t_cnt( votes );
304 1086 : WRITE_U64( votes_len, out, out_sz );
305 :
306 1086 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
307 16314 : !deq_fd_landed_vote_t_iter_done( votes, iter );
308 15228 : iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
309 15228 : fd_landed_vote_t const * elem = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
310 15228 : if( !is_v1_14_11 ) {
311 15224 : WRITE_U8( elem->latency, out, out_sz );
312 15224 : }
313 15228 : WRITE_U64( elem->lockout.slot, out, out_sz );
314 15228 : WRITE_U32( elem->lockout.confirmation_count, out, out_sz );
315 15228 : }
316 1086 : return 0;
317 1086 : }
318 :
319 : /* Root slot (Option<u64>) */
320 : static int
321 : ser_root_slot( uchar has_root_slot,
322 : ulong root_slot,
323 : uchar ** out,
324 1086 : ulong * out_sz ) {
325 1086 : WRITE_BOOL( has_root_slot, out, out_sz );
326 1086 : if( has_root_slot ) {
327 528 : WRITE_U64( root_slot, out, out_sz );
328 528 : }
329 1086 : return 0;
330 1086 : }
331 :
332 : /* Authorized voters (BTreeMap<u64, Pubkey>) */
333 : static int
334 : ser_authorized_voters( fd_vote_authorized_voter_t const * pool,
335 : fd_vote_authorized_voters_treap_t const * treap,
336 : uchar ** out,
337 1086 : ulong * out_sz ) {
338 1086 : if( FD_UNLIKELY( treap==NULL ) ) {
339 0 : WRITE_U64( 0UL, out, out_sz );
340 0 : return 0;
341 0 : }
342 :
343 1086 : ulong len = fd_vote_authorized_voters_treap_ele_cnt( treap );
344 1086 : WRITE_U64( len, out, out_sz );
345 :
346 1086 : for( fd_vote_authorized_voters_treap_fwd_iter_t iter = fd_vote_authorized_voters_treap_fwd_iter_init( treap, pool );
347 2287 : !fd_vote_authorized_voters_treap_fwd_iter_done( iter );
348 1201 : iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, pool ) ) {
349 1201 : fd_vote_authorized_voter_t const * voter = fd_vote_authorized_voters_treap_fwd_iter_ele_const( iter, pool );
350 1201 : WRITE_U64( voter->epoch, out, out_sz );
351 1201 : WRITE_PUBKEY( voter->pubkey, out, out_sz );
352 1201 : }
353 1086 : return 0;
354 1086 : }
355 :
356 : /* Prior voters (fixed-size circular buffer of 32 entries)
357 : We can directly memcpy the entire buffer because the wire
358 : format is identical to the in-memory representation of the
359 : array. */
360 : static int
361 : ser_prior_voters( fd_vote_prior_voters_t const * prior_voters,
362 : uchar ** out,
363 0 : ulong * out_sz ) {
364 0 : WRITE_BYTES( prior_voters->buf, PRIOR_VOTERS_MAX*sizeof(fd_vote_prior_voter_t), out, out_sz );
365 0 : WRITE_U64( prior_voters->idx, out, out_sz );
366 0 : WRITE_BOOL( prior_voters->is_empty, out, out_sz );
367 0 : return 0;
368 0 : }
369 :
370 : /* Epoch credits (Vec<EpochCredits>) */
371 : static int
372 : ser_epoch_credits( fd_vote_epoch_credits_t const * epoch_credits,
373 : uchar ** out,
374 1086 : ulong * out_sz ) {
375 1086 : if( FD_UNLIKELY( epoch_credits==NULL ) ) {
376 0 : WRITE_U64( 0UL, out, out_sz );
377 0 : return 0;
378 0 : }
379 :
380 1086 : ulong len = deq_fd_vote_epoch_credits_t_cnt( epoch_credits );
381 1086 : WRITE_U64( len, out, out_sz );
382 :
383 1086 : for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( epoch_credits );
384 30724 : !deq_fd_vote_epoch_credits_t_iter_done( epoch_credits, iter );
385 29638 : iter = deq_fd_vote_epoch_credits_t_iter_next( epoch_credits, iter ) ) {
386 29638 : fd_vote_epoch_credits_t const * elem = deq_fd_vote_epoch_credits_t_iter_ele_const( epoch_credits, iter );
387 29638 : WRITE_BYTES( elem, sizeof(fd_vote_epoch_credits_t), out, out_sz );
388 29638 : }
389 1086 : return 0;
390 1086 : }
391 :
392 : /**********************************************************************/
393 : /* Wire layout offsets */
394 : /**********************************************************************/
395 :
396 : /* Fixed-offset byte positions within the bincode wire format.
397 : For v1_14_11 and v3 the fixed prefix is: discriminant, node_pubkey,
398 : authorized_withdrawer, commission. For v4 the prefix additionally
399 : includes inflation_rewards_collector, block_revenue_collector,
400 : inflation_rewards_commission_bps, block_revenue_commission_bps,
401 : and pending_delegator_rewards before the first variable field. */
402 :
403 101 : #define WIRE_OFF_NODE_PUBKEY (4UL)
404 0 : #define WIRE_OFF_V1V3_COMMISSION (68UL) /* 4 + 32 + 32 */
405 101 : #define WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR (68UL) /* 4 + 32 + 32 */
406 101 : #define WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR (100UL) /* 4 + 32 + 32 + 32 */
407 : #define WIRE_OFF_V4_COMMISSION_BPS (132UL) /* 4 + 32 + 32 + 32 + 32 */
408 : #define WIRE_OFF_V4_BLOCK_REVENUE_COMMISSION_BPS (134UL) /* 4 + 32 + 32 + 32 + 32 + 2 */
409 0 : #define WIRE_OFF_V4_PENDING_DELEGATOR_REWARDS (136UL) /* 4 + 32 + 32 + 32 + 32 + 2 + 2 */
410 :
411 : /* Byte size of a Lockout on the wire: u64 slot + u32 confirmation_count */
412 0 : #define WIRE_LOCKOUT_SZ (12UL)
413 : /* Byte size of a LandedVote on the wire: u8 latency + u64 slot + u32 confirmation_count */
414 1086 : #define WIRE_LANDED_VOTE_SZ (13UL)
415 : /* Byte size of an authorized voter entry: u64 epoch + 32B pubkey */
416 1086 : #define WIRE_AUTH_VOTER_SZ (40UL)
417 : /* Byte size of prior_voters on the wire: 32 * 48B + 8B idx + 1B is_empty */
418 0 : #define WIRE_PRIOR_VOTERS_SZ (PRIOR_VOTERS_MAX * 48UL + 8UL + 1UL)
419 :
420 : /* Offset of the votes vector (v1/v3) or BLS option (v4) — first byte
421 : after the fixed-offset prefix. */
422 0 : #define WIRE_VOTES_OFF_V1_V3 (69UL) /* 4 + 32 + 32 + 1 */
423 1493 : #define WIRE_BLS_OFF_V4 (144UL) /* 4 + 32 + 32 + 32 + 32 + 2 + 2 + 8 */
424 :
425 : /* Offset of a Vote State V5 field */
426 0 : #define V5_OFF( f ) (4UL+offsetof( fd_vote_state_v5_t, f ))
427 :
428 : FD_STATIC_ASSERT( V5_OFF( node_pubkey )==WIRE_OFF_NODE_PUBKEY, v5_layout );
429 : FD_STATIC_ASSERT( V5_OFF( inflation_rewards_collector )==WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR, v5_layout );
430 : FD_STATIC_ASSERT( V5_OFF( block_revenue_collector )==WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR, v5_layout );
431 : FD_STATIC_ASSERT( sizeof(fd_vote_state_v5_authorized_voter_t)==WIRE_AUTH_VOTER_SZ, v5_layout );
432 :
433 : static void
434 : v5_commission_load( uchar const * data,
435 : uint kind,
436 0 : fd_vote_state_v5_commission_t * out ) {
437 0 : int block = kind==fd_commission_kind_enum_block_revenue;
438 0 : ulong len_off = block ? V5_OFF( block_revenue_commission_bps_len ) : V5_OFF( inflation_rewards_commission_bps_len );
439 0 : ulong epochs_off = block ? V5_OFF( block_revenue_commission_bps_epochs ) : V5_OFF( inflation_rewards_commission_bps_epochs );
440 0 : ulong bps_off = block ? V5_OFF( block_revenue_commission_bps ) : V5_OFF( inflation_rewards_commission_bps );
441 0 : out->len = FD_LOAD( uint, data+len_off );
442 0 : for( ulong i=0UL; i<FD_VOTE_STATE_V5_COMMISSION_MAX; i++ ) {
443 0 : out->epoch[ i ] = FD_LOAD( ulong, data+epochs_off+i*sizeof(ulong) );
444 0 : out->bps [ i ] = FD_LOAD( ushort, data+bps_off +i*sizeof(ushort) );
445 0 : }
446 0 : }
447 :
448 : /**********************************************************************/
449 : /* Vote account state -- direct field accessors */
450 : /* These read fields directly from raw bincode-encoded vote account */
451 : /* data without full deserialization. */
452 : /**********************************************************************/
453 :
454 : int
455 : fd_vote_account_node_pubkey( uchar const * data,
456 : ulong data_sz,
457 101 : fd_pubkey_t * out ) {
458 101 : CHECK( data_sz>=WIRE_OFF_NODE_PUBKEY+32UL );
459 101 : fd_memcpy( out, data+WIRE_OFF_NODE_PUBKEY, 32UL );
460 101 : return 0;
461 101 : }
462 :
463 : int
464 : fd_vote_account_commission_bps( uchar const * data,
465 : ulong data_sz,
466 : int commission_rate_in_basis_points,
467 101 : ushort * out ) {
468 101 : uchar const * ptr = data;
469 101 : ulong remaining = data_sz;
470 :
471 101 : uint discriminant;
472 101 : READ_U32( discriminant, &ptr, &remaining );
473 :
474 101 : switch( discriminant ) {
475 0 : case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
476 0 : case fd_vote_state_versioned_enum_v3:
477 0 : CHECK( data_sz>WIRE_OFF_V1V3_COMMISSION );
478 0 : *out = data[ WIRE_OFF_V1V3_COMMISSION ] * 100U;
479 0 : return 0;
480 0 : case fd_vote_state_versioned_enum_v5: {
481 0 : CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
482 0 : fd_vote_state_v5_commission_t c;
483 0 : v5_commission_load( data, fd_commission_kind_enum_inflation_rewards, &c );
484 0 : CHECK( fd_vote_state_v5_commission_is_valid( &c ) );
485 0 : *out = fd_vote_state_v5_commission_latest( &c );
486 0 : if( !commission_rate_in_basis_points ) *out = (ushort)( (*out/100U)*100U );
487 0 : return 0;
488 0 : }
489 101 : case fd_vote_state_versioned_enum_v4:
490 101 : CHECK( data_sz>=WIRE_OFF_V4_COMMISSION_BPS+2UL );
491 101 : *out = FD_LOAD( ushort, data+WIRE_OFF_V4_COMMISSION_BPS );
492 :
493 : /* Round down to the nearest whole percentage if SIMD-0291 is not
494 : yet active. */
495 101 : if( !commission_rate_in_basis_points ) {
496 0 : *out = (*out / 100U) * 100U;
497 0 : }
498 101 : return 0;
499 0 : default:
500 0 : return 1;
501 101 : }
502 101 : }
503 :
504 : int
505 : fd_vote_account_block_revenue_commission_bps( uchar const * data,
506 : ulong data_sz,
507 101 : ushort * out ) {
508 101 : uchar const * ptr = data;
509 101 : ulong remaining = data_sz;
510 :
511 101 : uint discriminant;
512 101 : READ_U32( discriminant, &ptr, &remaining );
513 :
514 101 : switch( discriminant ) {
515 0 : case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
516 0 : case fd_vote_state_versioned_enum_v3:
517 0 : *out = FD_VOTE_DEFAULT_BLOCK_REVENUE_COMMISSION_BPS;
518 0 : return 0;
519 0 : case fd_vote_state_versioned_enum_v5: {
520 0 : CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
521 0 : fd_vote_state_v5_commission_t c;
522 0 : v5_commission_load( data, fd_commission_kind_enum_block_revenue, &c );
523 0 : CHECK( fd_vote_state_v5_commission_is_valid( &c ) );
524 0 : *out = fd_vote_state_v5_commission_latest( &c );
525 0 : return 0;
526 0 : }
527 101 : case fd_vote_state_versioned_enum_v4:
528 101 : CHECK( data_sz>=WIRE_OFF_V4_BLOCK_REVENUE_COMMISSION_BPS+2UL );
529 101 : *out = FD_LOAD( ushort, data+WIRE_OFF_V4_BLOCK_REVENUE_COMMISSION_BPS );
530 101 : return 0;
531 0 : default:
532 0 : return 1;
533 101 : }
534 101 : }
535 :
536 : int
537 : fd_vote_account_pending_delegator_rewards( uchar const * data,
538 : ulong data_sz,
539 101 : ulong * out ) {
540 101 : uchar const * ptr = data;
541 101 : ulong remaining = data_sz;
542 :
543 101 : uint discriminant;
544 101 : READ_U32( discriminant, &ptr, &remaining );
545 :
546 101 : switch( discriminant ) {
547 0 : case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
548 0 : case fd_vote_state_versioned_enum_v3:
549 0 : *out = 0UL;
550 0 : return 0;
551 101 : case fd_vote_state_versioned_enum_v4:
552 101 : CHECK( data_sz==FD_VOTE_STATE_V4_SZ );
553 101 : *out = FD_LOAD( ulong, data+WIRE_OFF_V4_PENDING_DELEGATOR_REWARDS );
554 101 : return 0;
555 0 : case fd_vote_state_versioned_enum_v5:
556 0 : CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
557 0 : *out = FD_LOAD( ulong, data+V5_OFF( pending_delegator_rewards ) );
558 0 : return 0;
559 0 : default:
560 0 : return 1;
561 101 : }
562 101 : }
563 :
564 : static ulong
565 : pending_delegator_rewards_off( uchar const * data,
566 0 : ulong data_sz ) {
567 0 : if( data_sz<4UL ) return 0UL;
568 0 : switch( FD_LOAD( uint, data ) ) {
569 0 : case fd_vote_state_versioned_enum_v4: return fd_ulong_if( data_sz==FD_VOTE_STATE_V4_SZ, WIRE_OFF_V4_PENDING_DELEGATOR_REWARDS, 0UL );
570 0 : case fd_vote_state_versioned_enum_v5: return fd_ulong_if( data_sz==FD_VOTE_STATE_V5_SZ, V5_OFF( pending_delegator_rewards ), 0UL );
571 0 : default: return 0UL;
572 0 : }
573 0 : }
574 :
575 : int
576 : fd_vote_account_add_pending_delegator_rewards( uchar * data,
577 : ulong data_sz,
578 0 : ulong lamports ) {
579 0 : ulong off = pending_delegator_rewards_off( data, data_sz );
580 0 : if( FD_UNLIKELY( !off ) ) return 1;
581 0 : ulong cur = FD_LOAD( ulong, data+off );
582 0 : ulong sum;
583 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow( cur, lamports, &sum ) ) ) return 2;
584 0 : FD_STORE( ulong, data+off, sum );
585 0 : return 0;
586 0 : }
587 :
588 : int
589 : fd_vote_account_reset_pending_delegator_rewards( uchar * data,
590 : ulong data_sz,
591 0 : ulong * old_out ) {
592 0 : ulong off = pending_delegator_rewards_off( data, data_sz );
593 0 : if( FD_UNLIKELY( !off ) ) return 1;
594 0 : *old_out = FD_LOAD( ulong, data+off );
595 0 : FD_STORE( ulong, data+off, 0UL );
596 0 : return 0;
597 0 : }
598 :
599 : int
600 : fd_vote_account_collectors( uchar const * data,
601 : ulong data_sz,
602 : fd_pubkey_t const * vote_pubkey,
603 : fd_pubkey_t const * node_pubkey,
604 : fd_pubkey_t * inflation_rewards_collector_out,
605 101 : fd_pubkey_t * block_revenue_collector_out ) {
606 101 : uchar const * ptr = data;
607 101 : ulong remaining = data_sz;
608 :
609 101 : uint discriminant;
610 101 : READ_U32( discriminant, &ptr, &remaining );
611 :
612 101 : switch( discriminant ) {
613 0 : case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
614 0 : case fd_vote_state_versioned_enum_v3:
615 : /* Pre-v4 vote states have no collector fields: default to the
616 : vote account (inflation) and the node identity (block
617 : revenue). */
618 0 : *inflation_rewards_collector_out = *vote_pubkey;
619 0 : *block_revenue_collector_out = *node_pubkey;
620 0 : return 0;
621 0 : case fd_vote_state_versioned_enum_v5:
622 0 : CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
623 0 : __attribute__((fallthrough));
624 101 : case fd_vote_state_versioned_enum_v4:
625 101 : CHECK( data_sz>=WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR+32UL );
626 101 : fd_memcpy( inflation_rewards_collector_out, data+WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR, 32UL );
627 101 : fd_memcpy( block_revenue_collector_out, data+WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR, 32UL );
628 101 : return 0;
629 0 : default:
630 0 : return 1;
631 101 : }
632 101 : }
633 :
634 : /* Seeks past variable-length fields to the start of epoch_credits.
635 : On success sets *out_ptr to the first entry and *out_cnt to the
636 : entry count. Returns 0 on success, 1 on error. */
637 :
638 : /* seek_authorized_voters positions *out_ptr at the first entry of the
639 : authorized_voters map (sorted by ascending epoch, WIRE_AUTH_VOTER_SZ
640 : bytes each) and stores the entry count in *out_cnt. All entries
641 : are bounds checked. Returns 0 on success, 1 on malformed data. */
642 :
643 : static int
644 : seek_authorized_voters( uchar const * data,
645 : ulong data_sz,
646 : uint * out_discriminant,
647 : uchar const ** out_ptr,
648 : ulong * out_remaining,
649 101 : ulong * out_cnt ) {
650 101 : uchar const * ptr = data;
651 101 : ulong remaining = data_sz;
652 :
653 101 : uint discriminant;
654 101 : READ_U32( discriminant, &ptr, &remaining );
655 :
656 101 : switch( discriminant ) {
657 0 : case fd_vote_state_versioned_enum_v1_14_11: {
658 0 : SKIP_BYTES( WIRE_VOTES_OFF_V1_V3-4UL, &ptr, &remaining );
659 :
660 : /* Skip votes Vec<Lockout> */
661 0 : ulong votes_len;
662 0 : READ_U64( votes_len, &ptr, &remaining );
663 0 : CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LOCKOUT_SZ );
664 0 : SKIP_BYTES( votes_len*WIRE_LOCKOUT_SZ, &ptr, &remaining );
665 0 : break;
666 0 : }
667 :
668 0 : case fd_vote_state_versioned_enum_v3: {
669 0 : SKIP_BYTES( WIRE_VOTES_OFF_V1_V3-4UL, &ptr, &remaining );
670 :
671 : /* Skip votes Vec<LandedVote> */
672 0 : ulong votes_len;
673 0 : READ_U64( votes_len, &ptr, &remaining );
674 0 : CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LANDED_VOTE_SZ );
675 0 : SKIP_BYTES( votes_len*WIRE_LANDED_VOTE_SZ, &ptr, &remaining );
676 0 : break;
677 0 : }
678 :
679 101 : case fd_vote_state_versioned_enum_v4: {
680 101 : SKIP_BYTES( WIRE_BLS_OFF_V4-4UL, &ptr, &remaining );
681 :
682 : /* Skip Option<bls_pubkey_compressed> */
683 101 : uchar has_bls_pubkey;
684 101 : READ_U8( has_bls_pubkey, &ptr, &remaining );
685 101 : if( has_bls_pubkey ) {
686 101 : SKIP_BYTES( FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &remaining );
687 101 : }
688 :
689 : /* Skip votes Vec<LandedVote> */
690 101 : ulong votes_len;
691 101 : READ_U64( votes_len, &ptr, &remaining );
692 101 : CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LANDED_VOTE_SZ );
693 101 : SKIP_BYTES( votes_len*WIRE_LANDED_VOTE_SZ, &ptr, &remaining );
694 101 : break;
695 101 : }
696 :
697 101 : case fd_vote_state_versioned_enum_v5: {
698 0 : CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
699 0 : ulong cnt = FD_LOAD( uint, data+V5_OFF( authorized_voters_len ) );
700 0 : CHECK( cnt<=MAX_AUTHORIZED_VOTERS );
701 0 : *out_discriminant = discriminant;
702 0 : *out_ptr = data+V5_OFF( authorized_voters );
703 0 : *out_remaining = data_sz-V5_OFF( authorized_voters );
704 0 : *out_cnt = cnt;
705 0 : return 0;
706 0 : }
707 :
708 0 : default:
709 0 : return 1;
710 101 : }
711 :
712 : /* Skip root_slot Option<u64> */
713 101 : uchar has_root_slot;
714 101 : READ_U8( has_root_slot, &ptr, &remaining );
715 101 : if( has_root_slot ) {
716 30 : SKIP_BYTES( 8UL, &ptr, &remaining );
717 30 : }
718 :
719 : /* Now at authorized_voters BTreeMap<u64, Pubkey> */
720 101 : ulong authorized_voters_len;
721 101 : READ_U64( authorized_voters_len, &ptr, &remaining );
722 101 : CHECK_U64_MUL_OVERFLOW( authorized_voters_len, WIRE_AUTH_VOTER_SZ );
723 101 : CHECK( authorized_voters_len*WIRE_AUTH_VOTER_SZ<=remaining );
724 :
725 101 : *out_discriminant = discriminant;
726 101 : *out_ptr = ptr;
727 101 : *out_remaining = remaining;
728 101 : *out_cnt = authorized_voters_len;
729 101 : return 0;
730 101 : }
731 :
732 : static int
733 : seek_epoch_credits( uchar const * data,
734 : ulong data_sz,
735 : fd_vote_epoch_credits_t const ** out_ptr,
736 101 : ulong * out_cnt ) {
737 101 : if( data_sz>=4UL && FD_LOAD( uint, data )==fd_vote_state_versioned_enum_v5 ) {
738 0 : CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
739 0 : ulong cnt = FD_LOAD( uint, data+V5_OFF( accumulated_lamports_len ) );
740 0 : CHECK( cnt<=FD_VOTE_STATE_V5_ACCUMULATED_LAMPORTS_MAX );
741 0 : *out_ptr = fd_type_pun_const( data+V5_OFF( accumulated_lamports ) );
742 0 : *out_cnt = cnt;
743 0 : return 0;
744 0 : }
745 :
746 101 : uint discriminant;
747 101 : uchar const * ptr;
748 101 : ulong remaining;
749 101 : ulong authorized_voters_len;
750 101 : CHECK( !seek_authorized_voters( data, data_sz, &discriminant, &ptr, &remaining, &authorized_voters_len ) );
751 101 : SKIP_BYTES( authorized_voters_len*WIRE_AUTH_VOTER_SZ, &ptr, &remaining );
752 :
753 : /* Skip prior_voters (fixed size), absent in v4 */
754 101 : if( discriminant!=fd_vote_state_versioned_enum_v4 ) {
755 0 : SKIP_BYTES( WIRE_PRIOR_VOTERS_SZ, &ptr, &remaining );
756 0 : }
757 :
758 : /* Now at epoch_credits deque */
759 101 : ulong epoch_credits_len;
760 101 : READ_U64( epoch_credits_len, &ptr, &remaining );
761 101 : CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
762 101 : CHECK_U64_MUL_OVERFLOW( epoch_credits_len, sizeof(fd_vote_epoch_credits_t) );
763 101 : CHECK( epoch_credits_len*sizeof(fd_vote_epoch_credits_t)<=remaining );
764 :
765 101 : *out_ptr = (fd_vote_epoch_credits_t const *)ptr;
766 101 : *out_cnt = epoch_credits_len;
767 101 : return 0;
768 101 : }
769 :
770 : int
771 : fd_vote_account_authorized_voter( uchar const * data,
772 : ulong data_sz,
773 : ulong epoch,
774 0 : fd_pubkey_t * out ) {
775 0 : uint discriminant;
776 0 : uchar const * ptr;
777 0 : ulong remaining;
778 0 : ulong cnt;
779 0 : CHECK( !seek_authorized_voters( data, data_sz, &discriminant, &ptr, &remaining, &cnt ) );
780 :
781 : /* Take the last entry whose epoch is at or before the requested one. */
782 0 : int found = 0;
783 0 : for( ulong i=0UL; i<cnt; i++, ptr+=WIRE_AUTH_VOTER_SZ ) {
784 0 : if( FD_LOAD( ulong, ptr )>epoch ) continue;
785 0 : fd_memcpy( out, ptr+8UL, 32UL );
786 0 : found = 1;
787 0 : }
788 :
789 0 : return !found;
790 0 : }
791 :
792 : int
793 : fd_vote_account_last_timestamp( uchar const * data,
794 : ulong data_sz,
795 0 : fd_vote_block_timestamp_t * out ) {
796 0 : if( data_sz>=4UL && FD_LOAD( uint, data )==fd_vote_state_versioned_enum_v5 ) {
797 0 : CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
798 0 : fd_memcpy( out, data+V5_OFF( last_timestamp ), sizeof(fd_vote_block_timestamp_t) );
799 0 : return 0;
800 0 : }
801 :
802 0 : fd_vote_epoch_credits_t const * epoch_credits_ptr;
803 0 : ulong epoch_credits_len;
804 0 : CHECK( !seek_epoch_credits( data, data_sz, &epoch_credits_ptr, &epoch_credits_len ) );
805 :
806 0 : uchar const * timestamp_ptr = (uchar const *)( epoch_credits_ptr + epoch_credits_len );
807 0 : CHECK( timestamp_ptr+sizeof(fd_vote_block_timestamp_t)<=data+data_sz );
808 :
809 0 : fd_memcpy( out, timestamp_ptr, sizeof(fd_vote_block_timestamp_t) );
810 0 : return 0;
811 0 : }
812 :
813 : int
814 : fd_vote_account_has_bls_pubkey( uchar const * data,
815 306 : ulong data_sz ) {
816 306 : if( FD_UNLIKELY( data_sz<4UL ) ) return 0;
817 306 : uint discriminant = FD_LOAD( uint, data );
818 306 : if( discriminant==fd_vote_state_versioned_enum_v5 ) return data_sz==FD_VOTE_STATE_V5_SZ;
819 306 : if( discriminant!=fd_vote_state_versioned_enum_v4 ) return 0;
820 306 : if( FD_UNLIKELY( data_sz<WIRE_BLS_OFF_V4+1UL ) ) return 0;
821 306 : return !!data[ WIRE_BLS_OFF_V4 ];
822 306 : }
823 :
824 : int
825 : fd_vote_account_bls_pubkey( uchar const * data,
826 : ulong data_sz,
827 101 : uchar out[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
828 101 : if( FD_UNLIKELY( !fd_vote_account_has_bls_pubkey( data, data_sz ) ) ) return 1;
829 101 : if( FD_LOAD( uint, data )==fd_vote_state_versioned_enum_v5 ) {
830 0 : fd_memcpy( out, data+V5_OFF( bls_pubkey_compressed ), FD_BLS_PUBKEY_COMPRESSED_SZ );
831 0 : return 0;
832 0 : }
833 101 : if( FD_UNLIKELY( data_sz<WIRE_BLS_OFF_V4+1UL+FD_BLS_PUBKEY_COMPRESSED_SZ ) ) return 1;
834 101 : fd_memcpy( out, data+WIRE_BLS_OFF_V4+1UL, FD_BLS_PUBKEY_COMPRESSED_SZ );
835 101 : return 0;
836 101 : }
837 :
838 : fd_vote_epoch_credits_t const *
839 : fd_vote_account_epoch_credits( uchar const * data,
840 : ulong data_sz,
841 101 : ulong * cnt ) {
842 101 : fd_vote_epoch_credits_t const * ptr;
843 101 : CHECK_RET_NULL( !seek_epoch_credits( data, data_sz, &ptr, cnt ) );
844 101 : return ptr;
845 101 : }
846 :
847 : /**********************************************************************/
848 : /* Vote account state -- public API */
849 : /**********************************************************************/
850 :
851 : int
852 : fd_vote_account_is_valid_v5( uchar const * data,
853 102 : ulong data_sz ) {
854 102 : if( FD_UNLIKELY( data_sz!=FD_VOTE_STATE_V5_SZ || FD_LOAD( uint, data )!=fd_vote_state_versioned_enum_v5 ) ) return 0;
855 :
856 0 : fd_validator_location_t location = {
857 0 : .x = FD_LOAD( int, data+V5_OFF( location.x ) ),
858 0 : .y = FD_LOAD( int, data+V5_OFF( location.y ) ),
859 0 : .z = FD_LOAD( int, data+V5_OFF( location.z ) )
860 0 : };
861 0 : if( FD_UNLIKELY( !fd_validator_location_is_valid( &location ) ) ) return 0;
862 :
863 0 : fd_vote_state_v5_commission_t commission;
864 0 : v5_commission_load( data, fd_commission_kind_enum_inflation_rewards, &commission );
865 0 : if( FD_UNLIKELY( !fd_vote_state_v5_commission_is_valid( &commission ) ) ) return 0;
866 0 : v5_commission_load( data, fd_commission_kind_enum_block_revenue, &commission );
867 0 : if( FD_UNLIKELY( !fd_vote_state_v5_commission_is_valid( &commission ) ) ) return 0;
868 :
869 0 : ulong av_len = FD_LOAD( uint, data+V5_OFF( authorized_voters_len ) );
870 0 : uchar const * av = data+V5_OFF( authorized_voters );
871 0 : if( FD_UNLIKELY( av_len>MAX_AUTHORIZED_VOTERS ) ) return 0;
872 0 : for( ulong i=1UL; i<av_len; i++ ) {
873 0 : if( FD_UNLIKELY( FD_LOAD( ulong, av+i*WIRE_AUTH_VOTER_SZ )<=FD_LOAD( ulong, av+(i-1UL)*WIRE_AUTH_VOTER_SZ ) ) ) return 0;
874 0 : }
875 :
876 0 : ulong ec_len = FD_LOAD( uint, data+V5_OFF( accumulated_lamports_len ) );
877 0 : uchar const * ec = data+V5_OFF( accumulated_lamports );
878 0 : if( FD_UNLIKELY( ec_len>FD_VOTE_STATE_V5_ACCUMULATED_LAMPORTS_MAX ) ) return 0;
879 :
880 0 : if( FD_UNLIKELY( FD_LOAD( ulong, data+V5_OFF( rank_epoch ) )!=ULONG_MAX || FD_LOAD( ushort, data+V5_OFF( rank ) )!=USHORT_MAX ) ) return 0;
881 :
882 0 : return fd_mem_iszero( av+av_len*WIRE_AUTH_VOTER_SZ, (MAX_AUTHORIZED_VOTERS-av_len)*WIRE_AUTH_VOTER_SZ ) &&
883 0 : fd_mem_iszero( ec+ec_len*sizeof(fd_vote_epoch_credits_t),
884 0 : (FD_VOTE_STATE_V5_ACCUMULATED_LAMPORTS_MAX-ec_len)*sizeof(fd_vote_epoch_credits_t) ) &&
885 0 : fd_mem_iszero( data+FD_VOTE_STATE_V5_PAD_OFF, FD_VOTE_STATE_V5_PAD_SZ );
886 0 : }
887 :
888 : fd_validator_location_t
889 : fd_vote_account_location( uchar const * data,
890 101 : ulong data_sz ) {
891 101 : if( !fd_vote_account_is_valid_v5( data, data_sz ) ) return (fd_validator_location_t){0};
892 0 : return FD_LOAD( fd_validator_location_t, data+V5_OFF( location ) );
893 101 : }
894 :
895 : fd_vote_state_versioned_t *
896 : fd_vote_state_versioned_new( fd_vote_state_versioned_t * self,
897 4156 : uint kind ) {
898 4156 : if( FD_UNLIKELY( !self ) ) {
899 0 : FD_LOG_WARNING(( "NULL mem" ));
900 0 : return NULL;
901 0 : }
902 :
903 : /* Init and join data structures */
904 4156 : fd_landed_vote_t * votes = deq_fd_landed_vote_t_join(
905 4156 : deq_fd_landed_vote_t_new( self->landed_votes_mem, MAX_LOCKOUT_HISTORY_CAPACITY )
906 4156 : );
907 4156 : fd_vote_epoch_credits_t * epoch_credits = deq_fd_vote_epoch_credits_t_join(
908 4156 : deq_fd_vote_epoch_credits_t_new( self->epoch_credits_mem )
909 4156 : );
910 4156 : fd_vote_authorized_voter_t * authorized_voters_pool = fd_vote_authorized_voters_pool_join(
911 4156 : fd_vote_authorized_voters_pool_new( self->authorized_voters_pool_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
912 4156 : );
913 4156 : fd_vote_authorized_voters_treap_t * authorized_voters_treap = fd_vote_authorized_voters_treap_join(
914 4156 : fd_vote_authorized_voters_treap_new( self->authorized_voters_treap_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
915 4156 : );
916 :
917 4156 : self->kind = kind;
918 4156 : switch( kind ) {
919 825 : case fd_vote_state_versioned_enum_uninitialized:
920 825 : break;
921 281 : case fd_vote_state_versioned_enum_v1_14_11:
922 281 : memset( &self->v1_14_11, 0, sizeof(fd_vote_state_1_14_11_t) );
923 281 : self->v1_14_11.votes = votes;
924 281 : self->v1_14_11.epoch_credits = epoch_credits;
925 281 : self->v1_14_11.authorized_voters.pool = authorized_voters_pool;
926 281 : self->v1_14_11.authorized_voters.treap = authorized_voters_treap;
927 281 : break;
928 1441 : case fd_vote_state_versioned_enum_v3:
929 1441 : memset( &self->v3, 0, sizeof(fd_vote_state_v3_t) );
930 1441 : self->v3.votes = votes;
931 1441 : self->v3.epoch_credits = epoch_credits;
932 1441 : self->v3.authorized_voters.pool = authorized_voters_pool;
933 1441 : self->v3.authorized_voters.treap = authorized_voters_treap;
934 1441 : break;
935 1424 : case fd_vote_state_versioned_enum_v4:
936 1424 : memset( &self->v4, 0, sizeof(fd_vote_state_v4_t) );
937 1424 : self->v4.votes = votes;
938 1424 : self->v4.epoch_credits = epoch_credits;
939 1424 : self->v4.authorized_voters.pool = authorized_voters_pool;
940 1424 : self->v4.authorized_voters.treap = authorized_voters_treap;
941 1424 : break;
942 1 : case fd_vote_state_versioned_enum_v5:
943 1 : memset( &self->v5, 0, sizeof(fd_vote_state_v5_t) );
944 1 : self->v5.inflation_rewards_commission_bps_len = 1;
945 1 : self->v5.block_revenue_commission_bps_len = 1;
946 1 : self->v5.latest_vote = ULONG_MAX;
947 1 : self->v5.root_slot = ULONG_MAX;
948 1 : self->v5.rank_epoch = ULONG_MAX;
949 1 : self->v5.rank = USHORT_MAX;
950 1 : break;
951 192 : default:
952 192 : return NULL;
953 4156 : }
954 :
955 3965 : return self;
956 4156 : }
957 :
958 : static int
959 : fd_vote_state_versioned_deserialize_inner( fd_vote_state_versioned_t * self,
960 : uchar const * payload,
961 4014 : ulong payload_sz ) {
962 4014 : CHECK( self!=NULL );
963 4014 : CHECK( payload!=NULL );
964 :
965 4014 : uchar const * ptr = payload;
966 4014 : ulong rem = payload_sz;
967 :
968 4014 : uint kind;
969 4014 : READ_U32( kind, &ptr, &rem );
970 :
971 3989 : CHECK( fd_vote_state_versioned_new( self, kind )!=NULL );
972 :
973 3797 : switch( self->kind ) {
974 825 : case fd_vote_state_versioned_enum_uninitialized:
975 : /* No-op, nothing to decode */
976 825 : return 0;
977 :
978 281 : case fd_vote_state_versioned_enum_v1_14_11: {
979 281 : READ_PUBKEY( self->v1_14_11.node_pubkey, &ptr, &rem );
980 278 : READ_PUBKEY( self->v1_14_11.authorized_withdrawer, &ptr, &rem );
981 272 : READ_U8( self->v1_14_11.commission, &ptr, &rem );
982 270 : CHECK( !deser_votes( self->v1_14_11.votes, 1, &ptr, &rem ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
983 248 : CHECK( !deser_root_slot( &self->v1_14_11.has_root_slot, &self->v1_14_11.root_slot, &ptr, &rem ) );
984 239 : CHECK( !deser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &ptr, &rem ) );
985 179 : CHECK( !deser_prior_voters( &self->v1_14_11.prior_voters, &ptr, &rem ) );
986 150 : CHECK( !deser_epoch_credits( self->v1_14_11.epoch_credits, &ptr, &rem ) );
987 147 : READ_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
988 145 : break;
989 147 : }
990 :
991 1442 : case fd_vote_state_versioned_enum_v3: {
992 1442 : READ_PUBKEY( self->v3.node_pubkey, &ptr, &rem );
993 1431 : READ_PUBKEY( self->v3.authorized_withdrawer, &ptr, &rem );
994 1427 : READ_U8( self->v3.commission, &ptr, &rem );
995 1424 : CHECK( !deser_votes( self->v3.votes, 0, &ptr, &rem ) );
996 1395 : CHECK( !deser_root_slot( &self->v3.has_root_slot, &self->v3.root_slot, &ptr, &rem ) );
997 1387 : CHECK( !deser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &ptr, &rem ) );
998 1304 : CHECK( !deser_prior_voters( &self->v3.prior_voters, &ptr, &rem ) );
999 1279 : CHECK( !deser_epoch_credits( self->v3.epoch_credits, &ptr, &rem ) );
1000 1274 : READ_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
1001 1270 : break;
1002 1274 : }
1003 :
1004 1270 : case fd_vote_state_versioned_enum_v4: {
1005 1259 : READ_PUBKEY( self->v4.node_pubkey, &ptr, &rem );
1006 1257 : READ_PUBKEY( self->v4.authorized_withdrawer, &ptr, &rem );
1007 1257 : READ_PUBKEY( self->v4.inflation_rewards_collector, &ptr, &rem );
1008 1257 : READ_PUBKEY( self->v4.block_revenue_collector, &ptr, &rem );
1009 1257 : READ_U16( self->v4.inflation_rewards_commission_bps, &ptr, &rem );
1010 1257 : READ_U16( self->v4.block_revenue_commission_bps, &ptr, &rem );
1011 1257 : READ_U64( self->v4.pending_delegator_rewards, &ptr, &rem );
1012 :
1013 : /* Option<[u8; 48]> */
1014 1257 : READ_OPTION( self->v4.has_bls_pubkey_compressed, &ptr, &rem );
1015 1257 : if( self->v4.has_bls_pubkey_compressed ) {
1016 350 : READ_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &rem );
1017 350 : }
1018 :
1019 1257 : CHECK( !deser_votes( self->v4.votes, 0, &ptr, &rem ) );
1020 1257 : CHECK( !deser_root_slot( &self->v4.has_root_slot, &self->v4.root_slot, &ptr, &rem ) );
1021 1257 : CHECK( !deser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &ptr, &rem ) );
1022 1257 : CHECK( !deser_epoch_credits( self->v4.epoch_credits, &ptr, &rem ) ); /* v4 has no prior_voters */
1023 1257 : READ_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
1024 1257 : break;
1025 1257 : }
1026 :
1027 1257 : case fd_vote_state_versioned_enum_v5:
1028 1 : CHECK( fd_vote_account_is_valid_v5( payload, payload_sz ) );
1029 0 : fd_memcpy( &self->v5, ptr, sizeof(fd_vote_state_v5_t) );
1030 0 : break;
1031 :
1032 0 : default:
1033 0 : return 1;
1034 3797 : }
1035 :
1036 2671 : return 0;
1037 3797 : }
1038 :
1039 : fd_vote_state_versioned_t *
1040 : fd_vote_state_versioned_deserialize( fd_vote_state_versioned_t * self,
1041 : uchar const * payload,
1042 4013 : ulong payload_sz ) {
1043 4013 : CHECK_RET_NULL( !fd_vote_state_versioned_deserialize_inner( self, payload, payload_sz ) );
1044 3485 : return self;
1045 4013 : }
1046 :
1047 : int
1048 : fd_vote_state_versioned_serialize( fd_vote_state_versioned_t const * self,
1049 : uchar * buf,
1050 1086 : ulong buf_sz ) {
1051 1086 : CHECK( self!=NULL );
1052 1086 : CHECK( buf!=NULL );
1053 :
1054 1086 : uchar * out = buf;
1055 1086 : ulong out_sz = buf_sz;
1056 :
1057 1086 : WRITE_U32( self->kind, &out, &out_sz );
1058 :
1059 1086 : switch( self->kind ) {
1060 0 : case fd_vote_state_versioned_enum_uninitialized:
1061 : /* No-op, nothing to encode */
1062 0 : return 0;
1063 :
1064 0 : case fd_vote_state_versioned_enum_v1_14_11: {
1065 0 : WRITE_PUBKEY( self->v1_14_11.node_pubkey, &out, &out_sz );
1066 0 : WRITE_PUBKEY( self->v1_14_11.authorized_withdrawer, &out, &out_sz );
1067 0 : WRITE_U8( self->v1_14_11.commission, &out, &out_sz );
1068 0 : CHECK( !ser_votes( self->v1_14_11.votes, 1, &out, &out_sz ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
1069 0 : CHECK( !ser_root_slot( self->v1_14_11.has_root_slot, self->v1_14_11.root_slot, &out, &out_sz ) );
1070 0 : CHECK( !ser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &out, &out_sz ) );
1071 0 : CHECK( !ser_prior_voters( &self->v1_14_11.prior_voters, &out, &out_sz ) );
1072 0 : CHECK( !ser_epoch_credits( self->v1_14_11.epoch_credits, &out, &out_sz ) );
1073 0 : WRITE_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
1074 0 : break;
1075 0 : }
1076 :
1077 0 : case fd_vote_state_versioned_enum_v3: {
1078 0 : WRITE_PUBKEY( self->v3.node_pubkey, &out, &out_sz );
1079 0 : WRITE_PUBKEY( self->v3.authorized_withdrawer, &out, &out_sz );
1080 0 : WRITE_U8( self->v3.commission, &out, &out_sz );
1081 0 : CHECK( !ser_votes( self->v3.votes, 0, &out, &out_sz ) );
1082 0 : CHECK( !ser_root_slot( self->v3.has_root_slot, self->v3.root_slot, &out, &out_sz ) );
1083 0 : CHECK( !ser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &out, &out_sz ) );
1084 0 : CHECK( !ser_prior_voters( &self->v3.prior_voters, &out, &out_sz ) );
1085 0 : CHECK( !ser_epoch_credits( self->v3.epoch_credits, &out, &out_sz ) );
1086 0 : WRITE_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
1087 0 : break;
1088 0 : }
1089 :
1090 1086 : case fd_vote_state_versioned_enum_v4: {
1091 1086 : WRITE_PUBKEY( self->v4.node_pubkey, &out, &out_sz );
1092 1086 : WRITE_PUBKEY( self->v4.authorized_withdrawer, &out, &out_sz );
1093 1086 : WRITE_PUBKEY( self->v4.inflation_rewards_collector, &out, &out_sz );
1094 1086 : WRITE_PUBKEY( self->v4.block_revenue_collector, &out, &out_sz );
1095 1086 : WRITE_U16( self->v4.inflation_rewards_commission_bps, &out, &out_sz );
1096 1086 : WRITE_U16( self->v4.block_revenue_commission_bps, &out, &out_sz );
1097 1086 : WRITE_U64( self->v4.pending_delegator_rewards, &out, &out_sz );
1098 :
1099 : /* Option<[u8; 48]> */
1100 1086 : WRITE_OPTION( self->v4.has_bls_pubkey_compressed, &out, &out_sz );
1101 1086 : if( self->v4.has_bls_pubkey_compressed ) {
1102 103 : WRITE_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &out, &out_sz );
1103 103 : }
1104 :
1105 1086 : CHECK( !ser_votes( self->v4.votes, 0, &out, &out_sz ) );
1106 1086 : CHECK( !ser_root_slot( self->v4.has_root_slot, self->v4.root_slot, &out, &out_sz ) );
1107 1086 : CHECK( !ser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &out, &out_sz ) );
1108 1086 : CHECK( !ser_epoch_credits( self->v4.epoch_credits, &out, &out_sz ) ); /* v4 has no prior_voters */
1109 1086 : WRITE_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
1110 1086 : break;
1111 1086 : }
1112 :
1113 1086 : case fd_vote_state_versioned_enum_v5: {
1114 0 : fd_validator_location_t location = self->v5.location;
1115 0 : fd_vote_state_v5_commission_t inflation_commission;
1116 0 : fd_vote_state_v5_commission_t block_commission;
1117 0 : fd_vote_state_v5_commission_get( &self->v5, fd_commission_kind_enum_inflation_rewards, &inflation_commission );
1118 0 : fd_vote_state_v5_commission_get( &self->v5, fd_commission_kind_enum_block_revenue, &block_commission );
1119 0 : CHECK( buf_sz>=FD_VOTE_STATE_V5_SZ );
1120 0 : CHECK( self->v5.authorized_voters_len<=MAX_AUTHORIZED_VOTERS );
1121 0 : CHECK( self->v5.accumulated_lamports_len<=FD_VOTE_STATE_V5_ACCUMULATED_LAMPORTS_MAX );
1122 0 : CHECK( fd_validator_location_is_valid( &location ) );
1123 0 : CHECK( fd_vote_state_v5_commission_is_valid( &inflation_commission ) );
1124 0 : CHECK( fd_vote_state_v5_commission_is_valid( &block_commission ) );
1125 0 : CHECK( self->v5.rank_epoch==ULONG_MAX && self->v5.rank==USHORT_MAX );
1126 0 : fd_memcpy( out, &self->v5, offsetof( fd_vote_state_v5_t, _pad ) );
1127 0 : fd_memset( out+offsetof( fd_vote_state_v5_t, _pad ), 0, FD_VOTE_STATE_V5_PAD_SZ );
1128 0 : break;
1129 0 : }
1130 :
1131 0 : default:
1132 0 : return 1;
1133 1086 : }
1134 :
1135 1085 : return 0;
1136 1086 : }
1137 :
1138 : ulong
1139 1086 : fd_vote_state_versioned_serialized_size( fd_vote_state_versioned_t const * self ) {
1140 1086 : switch( self->kind ) {
1141 :
1142 0 : case fd_vote_state_versioned_enum_uninitialized:
1143 0 : return 4UL;
1144 :
1145 0 : case fd_vote_state_versioned_enum_v1_14_11: {
1146 0 : ulong votes_cnt = self->v1_14_11.votes ? deq_fd_landed_vote_t_cnt( self->v1_14_11.votes ) : 0UL;
1147 0 : ulong auth_voters_cnt = self->v1_14_11.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v1_14_11.authorized_voters.treap ) : 0UL;
1148 0 : ulong epoch_credits_cnt = self->v1_14_11.epoch_credits ? deq_fd_vote_epoch_credits_t_cnt( self->v1_14_11.epoch_credits ) : 0UL;
1149 0 : return WIRE_VOTES_OFF_V1_V3
1150 0 : + 8UL + votes_cnt * WIRE_LOCKOUT_SZ
1151 0 : + 1UL + (ulong)self->v1_14_11.has_root_slot * 8UL
1152 0 : + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
1153 0 : + WIRE_PRIOR_VOTERS_SZ
1154 0 : + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
1155 0 : + sizeof(fd_vote_block_timestamp_t);
1156 0 : }
1157 :
1158 0 : case fd_vote_state_versioned_enum_v3: {
1159 0 : ulong votes_cnt = self->v3.votes ? deq_fd_landed_vote_t_cnt( self->v3.votes ) : 0UL;
1160 0 : ulong auth_voters_cnt = self->v3.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v3.authorized_voters.treap ) : 0UL;
1161 0 : ulong epoch_credits_cnt = self->v3.epoch_credits ? deq_fd_vote_epoch_credits_t_cnt( self->v3.epoch_credits ) : 0UL;
1162 0 : return WIRE_VOTES_OFF_V1_V3
1163 0 : + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
1164 0 : + 1UL + (ulong)self->v3.has_root_slot * 8UL
1165 0 : + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
1166 0 : + WIRE_PRIOR_VOTERS_SZ
1167 0 : + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
1168 0 : + sizeof(fd_vote_block_timestamp_t);
1169 0 : }
1170 :
1171 1086 : case fd_vote_state_versioned_enum_v4: {
1172 1086 : ulong votes_cnt = self->v4.votes ? deq_fd_landed_vote_t_cnt( self->v4.votes ) : 0UL;
1173 1086 : ulong auth_voters_cnt = self->v4.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v4.authorized_voters.treap ) : 0UL;
1174 1086 : ulong epoch_credits_cnt = self->v4.epoch_credits ? deq_fd_vote_epoch_credits_t_cnt( self->v4.epoch_credits ) : 0UL;
1175 1086 : return WIRE_BLS_OFF_V4
1176 1086 : + 1UL + (ulong)self->v4.has_bls_pubkey_compressed * FD_BLS_PUBKEY_COMPRESSED_SZ
1177 1086 : + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
1178 1086 : + 1UL + (ulong)self->v4.has_root_slot * 8UL
1179 1086 : + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
1180 1086 : + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
1181 1086 : + sizeof(fd_vote_block_timestamp_t);
1182 0 : }
1183 :
1184 0 : case fd_vote_state_versioned_enum_v5:
1185 0 : return FD_VOTE_STATE_V5_SZ;
1186 :
1187 0 : default:
1188 0 : return 0UL;
1189 1086 : }
1190 1086 : }
1191 :
1192 : /**********************************************************************/
1193 : /* Vote instruction -- per-variant deserializers */
1194 : /**********************************************************************/
1195 :
1196 : static int
1197 : deser_vote_authorize( fd_vote_authorize_t * out,
1198 : uchar const ** data,
1199 1127 : ulong * sz ) {
1200 1127 : READ_ENUM( out->discriminant, 3U, data, sz );
1201 1117 : if( out->discriminant==fd_vote_authorize_enum_voter_with_bls ) {
1202 199 : READ_BYTES( out->voter_with_bls.bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
1203 193 : READ_BYTES( out->voter_with_bls.bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
1204 193 : }
1205 1105 : return 0;
1206 1117 : }
1207 :
1208 : static int
1209 : deser_vote_init( fd_vote_init_t * out,
1210 : uchar const ** data,
1211 233 : ulong * sz ) {
1212 233 : READ_BYTES( out, sizeof(fd_vote_init_t), data, sz );
1213 190 : return 0;
1214 233 : }
1215 :
1216 : static int
1217 : deser_vote_init_v2( fd_vote_init_v2_t * out,
1218 : uchar const ** data,
1219 1 : ulong * sz ) {
1220 1 : READ_PUBKEY( out->node_pubkey, data, sz );
1221 1 : READ_PUBKEY( out->authorized_voter, data, sz );
1222 1 : READ_BYTES( out->authorized_voter_bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
1223 1 : READ_BYTES( out->authorized_voter_bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
1224 1 : READ_PUBKEY( out->authorized_withdrawer, data, sz );
1225 1 : READ_U16( out->inflation_rewards_commission_bps, data, sz );
1226 1 : READ_PUBKEY( out->inflation_rewards_collector, data, sz );
1227 1 : READ_U16( out->block_revenue_commission_bps, data, sz );
1228 1 : READ_PUBKEY( out->block_revenue_collector, data, sz );
1229 1 : return 0;
1230 1 : }
1231 :
1232 : static int
1233 : deser_vote( fd_legacy_vote_t * out,
1234 : uchar const ** data,
1235 571 : ulong * sz ) {
1236 571 : ulong slots_len;
1237 571 : READ_U64( slots_len, data, sz );
1238 569 : CHECK( slots_len<=FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN );
1239 :
1240 564 : out->slots = deq_ulong_join( deq_ulong_new( out->slots_mem, FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN ) );
1241 :
1242 29182 : for( ulong i=0UL; i<slots_len; i++ ) {
1243 28648 : ulong * elem = deq_ulong_push_tail_nocopy( out->slots );
1244 28648 : READ_U64( *elem, data, sz );
1245 28648 : }
1246 :
1247 534 : READ_HASH( out->hash, data, sz );
1248 525 : READ_OPTION( out->has_timestamp, data, sz );
1249 520 : if( out->has_timestamp ) {
1250 243 : READ_I64( out->timestamp, data, sz );
1251 243 : }
1252 516 : return 0;
1253 520 : }
1254 :
1255 : static int
1256 : deser_vote_state_update( fd_vote_state_update_t * out,
1257 : uchar const ** data,
1258 897 : ulong * sz ) {
1259 897 : ulong lockouts_len;
1260 897 : READ_U64( lockouts_len, data, sz );
1261 897 : CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUTS_LEN );
1262 :
1263 893 : out->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( out->lockouts_mem, FD_VOTE_INSTR_MAX_LOCKOUTS_LEN ) );
1264 :
1265 35335 : for( ulong i=0; i<lockouts_len; i++ ) {
1266 34489 : fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
1267 34489 : READ_U64( elem->slot, data, sz );
1268 34489 : READ_U32( elem->confirmation_count, data, sz );
1269 34489 : }
1270 :
1271 846 : READ_OPTION( out->has_root, data, sz );
1272 842 : if( out->has_root ) {
1273 301 : READ_U64( out->root, data, sz );
1274 301 : }
1275 838 : READ_HASH( out->hash, data, sz );
1276 826 : READ_OPTION( out->has_timestamp, data, sz );
1277 821 : if( out->has_timestamp ) {
1278 382 : READ_I64( out->timestamp, data, sz );
1279 382 : }
1280 817 : return 0;
1281 821 : }
1282 :
1283 : /* This function contains slightly custom deserialization logic to
1284 : adhere to serde_compact_vote_state_update in Agave.
1285 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L265-L299 */
1286 : static int
1287 : deser_compact_vote_state_update( fd_compact_vote_state_update_t * out,
1288 : uchar const ** data,
1289 262 : ulong * sz ) {
1290 262 : READ_U64( out->root, data, sz );
1291 :
1292 261 : ushort lockouts_len;
1293 261 : READ_COMPACT_U16( lockouts_len, data, sz );
1294 253 : CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
1295 233 : out->lockouts_len = lockouts_len;
1296 :
1297 233 : out->lockouts = (fd_lockout_offset_t *)out->lockouts_mem;
1298 :
1299 233 : ulong slot = out->root!=ULONG_MAX ? out->root : 0UL;
1300 5031 : for( ushort i=0; i<lockouts_len; i++ ) {
1301 4822 : READ_U64_VARINT( out->lockouts[i].offset, data, sz );
1302 4819 : READ_U8( out->lockouts[i].confirmation_count, data, sz );
1303 :
1304 : /* Custom logic: check that slot+lockout_offset
1305 : does not overflow */
1306 4818 : CHECK( !__builtin_uaddl_overflow( slot, out->lockouts[i].offset, &slot ) );
1307 4818 : }
1308 :
1309 209 : READ_HASH( out->hash, data, sz );
1310 208 : READ_OPTION( out->has_timestamp, data, sz );
1311 129 : if( out->has_timestamp ) {
1312 63 : READ_I64( out->timestamp, data, sz );
1313 63 : }
1314 127 : return 0;
1315 129 : }
1316 :
1317 : /* Similar to above. Tower sync uses delta-encoded lockout offsets,
1318 : converting them to absolute slot numbers on the fly. Mirrors the
1319 : checked arithmetic from Agave's custom deserializer:
1320 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L360-L396 */
1321 :
1322 : static int
1323 : deser_tower_sync( fd_tower_sync_t * out,
1324 : uchar const ** data,
1325 825 : ulong * sz ) {
1326 825 : READ_U64( out->root, data, sz );
1327 824 : out->has_root = 1;
1328 :
1329 : /* First bit of custom logic: if root is ULONG_MAX, set root to 0. */
1330 824 : if( FD_UNLIKELY( out->root==ULONG_MAX ) ) {
1331 252 : out->has_root = 0;
1332 252 : out->root = 0UL;
1333 252 : }
1334 :
1335 824 : ushort lockout_offsets_len;
1336 824 : READ_COMPACT_U16( lockout_offsets_len, data, sz );
1337 759 : CHECK( lockout_offsets_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
1338 :
1339 734 : out->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( out->lockouts_mem, FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN ) );
1340 :
1341 734 : ulong last_slot = out->root;
1342 27783 : for( ushort i=0; i<lockout_offsets_len; i++ ) {
1343 27095 : fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
1344 :
1345 27095 : ulong offset;
1346 27095 : READ_U64_VARINT( offset, data, sz );
1347 :
1348 27070 : uchar confirmation_count;
1349 27070 : READ_U8( confirmation_count, data, sz );
1350 :
1351 : /* Second bit of custom logic: check that last_slot+offset does not
1352 : overflow */
1353 27066 : CHECK( !__builtin_uaddl_overflow( last_slot, offset, &elem->slot ) );
1354 27049 : elem->confirmation_count = (uint)confirmation_count;
1355 27049 : last_slot = elem->slot;
1356 27049 : }
1357 :
1358 688 : out->lockouts_cnt = (ulong)lockout_offsets_len;
1359 :
1360 688 : READ_HASH( out->hash, data, sz );
1361 685 : READ_OPTION( out->has_timestamp, data, sz );
1362 683 : if( out->has_timestamp ) {
1363 482 : READ_I64( out->timestamp, data, sz );
1364 482 : }
1365 682 : READ_HASH( out->block_id, data, sz );
1366 681 : return 0;
1367 682 : }
1368 :
1369 : static int
1370 : deser_vote_authorize_with_seed( fd_vote_authorize_with_seed_args_t * out,
1371 : uchar const ** data,
1372 255 : ulong * sz ) {
1373 255 : CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
1374 248 : READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
1375 :
1376 243 : ulong seed_len;
1377 243 : READ_U64( seed_len, data, sz );
1378 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L42-L49 */
1379 242 : CHECK( seed_len<=FD_VOTE_INSTR_SEED_MAX );
1380 242 : out->current_authority_derived_key_seed_len = seed_len;
1381 :
1382 242 : READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
1383 241 : CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
1384 :
1385 201 : READ_PUBKEY( out->new_authority, data, sz );
1386 200 : return 0;
1387 201 : }
1388 :
1389 : static int
1390 : deser_vote_authorize_checked_with_seed( fd_vote_authorize_checked_with_seed_args_t * out,
1391 : uchar const ** data,
1392 259 : ulong * sz ) {
1393 259 : CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
1394 255 : READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
1395 :
1396 255 : ulong seed_len;
1397 255 : READ_U64( seed_len, data, sz );
1398 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L130 */
1399 254 : CHECK( seed_len<=FD_VOTE_INSTR_SEED_MAX );
1400 242 : out->current_authority_derived_key_seed_len = seed_len;
1401 :
1402 242 : READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
1403 239 : CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
1404 174 : return 0;
1405 239 : }
1406 :
1407 : /**********************************************************************/
1408 : /* Vote instruction -- top-level decoder */
1409 : /**********************************************************************/
1410 :
1411 : /* FD_VOTE_INSTR_PREFIX_SZ covers the discriminant and every scalar
1412 : field of every union member. The rest is deque / seed storage the
1413 : decoder fills up to the decoded count, which is all consumers read. */
1414 :
1415 5276 : #define FD_VOTE_INSTR_PREFIX_SZ (320UL)
1416 :
1417 : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, vote.slots_mem )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
1418 : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, update_vote_state.lockouts_mem )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
1419 : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, compact_update_vote_state.lockouts_mem )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
1420 : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, tower_sync.lockouts_mem )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
1421 : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, authorize_with_seed.current_authority_derived_key_seed )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
1422 : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, authorize_checked_with_seed.current_authority_derived_key_seed )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
1423 : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, initialize_account_v2 )+sizeof( fd_vote_init_v2_t )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
1424 : FD_STATIC_ASSERT( sizeof( fd_vote_instruction_t )>=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
1425 :
1426 : static int
1427 : fd_vote_instruction_deserialize_inner( fd_vote_instruction_t * instruction,
1428 : uchar const * data,
1429 5276 : ulong data_sz ) {
1430 : /* Zero the scalar prefix only; with handholding, poison the 10 KiB
1431 : tail so a read past the decoded count shows up. */
1432 5276 : fd_memset( instruction, 0, FD_VOTE_INSTR_PREFIX_SZ );
1433 : #if FD_TMPL_USE_HANDHOLDING
1434 : fd_memset( (uchar *)instruction+FD_VOTE_INSTR_PREFIX_SZ, 0xA5, sizeof(fd_vote_instruction_t)-FD_VOTE_INSTR_PREFIX_SZ );
1435 : #endif
1436 :
1437 5276 : uchar const ** p = &data;
1438 5276 : ulong * sz = &data_sz;
1439 :
1440 5276 : READ_U32( instruction->discriminant, p, sz );
1441 :
1442 5261 : switch( instruction->discriminant ) {
1443 :
1444 233 : case fd_vote_instruction_enum_initialize_account:
1445 233 : return deser_vote_init( &instruction->initialize_account, p, sz );
1446 :
1447 325 : case fd_vote_instruction_enum_authorize: {
1448 325 : READ_PUBKEY( instruction->authorize.pubkey, p, sz );
1449 313 : return deser_vote_authorize( &instruction->authorize.vote_authorize, p, sz );
1450 325 : }
1451 :
1452 295 : case fd_vote_instruction_enum_vote:
1453 295 : return deser_vote( &instruction->vote, p, sz );
1454 :
1455 366 : case fd_vote_instruction_enum_withdraw:
1456 366 : READ_U64( instruction->withdraw, p, sz );
1457 365 : return 0;
1458 :
1459 402 : case fd_vote_instruction_enum_update_validator_identity:
1460 402 : return 0;
1461 :
1462 496 : case fd_vote_instruction_enum_update_commission:
1463 496 : READ_U8( instruction->update_commission, p, sz );
1464 494 : return 0;
1465 :
1466 276 : case fd_vote_instruction_enum_vote_switch:
1467 276 : CHECK( !deser_vote( &instruction->vote_switch.vote, p, sz ) );
1468 248 : READ_HASH( instruction->vote_switch.hash, p, sz );
1469 243 : return 0;
1470 :
1471 300 : case fd_vote_instruction_enum_authorize_checked:
1472 300 : return deser_vote_authorize( &instruction->authorize_checked, p, sz );
1473 :
1474 448 : case fd_vote_instruction_enum_update_vote_state:
1475 448 : return deser_vote_state_update( &instruction->update_vote_state, p, sz );
1476 :
1477 449 : case fd_vote_instruction_enum_update_vote_state_switch:
1478 449 : CHECK( !deser_vote_state_update( &instruction->update_vote_state_switch.vote_state_update, p, sz ) );
1479 403 : READ_HASH( instruction->update_vote_state_switch.hash, p, sz );
1480 392 : return 0;
1481 :
1482 255 : case fd_vote_instruction_enum_authorize_with_seed:
1483 255 : return deser_vote_authorize_with_seed( &instruction->authorize_with_seed, p, sz );
1484 :
1485 259 : case fd_vote_instruction_enum_authorize_checked_with_seed:
1486 259 : return deser_vote_authorize_checked_with_seed( &instruction->authorize_checked_with_seed, p, sz );
1487 :
1488 144 : case fd_vote_instruction_enum_compact_update_vote_state:
1489 144 : return deser_compact_vote_state_update( &instruction->compact_update_vote_state, p, sz );
1490 :
1491 118 : case fd_vote_instruction_enum_compact_update_vote_state_switch:
1492 118 : CHECK( !deser_compact_vote_state_update( &instruction->compact_update_vote_state_switch.compact_vote_state_update, p, sz ) );
1493 64 : READ_HASH( instruction->compact_update_vote_state_switch.hash, p, sz );
1494 62 : return 0;
1495 :
1496 543 : case fd_vote_instruction_enum_tower_sync:
1497 543 : return deser_tower_sync( &instruction->tower_sync, p, sz );
1498 :
1499 284 : case fd_vote_instruction_enum_tower_sync_switch:
1500 284 : CHECK( !deser_tower_sync( &instruction->tower_sync_switch.tower_sync, p, sz ) );
1501 199 : READ_HASH( instruction->tower_sync_switch.hash, p, sz );
1502 195 : return 0;
1503 :
1504 1 : case fd_vote_instruction_enum_initialize_account_v2:
1505 1 : return deser_vote_init_v2( &instruction->initialize_account_v2, p, sz );
1506 :
1507 0 : case fd_vote_instruction_enum_update_commission_collector:
1508 0 : READ_ENUM( instruction->update_commission_collector.discriminant, 2U, p, sz );
1509 0 : return 0;
1510 :
1511 0 : case fd_vote_instruction_enum_update_commission_bps:
1512 0 : READ_U16( instruction->update_commission_bps.commission_bps, p, sz );
1513 0 : READ_ENUM( instruction->update_commission_bps.kind.discriminant, 2U, p, sz );
1514 0 : return 0;
1515 :
1516 0 : case fd_vote_instruction_enum_deposit_delegator_rewards:
1517 0 : READ_U64( instruction->deposit_delegator_rewards.deposit, p, sz );
1518 0 : return 0;
1519 :
1520 0 : case fd_vote_instruction_enum_update_location:
1521 0 : READ_I32( instruction->update_location.x, p, sz );
1522 0 : READ_I32( instruction->update_location.y, p, sz );
1523 0 : READ_I32( instruction->update_location.z, p, sz );
1524 0 : return 0;
1525 :
1526 82 : default:
1527 82 : return 1;
1528 5261 : }
1529 5261 : }
1530 :
1531 : fd_vote_instruction_t *
1532 : fd_vote_instruction_deserialize( fd_vote_instruction_t * instruction,
1533 : uchar const * data,
1534 5276 : ulong data_sz ) {
1535 : /* limited_deserialize(data, PACKET_DATA_SIZE)
1536 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L130 */
1537 5276 : data_sz = fd_ulong_min( data_sz, FD_TXN_MTU_V0 );
1538 5276 : CHECK_RET_NULL( !fd_vote_instruction_deserialize_inner( instruction, data, data_sz ) );
1539 4534 : return instruction;
1540 5276 : }
|