Line data Source code
1 : #include "fd_vote_codec.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 110967 : #define CHECK( cond ) do { \
13 110967 : if( FD_UNLIKELY( !(cond) ) ) return 1; \
14 110967 : } while( 0 )
15 :
16 609 : #define CHECK_RET_NULL( cond ) do { \
17 609 : if( FD_UNLIKELY( !(cond) ) ) return NULL; \
18 609 : } while( 0 )
19 :
20 2106 : #define CHECK_U64_MUL_OVERFLOW( a, b ) do { \
21 2106 : ulong _dummy; \
22 2106 : CHECK( !__builtin_umull_overflow( (a), (b), &_dummy ) ); \
23 2106 : } while( 0 )
24 :
25 : /**********************************************************************/
26 : /* Read macros */
27 : /**********************************************************************/
28 :
29 936 : #define READ_BYTES( dst, n, data, sz ) do { \
30 936 : CHECK( (n)<=(*(sz)) ); \
31 936 : fd_memcpy( (dst), *(data), (n) ); \
32 927 : *(data) += (n); \
33 927 : *(sz) -= (n); \
34 927 : } while( 0 )
35 :
36 2751 : #define SKIP_BYTES( n, data, sz ) do { \
37 2751 : CHECK( (n)<=(*(sz)) ); \
38 2751 : *(data) += (n); \
39 2751 : *(sz) -= (n); \
40 2751 : } while( 0 )
41 :
42 1686 : #define READ_U8( dst, data, sz ) do { \
43 1686 : CHECK( 1UL<=(*(sz)) ); \
44 1686 : (dst) = FD_LOAD( uchar, *(data) ); \
45 1686 : *(data) += 1UL; \
46 1686 : *(sz) -= 1UL; \
47 1686 : } while( 0 )
48 :
49 279 : #define READ_U16( dst, data, sz ) do { \
50 279 : CHECK( 2UL<=(*(sz)) ); \
51 279 : (dst) = FD_LOAD( ushort, *(data) ); \
52 279 : *(data) += 2UL; \
53 279 : *(sz) -= 2UL; \
54 279 : } while( 0 )
55 :
56 1632 : #define READ_U32( dst, data, sz ) do { \
57 1632 : CHECK( 4UL<=(*(sz)) ); \
58 1632 : (dst) = FD_LOAD( uint, *(data) ); \
59 1632 : *(data) += 4UL; \
60 1632 : *(sz) -= 4UL; \
61 1632 : } while( 0 )
62 :
63 2793 : #define READ_U64( dst, data, sz ) do { \
64 2793 : CHECK( 8UL<=(*(sz)) ); \
65 2793 : (dst) = FD_LOAD( ulong, *(data) ); \
66 2793 : *(data) += 8UL; \
67 2793 : *(sz) -= 8UL; \
68 2793 : } while( 0 )
69 :
70 0 : #define READ_I64( dst, data, sz ) do { \
71 0 : CHECK( 8UL<=(*(sz)) ); \
72 0 : (dst) = FD_LOAD( long, *(data) ); \
73 0 : *(data) += 8UL; \
74 0 : *(sz) -= 8UL; \
75 0 : } while( 0 )
76 :
77 : #define READ_HASH( dst, data, sz ) \
78 717 : READ_BYTES( (dst).uc, 32UL, data, sz )
79 :
80 717 : #define READ_PUBKEY READ_HASH
81 :
82 276 : #define READ_BOOL( dst, data, sz ) do { \
83 276 : READ_U8( dst, data, sz ); \
84 276 : CHECK( (dst)==0 || (dst)==1 ); \
85 276 : } while( 0 )
86 :
87 270 : #define READ_OPTION READ_BOOL
88 :
89 78 : #define READ_ENUM( dst, n, data, sz ) do { \
90 78 : CHECK( 4UL<=(*(sz)) ); \
91 78 : (dst) = FD_LOAD( uint, *(data) ); \
92 78 : CHECK( (dst)<(n) ); \
93 78 : *(data) += 4UL; \
94 78 : *(sz) -= 4UL; \
95 78 : } while( 0 )
96 :
97 : /* Varint decoder for u64 (LEB128). */
98 0 : #define READ_U64_VARINT( dst, data, sz ) do { \
99 0 : ulong _val = 0UL; \
100 0 : uint _shift = 0U; \
101 0 : for(;;) { \
102 0 : CHECK( 1UL<=(*(sz)) ); \
103 0 : uchar _byte = FD_LOAD( uchar, *(data) ); \
104 0 : *(data) += 1UL; \
105 0 : *(sz) -= 1UL; \
106 0 : _val |= (ulong)(_byte & 0x7F) << _shift; \
107 0 : if( FD_LIKELY( !(_byte & 0x80) ) ) { \
108 0 : CHECK( (_val>>_shift)==(ulong)_byte ); /* last byte not truncated */ \
109 0 : CHECK( _byte || !_shift ); /* no trailing zero bytes */ \
110 0 : (dst) = _val; \
111 0 : break; \
112 0 : } \
113 0 : _shift += 7U; \
114 0 : CHECK( _shift<64U ); \
115 0 : } \
116 0 : } while( 0 )
117 :
118 0 : #define READ_COMPACT_U16( dst, data, sz ) do { \
119 0 : ulong _n = fd_cu16_dec( *(data), *(sz), &(dst) ); \
120 0 : CHECK( _n ); \
121 0 : *(data) += _n; \
122 0 : *(sz) -= _n; \
123 0 : } while( 0 )
124 :
125 : /**********************************************************************/
126 : /* Write macros */
127 : /**********************************************************************/
128 :
129 28497 : #define WRITE_BYTES( src, n, out, out_sz ) do { \
130 28497 : CHECK( (n)<=(*(out_sz)) ); \
131 28497 : fd_memcpy( *(out), (src), (n) ); \
132 28497 : *(out) += (n); \
133 28497 : *(out_sz) -= (n); \
134 28497 : } while( 0 )
135 :
136 8154 : #define WRITE_U8( val, out, out_sz ) do { \
137 8154 : CHECK( 1UL<=(*(out_sz)) ); \
138 8154 : FD_STORE( uchar, *(out), (val) ); \
139 8154 : *(out) += 1UL; \
140 8154 : *(out_sz) -= 1UL; \
141 8154 : } while( 0 )
142 :
143 8154 : #define WRITE_U16( val, out, out_sz ) do { \
144 8154 : CHECK( 2UL<=(*(out_sz)) ); \
145 8154 : FD_STORE( ushort, *(out), (val) ); \
146 8154 : *(out) += 2UL; \
147 8154 : *(out_sz) -= 2UL; \
148 8154 : } while( 0 )
149 :
150 4077 : #define WRITE_U32( val, out, out_sz ) do { \
151 4077 : CHECK( 4UL<=(*(out_sz)) ); \
152 4077 : FD_STORE( uint, *(out), (val) ); \
153 4077 : *(out) += 4UL; \
154 4077 : *(out_sz) -= 4UL; \
155 4077 : } while( 0 )
156 :
157 20385 : #define WRITE_U64( val, out, out_sz ) do { \
158 20385 : CHECK( 8UL<=(*(out_sz)) ); \
159 20385 : FD_STORE( ulong, *(out), (val) ); \
160 20385 : *(out) += 8UL; \
161 20385 : *(out_sz) -= 8UL; \
162 20385 : } while( 0 )
163 :
164 : #define WRITE_I64( val, out, out_sz ) do { \
165 : CHECK( 8UL<=(*(out_sz)) ); \
166 : FD_STORE( long, *(out), (val) ); \
167 : *(out) += 8UL; \
168 : *(out_sz) -= 8UL; \
169 : } while( 0 )
170 :
171 : #define WRITE_PUBKEY( src, out, out_sz ) \
172 20385 : WRITE_BYTES( (src).uc, 32UL, out, out_sz )
173 :
174 : #define WRITE_BOOL( val, out, out_sz ) \
175 8154 : WRITE_U8( (uchar)!!(val), out, out_sz )
176 :
177 4077 : #define WRITE_OPTION WRITE_BOOL
178 :
179 : /**********************************************************************/
180 : /* Vote account state -- deserialization helpers */
181 : /* Each returns 0 on success, 1 on failure. */
182 : /**********************************************************************/
183 :
184 : /* Votes (is_v1_14_11=1 for v1_14_11 Vec<Lockout>, 0 for v3/v4
185 : Vec<LandedVote>) */
186 : static int
187 : deser_votes( fd_landed_vote_t * votes,
188 : int is_v1_14_11,
189 : uchar const ** ptr,
190 138 : ulong * rem ) {
191 138 : ulong votes_len;
192 138 : READ_U64( votes_len, ptr, rem );
193 138 : CHECK( votes_len<=MAX_LOCKOUT_HISTORY );
194 :
195 138 : for( ulong i=0UL; i<votes_len; i++ ) {
196 0 : fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy( votes );
197 0 : if( is_v1_14_11 ) {
198 0 : elem->latency = 0; /* Unused field for v1_14_11 */
199 0 : } else {
200 0 : READ_U8( elem->latency, ptr, rem );
201 0 : }
202 0 : READ_U64( elem->lockout.slot, ptr, rem );
203 0 : READ_U32( elem->lockout.confirmation_count, ptr, rem );
204 0 : }
205 138 : return 0;
206 138 : }
207 :
208 : /* Root slot (Option<u64>) */
209 : static int
210 : deser_root_slot( uchar * has_root_slot,
211 : ulong * root_slot,
212 : uchar const ** ptr,
213 138 : ulong * rem ) {
214 138 : uchar opt;
215 138 : READ_OPTION( opt, ptr, rem );
216 138 : *has_root_slot = opt;
217 138 : if( opt ) {
218 0 : READ_U64( *root_slot, ptr, rem );
219 0 : }
220 138 : return 0;
221 138 : }
222 :
223 : /* Authorized voters (BTreeMap<u64, Pubkey>) */
224 : static int
225 : deser_authorized_voters( fd_vote_authorized_voter_t * pool,
226 : fd_vote_authorized_voters_treap_t * treap,
227 : uchar const ** ptr,
228 138 : ulong * rem ) {
229 138 : ulong authorized_voters_len;
230 138 : READ_U64( authorized_voters_len, ptr, rem );
231 138 : CHECK( authorized_voters_len<=MAX_AUTHORIZED_VOTERS );
232 273 : for( ulong i=0UL; i<authorized_voters_len; i++ ) {
233 135 : fd_vote_authorized_voter_t * voter = fd_vote_authorized_voters_pool_ele_acquire( pool );
234 135 : READ_U64( voter->epoch, ptr, rem );
235 135 : READ_PUBKEY( voter->pubkey, ptr, rem );
236 135 : voter->prio = voter->pubkey.uc[0];
237 :
238 : /* Check for existing entries, overwrite if exists */
239 135 : fd_vote_authorized_voter_t * existing_voter = fd_vote_authorized_voters_treap_ele_query( treap, voter->epoch, pool );
240 135 : if( FD_UNLIKELY( existing_voter ) ) {
241 0 : fd_vote_authorized_voters_treap_ele_remove( treap, existing_voter, pool );
242 0 : fd_vote_authorized_voters_pool_ele_release( pool, existing_voter );
243 0 : }
244 :
245 135 : fd_vote_authorized_voters_treap_ele_insert( treap, voter, pool );
246 135 : }
247 138 : return 0;
248 138 : }
249 :
250 : /* Prior voters (fixed-size circular buffer of 32 entries)
251 : We can directly memcpy the entire buffer because the wire
252 : format is identical to the in-memory representation of the
253 : array. */
254 : static int
255 : deser_prior_voters( fd_vote_prior_voters_t * prior_voters,
256 : uchar const ** ptr,
257 6 : ulong * rem ) {
258 6 : READ_BYTES( prior_voters->buf, PRIOR_VOTERS_MAX*sizeof(fd_vote_prior_voter_t), ptr, rem );
259 6 : READ_U64( prior_voters->idx, ptr, rem );
260 6 : READ_BOOL( prior_voters->is_empty, ptr, rem );
261 6 : return 0;
262 6 : }
263 :
264 : /* Epoch credits (Vec<EpochCredits>) */
265 : static int
266 : deser_epoch_credits( fd_vote_epoch_credits_t * epoch_credits,
267 : uchar const ** ptr,
268 138 : ulong * rem ) {
269 138 : ulong epoch_credits_len;
270 138 : READ_U64( epoch_credits_len, ptr, rem );
271 138 : CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
272 138 : for( ulong i=0UL; i<epoch_credits_len; i++ ) {
273 0 : fd_vote_epoch_credits_t * elem = deq_fd_vote_epoch_credits_t_push_tail_nocopy( epoch_credits );
274 0 : READ_BYTES( elem, sizeof(fd_vote_epoch_credits_t), ptr, rem );
275 0 : }
276 138 : return 0;
277 138 : }
278 :
279 : /**********************************************************************/
280 : /* Vote account state -- serialization helpers */
281 : /* Each returns 0 on success, 1 on failure. */
282 : /**********************************************************************/
283 :
284 : /* Votes (is_v1_14_11=1 for v1_14_11 Vec<Lockout>, 0 for v3/v4
285 : Vec<LandedVote>) */
286 : static int
287 : ser_votes( fd_landed_vote_t const * votes,
288 : int is_v1_14_11,
289 : uchar ** out,
290 4077 : ulong * out_sz ) {
291 4077 : if( FD_UNLIKELY( votes==NULL ) ) {
292 0 : WRITE_U64( 0UL, out, out_sz );
293 0 : return 0;
294 0 : }
295 :
296 4077 : ulong votes_len = deq_fd_landed_vote_t_cnt( votes );
297 4077 : WRITE_U64( votes_len, out, out_sz );
298 :
299 4077 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
300 4077 : !deq_fd_landed_vote_t_iter_done( votes, iter );
301 4077 : iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
302 0 : fd_landed_vote_t const * elem = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
303 0 : if( !is_v1_14_11 ) {
304 0 : WRITE_U8( elem->latency, out, out_sz );
305 0 : }
306 0 : WRITE_U64( elem->lockout.slot, out, out_sz );
307 0 : WRITE_U32( elem->lockout.confirmation_count, out, out_sz );
308 0 : }
309 4077 : return 0;
310 4077 : }
311 :
312 : /* Root slot (Option<u64>) */
313 : static int
314 : ser_root_slot( uchar has_root_slot,
315 : ulong root_slot,
316 : uchar ** out,
317 4077 : ulong * out_sz ) {
318 4077 : WRITE_BOOL( has_root_slot, out, out_sz );
319 4077 : if( has_root_slot ) {
320 0 : WRITE_U64( root_slot, out, out_sz );
321 0 : }
322 4077 : return 0;
323 4077 : }
324 :
325 : /* Authorized voters (BTreeMap<u64, Pubkey>) */
326 : static int
327 : ser_authorized_voters( fd_vote_authorized_voter_t const * pool,
328 : fd_vote_authorized_voters_treap_t const * treap,
329 : uchar ** out,
330 4077 : ulong * out_sz ) {
331 4077 : if( FD_UNLIKELY( treap==NULL ) ) {
332 0 : WRITE_U64( 0UL, out, out_sz );
333 0 : return 0;
334 0 : }
335 :
336 4077 : ulong len = fd_vote_authorized_voters_treap_ele_cnt( treap );
337 4077 : WRITE_U64( len, out, out_sz );
338 :
339 4077 : for( fd_vote_authorized_voters_treap_fwd_iter_t iter = fd_vote_authorized_voters_treap_fwd_iter_init( treap, pool );
340 8154 : !fd_vote_authorized_voters_treap_fwd_iter_done( iter );
341 4077 : iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, pool ) ) {
342 4077 : fd_vote_authorized_voter_t const * voter = fd_vote_authorized_voters_treap_fwd_iter_ele_const( iter, pool );
343 4077 : WRITE_U64( voter->epoch, out, out_sz );
344 4077 : WRITE_PUBKEY( voter->pubkey, out, out_sz );
345 4077 : }
346 4077 : return 0;
347 4077 : }
348 :
349 : /* Prior voters (fixed-size circular buffer of 32 entries)
350 : We can directly memcpy the entire buffer because the wire
351 : format is identical to the in-memory representation of the
352 : array. */
353 : static int
354 : ser_prior_voters( fd_vote_prior_voters_t const * prior_voters,
355 : uchar ** out,
356 0 : ulong * out_sz ) {
357 0 : WRITE_BYTES( prior_voters->buf, PRIOR_VOTERS_MAX*sizeof(fd_vote_prior_voter_t), out, out_sz );
358 0 : WRITE_U64( prior_voters->idx, out, out_sz );
359 0 : WRITE_BOOL( prior_voters->is_empty, out, out_sz );
360 0 : return 0;
361 0 : }
362 :
363 : /* Epoch credits (Vec<EpochCredits>) */
364 : static int
365 : ser_epoch_credits( fd_vote_epoch_credits_t const * epoch_credits,
366 : uchar ** out,
367 4077 : ulong * out_sz ) {
368 4077 : if( FD_UNLIKELY( epoch_credits==NULL ) ) {
369 0 : WRITE_U64( 0UL, out, out_sz );
370 0 : return 0;
371 0 : }
372 :
373 4077 : ulong len = deq_fd_vote_epoch_credits_t_cnt( epoch_credits );
374 4077 : WRITE_U64( len, out, out_sz );
375 :
376 4077 : for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( epoch_credits );
377 4110 : !deq_fd_vote_epoch_credits_t_iter_done( epoch_credits, iter );
378 4077 : iter = deq_fd_vote_epoch_credits_t_iter_next( epoch_credits, iter ) ) {
379 33 : fd_vote_epoch_credits_t const * elem = deq_fd_vote_epoch_credits_t_iter_ele_const( epoch_credits, iter );
380 33 : WRITE_BYTES( elem, sizeof(fd_vote_epoch_credits_t), out, out_sz );
381 33 : }
382 4077 : return 0;
383 4077 : }
384 :
385 : /**********************************************************************/
386 : /* Wire layout offsets */
387 : /**********************************************************************/
388 :
389 : /* Fixed-offset byte positions within the bincode wire format.
390 : For v1_14_11 and v3 the fixed prefix is: discriminant, node_pubkey,
391 : authorized_withdrawer, commission. For v4 the prefix additionally
392 : includes inflation_rewards_collector, block_revenue_collector,
393 : inflation_rewards_commission_bps, block_revenue_commission_bps,
394 : and pending_delegator_rewards before the first variable field. */
395 :
396 321 : #define WIRE_OFF_NODE_PUBKEY (4UL)
397 0 : #define WIRE_OFF_V1V3_COMMISSION (68UL) /* 4 + 32 + 32 */
398 321 : #define WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR (68UL) /* 4 + 32 + 32 */
399 321 : #define WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR (100UL) /* 4 + 32 + 32 + 32 */
400 : #define WIRE_OFF_V4_COMMISSION_BPS (132UL) /* 4 + 32 + 32 + 32 + 32 */
401 :
402 : /* Byte size of a Lockout on the wire: u64 slot + u32 confirmation_count */
403 0 : #define WIRE_LOCKOUT_SZ (12UL)
404 : /* Byte size of a LandedVote on the wire: u8 latency + u64 slot + u32 confirmation_count */
405 72 : #define WIRE_LANDED_VOTE_SZ (13UL)
406 : /* Byte size of an authorized voter entry: u64 epoch + 32B pubkey */
407 72 : #define WIRE_AUTH_VOTER_SZ (40UL)
408 : /* Byte size of prior_voters on the wire: 32 * 48B + 8B idx + 1B is_empty */
409 0 : #define WIRE_PRIOR_VOTERS_SZ (PRIOR_VOTERS_MAX * 48UL + 8UL + 1UL)
410 :
411 : /* Offset of the votes vector (v1/v3) or BLS option (v4) — first byte
412 : after the fixed-offset prefix. */
413 0 : #define WIRE_VOTES_OFF_V1_V3 (69UL) /* 4 + 32 + 32 + 1 */
414 402 : #define WIRE_BLS_OFF_V4 (144UL) /* 4 + 32 + 32 + 32 + 32 + 2 + 2 + 8 */
415 :
416 : /**********************************************************************/
417 : /* Vote account state -- direct field accessors */
418 : /* These read fields directly from raw bincode-encoded vote account */
419 : /* data without full deserialization. */
420 : /**********************************************************************/
421 :
422 : int
423 : fd_vote_account_node_pubkey( uchar const * data,
424 : ulong data_sz,
425 321 : fd_pubkey_t * out ) {
426 321 : CHECK( data_sz>=WIRE_OFF_NODE_PUBKEY+32UL );
427 321 : fd_memcpy( out, data+WIRE_OFF_NODE_PUBKEY, 32UL );
428 321 : return 0;
429 321 : }
430 :
431 : int
432 : fd_vote_account_commission_bps( uchar const * data,
433 : ulong data_sz,
434 : int commission_rate_in_basis_points,
435 321 : ushort * out ) {
436 321 : uchar const * ptr = data;
437 321 : ulong remaining = data_sz;
438 :
439 321 : uint discriminant;
440 321 : READ_U32( discriminant, &ptr, &remaining );
441 :
442 321 : switch( discriminant ) {
443 0 : case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
444 0 : case fd_vote_state_versioned_enum_v3:
445 0 : CHECK( data_sz>WIRE_OFF_V1V3_COMMISSION );
446 0 : *out = data[ WIRE_OFF_V1V3_COMMISSION ] * 100U;
447 0 : return 0;
448 321 : case fd_vote_state_versioned_enum_v4:
449 321 : CHECK( data_sz>=WIRE_OFF_V4_COMMISSION_BPS+2UL );
450 321 : *out = FD_LOAD( ushort, data+WIRE_OFF_V4_COMMISSION_BPS );
451 :
452 : /* Round down to the nearest whole percentage if SIMD-0291 is not
453 : yet active. */
454 321 : if( !commission_rate_in_basis_points ) {
455 249 : *out = (*out / 100U) * 100U;
456 249 : }
457 321 : return 0;
458 0 : default:
459 0 : return 1;
460 321 : }
461 321 : }
462 :
463 : int
464 : fd_vote_account_collectors( uchar const * data,
465 : ulong data_sz,
466 : fd_pubkey_t const * vote_pubkey,
467 : fd_pubkey_t const * node_pubkey,
468 : fd_pubkey_t * inflation_rewards_collector_out,
469 321 : fd_pubkey_t * block_revenue_collector_out ) {
470 321 : uchar const * ptr = data;
471 321 : ulong remaining = data_sz;
472 :
473 321 : uint discriminant;
474 321 : READ_U32( discriminant, &ptr, &remaining );
475 :
476 321 : switch( discriminant ) {
477 0 : case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
478 0 : case fd_vote_state_versioned_enum_v3:
479 : /* Pre-v4 vote states have no collector fields: default to the
480 : vote account (inflation) and the node identity (block
481 : revenue). */
482 0 : *inflation_rewards_collector_out = *vote_pubkey;
483 0 : *block_revenue_collector_out = *node_pubkey;
484 0 : return 0;
485 321 : case fd_vote_state_versioned_enum_v4:
486 321 : CHECK( data_sz>=WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR+32UL );
487 321 : fd_memcpy( inflation_rewards_collector_out, data+WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR, 32UL );
488 321 : fd_memcpy( block_revenue_collector_out, data+WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR, 32UL );
489 321 : return 0;
490 0 : default:
491 0 : return 1;
492 321 : }
493 321 : }
494 :
495 : /* Seeks past variable-length fields to the start of epoch_credits.
496 : On success sets *out_ptr to the first entry and *out_cnt to the
497 : entry count. Returns 0 on success, 1 on error. */
498 :
499 : static int
500 : seek_epoch_credits( uchar const * data,
501 : ulong data_sz,
502 : fd_vote_epoch_credits_t const ** out_ptr,
503 702 : ulong * out_cnt ) {
504 702 : uchar const * ptr = data;
505 702 : ulong remaining = data_sz;
506 :
507 702 : uint discriminant;
508 702 : READ_U32( discriminant, &ptr, &remaining );
509 :
510 702 : switch( discriminant ) {
511 0 : case fd_vote_state_versioned_enum_v1_14_11: {
512 0 : SKIP_BYTES( WIRE_VOTES_OFF_V1_V3-4UL, &ptr, &remaining );
513 :
514 : /* Skip votes Vec<Lockout> */
515 0 : ulong votes_len;
516 0 : READ_U64( votes_len, &ptr, &remaining );
517 0 : CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LOCKOUT_SZ );
518 0 : SKIP_BYTES( votes_len*WIRE_LOCKOUT_SZ, &ptr, &remaining );
519 :
520 : /* Skip root_slot Option<u64> */
521 0 : uchar has_root_slot;
522 0 : READ_U8( has_root_slot, &ptr, &remaining );
523 0 : if( has_root_slot ) {
524 0 : SKIP_BYTES( 8UL, &ptr, &remaining );
525 0 : }
526 :
527 : /* Skip authorized_voters BTreeMap<u64, Pubkey> */
528 0 : ulong authorized_voters_len;
529 0 : READ_U64( authorized_voters_len, &ptr, &remaining );
530 0 : CHECK_U64_MUL_OVERFLOW( authorized_voters_len, WIRE_AUTH_VOTER_SZ );
531 0 : SKIP_BYTES( authorized_voters_len*WIRE_AUTH_VOTER_SZ, &ptr, &remaining );
532 :
533 : /* Skip prior_voters (fixed size) */
534 0 : SKIP_BYTES( WIRE_PRIOR_VOTERS_SZ, &ptr, &remaining );
535 0 : break;
536 0 : }
537 :
538 0 : case fd_vote_state_versioned_enum_v3: {
539 0 : SKIP_BYTES( WIRE_VOTES_OFF_V1_V3-4UL, &ptr, &remaining );
540 :
541 : /* Skip votes Vec<LandedVote> */
542 0 : ulong votes_len;
543 0 : READ_U64( votes_len, &ptr, &remaining );
544 0 : CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LANDED_VOTE_SZ );
545 0 : SKIP_BYTES( votes_len*WIRE_LANDED_VOTE_SZ, &ptr, &remaining );
546 :
547 : /* Skip root_slot Option<u64> */
548 0 : uchar has_root_slot;
549 0 : READ_U8( has_root_slot, &ptr, &remaining );
550 0 : if( has_root_slot ) {
551 0 : SKIP_BYTES( 8UL, &ptr, &remaining );
552 0 : }
553 :
554 : /* Skip authorized_voters BTreeMap<u64, Pubkey> */
555 0 : ulong authorized_voters_len;
556 0 : READ_U64( authorized_voters_len, &ptr, &remaining );
557 0 : CHECK_U64_MUL_OVERFLOW( authorized_voters_len, WIRE_AUTH_VOTER_SZ );
558 0 : SKIP_BYTES( authorized_voters_len*WIRE_AUTH_VOTER_SZ, &ptr, &remaining );
559 :
560 : /* Skip prior_voters (fixed size) */
561 0 : SKIP_BYTES( WIRE_PRIOR_VOTERS_SZ, &ptr, &remaining );
562 0 : break;
563 0 : }
564 :
565 702 : case fd_vote_state_versioned_enum_v4: {
566 702 : SKIP_BYTES( WIRE_BLS_OFF_V4-4UL, &ptr, &remaining );
567 :
568 : /* Skip Option<bls_pubkey_compressed> */
569 702 : uchar has_bls_pubkey;
570 702 : READ_U8( has_bls_pubkey, &ptr, &remaining );
571 702 : if( has_bls_pubkey ) {
572 645 : SKIP_BYTES( FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &remaining );
573 645 : }
574 :
575 : /* Skip votes Vec<LandedVote> */
576 702 : ulong votes_len;
577 702 : READ_U64( votes_len, &ptr, &remaining );
578 702 : CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LANDED_VOTE_SZ );
579 702 : SKIP_BYTES( votes_len*WIRE_LANDED_VOTE_SZ, &ptr, &remaining );
580 :
581 : /* Skip root_slot Option<u64> */
582 702 : uchar has_root_slot;
583 702 : READ_U8( has_root_slot, &ptr, &remaining );
584 702 : if( has_root_slot ) {
585 0 : SKIP_BYTES( 8UL, &ptr, &remaining );
586 0 : }
587 :
588 : /* Skip authorized_voters BTreeMap<u64, Pubkey> */
589 702 : ulong authorized_voters_len;
590 702 : READ_U64( authorized_voters_len, &ptr, &remaining );
591 702 : CHECK_U64_MUL_OVERFLOW( authorized_voters_len, WIRE_AUTH_VOTER_SZ );
592 702 : SKIP_BYTES( authorized_voters_len*WIRE_AUTH_VOTER_SZ, &ptr, &remaining );
593 702 : break;
594 702 : }
595 :
596 702 : default:
597 0 : return 1;
598 702 : }
599 :
600 : /* Now at epoch_credits deque */
601 702 : ulong epoch_credits_len;
602 702 : READ_U64( epoch_credits_len, &ptr, &remaining );
603 702 : CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
604 702 : CHECK_U64_MUL_OVERFLOW( epoch_credits_len, sizeof(fd_vote_epoch_credits_t) );
605 702 : CHECK( epoch_credits_len*sizeof(fd_vote_epoch_credits_t)<=remaining );
606 :
607 702 : *out_ptr = (fd_vote_epoch_credits_t const *)ptr;
608 702 : *out_cnt = epoch_credits_len;
609 702 : return 0;
610 702 : }
611 :
612 : int
613 : fd_vote_account_last_timestamp( uchar const * data,
614 : ulong data_sz,
615 381 : fd_vote_block_timestamp_t * out ) {
616 381 : fd_vote_epoch_credits_t const * epoch_credits_ptr;
617 381 : ulong epoch_credits_len;
618 381 : CHECK( !seek_epoch_credits( data, data_sz, &epoch_credits_ptr, &epoch_credits_len ) );
619 :
620 381 : uchar const * timestamp_ptr = (uchar const *)( epoch_credits_ptr + epoch_credits_len );
621 381 : CHECK( timestamp_ptr+sizeof(fd_vote_block_timestamp_t)<=data+data_sz );
622 :
623 381 : fd_memcpy( out, timestamp_ptr, sizeof(fd_vote_block_timestamp_t) );
624 381 : return 0;
625 381 : }
626 :
627 : int
628 : fd_vote_account_is_v4_with_bls_pubkey( uchar const * data,
629 330 : ulong data_sz ) {
630 330 : if( FD_UNLIKELY( data_sz<WIRE_BLS_OFF_V4+1UL ) ) return 0;
631 330 : uint discriminant = FD_LOAD( uint, data );
632 330 : if( discriminant!=fd_vote_state_versioned_enum_v4 ) return 0;
633 330 : return !!data[ WIRE_BLS_OFF_V4 ];
634 330 : }
635 :
636 : fd_vote_epoch_credits_t const *
637 : fd_vote_account_epoch_credits( uchar const * data,
638 : ulong data_sz,
639 321 : ulong * cnt ) {
640 321 : fd_vote_epoch_credits_t const * ptr;
641 321 : CHECK_RET_NULL( !seek_epoch_credits( data, data_sz, &ptr, cnt ) );
642 321 : return ptr;
643 321 : }
644 :
645 : /**********************************************************************/
646 : /* Vote account state -- public API */
647 : /**********************************************************************/
648 :
649 : fd_vote_state_versioned_t *
650 : fd_vote_state_versioned_new( fd_vote_state_versioned_t * self,
651 4170 : uint kind ) {
652 4170 : if( FD_UNLIKELY( !self ) ) {
653 0 : FD_LOG_WARNING(( "NULL mem" ));
654 0 : return NULL;
655 0 : }
656 :
657 : /* Init and join data structures */
658 4170 : fd_landed_vote_t * votes = deq_fd_landed_vote_t_join(
659 4170 : deq_fd_landed_vote_t_new( self->landed_votes_mem, MAX_LOCKOUT_HISTORY_CAPACITY )
660 4170 : );
661 4170 : fd_vote_epoch_credits_t * epoch_credits = deq_fd_vote_epoch_credits_t_join(
662 4170 : deq_fd_vote_epoch_credits_t_new( self->epoch_credits_mem )
663 4170 : );
664 4170 : fd_vote_authorized_voter_t * authorized_voters_pool = fd_vote_authorized_voters_pool_join(
665 4170 : fd_vote_authorized_voters_pool_new( self->authorized_voters_pool_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
666 4170 : );
667 4170 : fd_vote_authorized_voters_treap_t * authorized_voters_treap = fd_vote_authorized_voters_treap_join(
668 4170 : fd_vote_authorized_voters_treap_new( self->authorized_voters_treap_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
669 4170 : );
670 :
671 4170 : self->kind = kind;
672 4170 : switch( kind ) {
673 27 : case fd_vote_state_versioned_enum_uninitialized:
674 27 : break;
675 3 : case fd_vote_state_versioned_enum_v1_14_11:
676 3 : memset( &self->v1_14_11, 0, sizeof(fd_vote_state_1_14_11_t) );
677 3 : self->v1_14_11.votes = votes;
678 3 : self->v1_14_11.epoch_credits = epoch_credits;
679 3 : self->v1_14_11.authorized_voters.pool = authorized_voters_pool;
680 3 : self->v1_14_11.authorized_voters.treap = authorized_voters_treap;
681 3 : break;
682 9 : case fd_vote_state_versioned_enum_v3:
683 9 : memset( &self->v3, 0, sizeof(fd_vote_state_v3_t) );
684 9 : self->v3.votes = votes;
685 9 : self->v3.epoch_credits = epoch_credits;
686 9 : self->v3.authorized_voters.pool = authorized_voters_pool;
687 9 : self->v3.authorized_voters.treap = authorized_voters_treap;
688 9 : break;
689 4131 : case fd_vote_state_versioned_enum_v4:
690 4131 : memset( &self->v4, 0, sizeof(fd_vote_state_v4_t) );
691 4131 : self->v4.votes = votes;
692 4131 : self->v4.epoch_credits = epoch_credits;
693 4131 : self->v4.authorized_voters.pool = authorized_voters_pool;
694 4131 : self->v4.authorized_voters.treap = authorized_voters_treap;
695 4131 : break;
696 0 : default:
697 0 : return NULL;
698 4170 : }
699 :
700 4170 : return self;
701 4170 : }
702 :
703 : static int
704 : fd_vote_state_versioned_deserialize_inner( fd_vote_state_versioned_t * self,
705 : uchar const * payload,
706 174 : ulong payload_sz ) {
707 174 : CHECK( self!=NULL );
708 174 : CHECK( payload!=NULL );
709 :
710 174 : uchar const * ptr = payload;
711 174 : ulong rem = payload_sz;
712 :
713 174 : uint kind;
714 174 : READ_U32( kind, &ptr, &rem );
715 :
716 174 : CHECK( fd_vote_state_versioned_new( self, kind )!=NULL );
717 :
718 174 : switch( self->kind ) {
719 27 : case fd_vote_state_versioned_enum_uninitialized:
720 : /* No-op, nothing to decode */
721 27 : return 0;
722 :
723 3 : case fd_vote_state_versioned_enum_v1_14_11: {
724 3 : READ_PUBKEY( self->v1_14_11.node_pubkey, &ptr, &rem );
725 0 : READ_PUBKEY( self->v1_14_11.authorized_withdrawer, &ptr, &rem );
726 0 : READ_U8( self->v1_14_11.commission, &ptr, &rem );
727 0 : CHECK( !deser_votes( self->v1_14_11.votes, 1, &ptr, &rem ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
728 0 : CHECK( !deser_root_slot( &self->v1_14_11.has_root_slot, &self->v1_14_11.root_slot, &ptr, &rem ) );
729 0 : CHECK( !deser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &ptr, &rem ) );
730 0 : CHECK( !deser_prior_voters( &self->v1_14_11.prior_voters, &ptr, &rem ) );
731 0 : CHECK( !deser_epoch_credits( self->v1_14_11.epoch_credits, &ptr, &rem ) );
732 0 : READ_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
733 0 : break;
734 0 : }
735 :
736 9 : case fd_vote_state_versioned_enum_v3: {
737 9 : READ_PUBKEY( self->v3.node_pubkey, &ptr, &rem );
738 6 : READ_PUBKEY( self->v3.authorized_withdrawer, &ptr, &rem );
739 6 : READ_U8( self->v3.commission, &ptr, &rem );
740 6 : CHECK( !deser_votes( self->v3.votes, 0, &ptr, &rem ) );
741 6 : CHECK( !deser_root_slot( &self->v3.has_root_slot, &self->v3.root_slot, &ptr, &rem ) );
742 6 : CHECK( !deser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &ptr, &rem ) );
743 6 : CHECK( !deser_prior_voters( &self->v3.prior_voters, &ptr, &rem ) );
744 6 : CHECK( !deser_epoch_credits( self->v3.epoch_credits, &ptr, &rem ) );
745 6 : READ_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
746 6 : break;
747 6 : }
748 :
749 135 : case fd_vote_state_versioned_enum_v4: {
750 135 : READ_PUBKEY( self->v4.node_pubkey, &ptr, &rem );
751 132 : READ_PUBKEY( self->v4.authorized_withdrawer, &ptr, &rem );
752 132 : READ_PUBKEY( self->v4.inflation_rewards_collector, &ptr, &rem );
753 132 : READ_PUBKEY( self->v4.block_revenue_collector, &ptr, &rem );
754 132 : READ_U16( self->v4.inflation_rewards_commission_bps, &ptr, &rem );
755 132 : READ_U16( self->v4.block_revenue_commission_bps, &ptr, &rem );
756 132 : READ_U64( self->v4.pending_delegator_rewards, &ptr, &rem );
757 :
758 : /* Option<[u8; 48]> */
759 132 : READ_OPTION( self->v4.has_bls_pubkey_compressed, &ptr, &rem );
760 132 : if( self->v4.has_bls_pubkey_compressed ) {
761 39 : READ_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &rem );
762 39 : }
763 :
764 132 : CHECK( !deser_votes( self->v4.votes, 0, &ptr, &rem ) );
765 132 : CHECK( !deser_root_slot( &self->v4.has_root_slot, &self->v4.root_slot, &ptr, &rem ) );
766 132 : CHECK( !deser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &ptr, &rem ) );
767 132 : CHECK( !deser_epoch_credits( self->v4.epoch_credits, &ptr, &rem ) ); /* v4 has no prior_voters */
768 132 : READ_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
769 132 : break;
770 132 : }
771 :
772 132 : default:
773 0 : return 1;
774 174 : }
775 :
776 138 : return 0;
777 174 : }
778 :
779 : fd_vote_state_versioned_t *
780 : fd_vote_state_versioned_deserialize( fd_vote_state_versioned_t * self,
781 : uchar const * payload,
782 174 : ulong payload_sz ) {
783 174 : CHECK_RET_NULL( !fd_vote_state_versioned_deserialize_inner( self, payload, payload_sz ) );
784 165 : return self;
785 174 : }
786 :
787 : int
788 : fd_vote_state_versioned_serialize( fd_vote_state_versioned_t const * self,
789 : uchar * buf,
790 4077 : ulong buf_sz ) {
791 4077 : CHECK( self!=NULL );
792 4077 : CHECK( buf!=NULL );
793 :
794 4077 : uchar * out = buf;
795 4077 : ulong out_sz = buf_sz;
796 :
797 4077 : WRITE_U32( self->kind, &out, &out_sz );
798 :
799 4077 : switch( self->kind ) {
800 0 : case fd_vote_state_versioned_enum_uninitialized:
801 : /* No-op, nothing to encode */
802 0 : return 0;
803 :
804 0 : case fd_vote_state_versioned_enum_v1_14_11: {
805 0 : WRITE_PUBKEY( self->v1_14_11.node_pubkey, &out, &out_sz );
806 0 : WRITE_PUBKEY( self->v1_14_11.authorized_withdrawer, &out, &out_sz );
807 0 : WRITE_U8( self->v1_14_11.commission, &out, &out_sz );
808 0 : CHECK( !ser_votes( self->v1_14_11.votes, 1, &out, &out_sz ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
809 0 : CHECK( !ser_root_slot( self->v1_14_11.has_root_slot, self->v1_14_11.root_slot, &out, &out_sz ) );
810 0 : CHECK( !ser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &out, &out_sz ) );
811 0 : CHECK( !ser_prior_voters( &self->v1_14_11.prior_voters, &out, &out_sz ) );
812 0 : CHECK( !ser_epoch_credits( self->v1_14_11.epoch_credits, &out, &out_sz ) );
813 0 : WRITE_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
814 0 : break;
815 0 : }
816 :
817 0 : case fd_vote_state_versioned_enum_v3: {
818 0 : WRITE_PUBKEY( self->v3.node_pubkey, &out, &out_sz );
819 0 : WRITE_PUBKEY( self->v3.authorized_withdrawer, &out, &out_sz );
820 0 : WRITE_U8( self->v3.commission, &out, &out_sz );
821 0 : CHECK( !ser_votes( self->v3.votes, 0, &out, &out_sz ) );
822 0 : CHECK( !ser_root_slot( self->v3.has_root_slot, self->v3.root_slot, &out, &out_sz ) );
823 0 : CHECK( !ser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &out, &out_sz ) );
824 0 : CHECK( !ser_prior_voters( &self->v3.prior_voters, &out, &out_sz ) );
825 0 : CHECK( !ser_epoch_credits( self->v3.epoch_credits, &out, &out_sz ) );
826 0 : WRITE_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
827 0 : break;
828 0 : }
829 :
830 4077 : case fd_vote_state_versioned_enum_v4: {
831 4077 : WRITE_PUBKEY( self->v4.node_pubkey, &out, &out_sz );
832 4077 : WRITE_PUBKEY( self->v4.authorized_withdrawer, &out, &out_sz );
833 4077 : WRITE_PUBKEY( self->v4.inflation_rewards_collector, &out, &out_sz );
834 4077 : WRITE_PUBKEY( self->v4.block_revenue_collector, &out, &out_sz );
835 4077 : WRITE_U16( self->v4.inflation_rewards_commission_bps, &out, &out_sz );
836 4077 : WRITE_U16( self->v4.block_revenue_commission_bps, &out, &out_sz );
837 4077 : WRITE_U64( self->v4.pending_delegator_rewards, &out, &out_sz );
838 :
839 : /* Option<[u8; 48]> */
840 4077 : WRITE_OPTION( self->v4.has_bls_pubkey_compressed, &out, &out_sz );
841 4077 : if( self->v4.has_bls_pubkey_compressed ) {
842 4002 : WRITE_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &out, &out_sz );
843 4002 : }
844 :
845 4077 : CHECK( !ser_votes( self->v4.votes, 0, &out, &out_sz ) );
846 4077 : CHECK( !ser_root_slot( self->v4.has_root_slot, self->v4.root_slot, &out, &out_sz ) );
847 4077 : CHECK( !ser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &out, &out_sz ) );
848 4077 : CHECK( !ser_epoch_credits( self->v4.epoch_credits, &out, &out_sz ) ); /* v4 has no prior_voters */
849 4077 : WRITE_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
850 4077 : break;
851 4077 : }
852 :
853 4077 : default:
854 0 : return 1;
855 4077 : }
856 :
857 4077 : return 0;
858 4077 : }
859 :
860 : ulong
861 72 : fd_vote_state_versioned_serialized_size( fd_vote_state_versioned_t const * self ) {
862 72 : switch( self->kind ) {
863 :
864 0 : case fd_vote_state_versioned_enum_uninitialized:
865 0 : return 4UL;
866 :
867 0 : case fd_vote_state_versioned_enum_v1_14_11: {
868 0 : ulong votes_cnt = self->v1_14_11.votes ? deq_fd_landed_vote_t_cnt( self->v1_14_11.votes ) : 0UL;
869 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;
870 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;
871 0 : return WIRE_VOTES_OFF_V1_V3
872 0 : + 8UL + votes_cnt * WIRE_LOCKOUT_SZ
873 0 : + 1UL + (ulong)self->v1_14_11.has_root_slot * 8UL
874 0 : + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
875 0 : + WIRE_PRIOR_VOTERS_SZ
876 0 : + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
877 0 : + sizeof(fd_vote_block_timestamp_t);
878 0 : }
879 :
880 0 : case fd_vote_state_versioned_enum_v3: {
881 0 : ulong votes_cnt = self->v3.votes ? deq_fd_landed_vote_t_cnt( self->v3.votes ) : 0UL;
882 0 : ulong auth_voters_cnt = self->v3.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v3.authorized_voters.treap ) : 0UL;
883 0 : ulong epoch_credits_cnt = self->v3.epoch_credits ? deq_fd_vote_epoch_credits_t_cnt( self->v3.epoch_credits ) : 0UL;
884 0 : return WIRE_VOTES_OFF_V1_V3
885 0 : + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
886 0 : + 1UL + (ulong)self->v3.has_root_slot * 8UL
887 0 : + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
888 0 : + WIRE_PRIOR_VOTERS_SZ
889 0 : + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
890 0 : + sizeof(fd_vote_block_timestamp_t);
891 0 : }
892 :
893 72 : case fd_vote_state_versioned_enum_v4: {
894 72 : ulong votes_cnt = self->v4.votes ? deq_fd_landed_vote_t_cnt( self->v4.votes ) : 0UL;
895 72 : ulong auth_voters_cnt = self->v4.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v4.authorized_voters.treap ) : 0UL;
896 72 : ulong epoch_credits_cnt = self->v4.epoch_credits ? deq_fd_vote_epoch_credits_t_cnt( self->v4.epoch_credits ) : 0UL;
897 72 : return WIRE_BLS_OFF_V4
898 72 : + 1UL + (ulong)self->v4.has_bls_pubkey_compressed * FD_BLS_PUBKEY_COMPRESSED_SZ
899 72 : + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
900 72 : + 1UL + (ulong)self->v4.has_root_slot * 8UL
901 72 : + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
902 72 : + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
903 72 : + sizeof(fd_vote_block_timestamp_t);
904 0 : }
905 :
906 0 : default:
907 0 : return 0UL;
908 72 : }
909 72 : }
910 :
911 : /**********************************************************************/
912 : /* Vote instruction -- per-variant deserializers */
913 : /**********************************************************************/
914 :
915 : static int
916 : deser_vote_authorize( fd_vote_authorize_t * out,
917 : uchar const ** data,
918 3 : ulong * sz ) {
919 3 : READ_ENUM( out->discriminant, 3U, data, sz );
920 3 : if( out->discriminant==fd_vote_authorize_enum_voter_with_bls ) {
921 0 : READ_BYTES( out->voter_with_bls.bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
922 0 : READ_BYTES( out->voter_with_bls.bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
923 0 : }
924 3 : return 0;
925 3 : }
926 :
927 : static int
928 : deser_vote_init( fd_vote_init_t * out,
929 : uchar const ** data,
930 24 : ulong * sz ) {
931 24 : READ_BYTES( out, sizeof(fd_vote_init_t), data, sz );
932 24 : return 0;
933 24 : }
934 :
935 : static int
936 : deser_vote_init_v2( fd_vote_init_v2_t * out,
937 : uchar const ** data,
938 6 : ulong * sz ) {
939 6 : READ_PUBKEY( out->node_pubkey, data, sz );
940 6 : READ_PUBKEY( out->authorized_voter, data, sz );
941 6 : READ_BYTES( out->authorized_voter_bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
942 6 : READ_BYTES( out->authorized_voter_bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
943 6 : READ_PUBKEY( out->authorized_withdrawer, data, sz );
944 6 : READ_U16( out->inflation_rewards_commission_bps, data, sz );
945 6 : READ_PUBKEY( out->inflation_rewards_collector, data, sz );
946 6 : READ_U16( out->block_revenue_commission_bps, data, sz );
947 6 : READ_PUBKEY( out->block_revenue_collector, data, sz );
948 6 : return 0;
949 6 : }
950 :
951 : static int
952 : deser_vote( fd_vote_t * out,
953 : uchar const ** data,
954 0 : ulong * sz ) {
955 0 : ulong slots_len;
956 0 : READ_U64( slots_len, data, sz );
957 0 : CHECK( slots_len<=FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN );
958 :
959 0 : out->slots = deq_ulong_join( deq_ulong_new( out->slots_mem, FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN ) );
960 :
961 0 : for( ulong i=0UL; i<slots_len; i++ ) {
962 0 : ulong * elem = deq_ulong_push_tail_nocopy( out->slots );
963 0 : READ_U64( *elem, data, sz );
964 0 : }
965 :
966 0 : READ_HASH( out->hash, data, sz );
967 0 : READ_OPTION( out->has_timestamp, data, sz );
968 0 : if( out->has_timestamp ) {
969 0 : READ_I64( out->timestamp, data, sz );
970 0 : }
971 0 : return 0;
972 0 : }
973 :
974 : static int
975 : deser_vote_state_update( fd_vote_state_update_t * out,
976 : uchar const ** data,
977 0 : ulong * sz ) {
978 0 : ulong lockouts_len;
979 0 : READ_U64( lockouts_len, data, sz );
980 0 : CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUTS_LEN );
981 :
982 0 : out->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( out->lockouts_mem, FD_VOTE_INSTR_MAX_LOCKOUTS_LEN ) );
983 :
984 0 : for( ulong i=0; i<lockouts_len; i++ ) {
985 0 : fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
986 0 : READ_U64( elem->slot, data, sz );
987 0 : READ_U32( elem->confirmation_count, data, sz );
988 0 : }
989 :
990 0 : READ_OPTION( out->has_root, data, sz );
991 0 : if( out->has_root ) {
992 0 : READ_U64( out->root, data, sz );
993 0 : }
994 0 : READ_HASH( out->hash, data, sz );
995 0 : READ_OPTION( out->has_timestamp, data, sz );
996 0 : if( out->has_timestamp ) {
997 0 : READ_I64( out->timestamp, data, sz );
998 0 : }
999 0 : return 0;
1000 0 : }
1001 :
1002 : /* This function contains slightly custom deserialization logic to
1003 : adhere to serde_compact_vote_state_update in Agave.
1004 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L265-L299 */
1005 : static int
1006 : deser_compact_vote_state_update( fd_compact_vote_state_update_t * out,
1007 : uchar const ** data,
1008 0 : ulong * sz ) {
1009 0 : READ_U64( out->root, data, sz );
1010 :
1011 0 : ushort lockouts_len;
1012 0 : READ_COMPACT_U16( lockouts_len, data, sz );
1013 0 : CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
1014 0 : out->lockouts_len = lockouts_len;
1015 :
1016 0 : out->lockouts = (fd_lockout_offset_t *)out->lockouts_mem;
1017 :
1018 0 : ulong slot = out->root!=ULONG_MAX ? out->root : 0UL;
1019 0 : for( ushort i=0; i<lockouts_len; i++ ) {
1020 0 : READ_U64_VARINT( out->lockouts[i].offset, data, sz );
1021 0 : READ_U8( out->lockouts[i].confirmation_count, data, sz );
1022 :
1023 : /* Custom logic: check that slot+lockout_offset
1024 : does not overflow */
1025 0 : CHECK( !__builtin_uaddl_overflow( slot, out->lockouts[i].offset, &slot ) );
1026 0 : }
1027 :
1028 0 : READ_HASH( out->hash, data, sz );
1029 0 : READ_OPTION( out->has_timestamp, data, sz );
1030 0 : if( out->has_timestamp ) {
1031 0 : READ_I64( out->timestamp, data, sz );
1032 0 : }
1033 0 : return 0;
1034 0 : }
1035 :
1036 : /* Similar to above. Tower sync uses delta-encoded lockout offsets,
1037 : converting them to absolute slot numbers on the fly. Mirrors the
1038 : checked arithmetic from Agave's custom deserializer:
1039 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L360-L396 */
1040 :
1041 : static int
1042 : deser_tower_sync( fd_tower_sync_t * out,
1043 : uchar const ** data,
1044 0 : ulong * sz ) {
1045 0 : READ_U64( out->root, data, sz );
1046 0 : out->has_root = 1;
1047 :
1048 : /* First bit of custom logic: if root is ULONG_MAX, set root to 0. */
1049 0 : if( FD_UNLIKELY( out->root==ULONG_MAX ) ) {
1050 0 : out->has_root = 0;
1051 0 : out->root = 0UL;
1052 0 : }
1053 :
1054 0 : ushort lockout_offsets_len;
1055 0 : READ_COMPACT_U16( lockout_offsets_len, data, sz );
1056 0 : CHECK( lockout_offsets_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
1057 :
1058 0 : out->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( out->lockouts_mem, FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN ) );
1059 :
1060 0 : ulong last_slot = out->root;
1061 0 : for( ushort i=0; i<lockout_offsets_len; i++ ) {
1062 0 : fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
1063 :
1064 0 : ulong offset;
1065 0 : READ_U64_VARINT( offset, data, sz );
1066 :
1067 0 : uchar confirmation_count;
1068 0 : READ_U8( confirmation_count, data, sz );
1069 :
1070 : /* Second bit of custom logic: check that last_slot+offset does not
1071 : overflow */
1072 0 : CHECK( !__builtin_uaddl_overflow( last_slot, offset, &elem->slot ) );
1073 0 : elem->confirmation_count = (uint)confirmation_count;
1074 0 : last_slot = elem->slot;
1075 0 : }
1076 :
1077 0 : out->lockouts_cnt = (ulong)lockout_offsets_len;
1078 :
1079 0 : READ_HASH( out->hash, data, sz );
1080 0 : READ_OPTION( out->has_timestamp, data, sz );
1081 0 : if( out->has_timestamp ) {
1082 0 : READ_I64( out->timestamp, data, sz );
1083 0 : }
1084 0 : READ_HASH( out->block_id, data, sz );
1085 0 : return 0;
1086 0 : }
1087 :
1088 : static int
1089 : deser_vote_authorize_with_seed( fd_vote_authorize_with_seed_args_t * out,
1090 : uchar const ** data,
1091 0 : ulong * sz ) {
1092 0 : CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
1093 0 : READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
1094 :
1095 0 : ulong seed_len;
1096 0 : READ_U64( seed_len, data, sz );
1097 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L42-L49 */
1098 0 : CHECK( seed_len<=FD_VOTE_INSTR_SEED_MAX );
1099 0 : out->current_authority_derived_key_seed_len = seed_len;
1100 :
1101 0 : READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
1102 0 : CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
1103 :
1104 0 : READ_PUBKEY( out->new_authority, data, sz );
1105 0 : return 0;
1106 0 : }
1107 :
1108 : static int
1109 : deser_vote_authorize_checked_with_seed( fd_vote_authorize_checked_with_seed_args_t * out,
1110 : uchar const ** data,
1111 0 : ulong * sz ) {
1112 0 : CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
1113 0 : READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
1114 :
1115 0 : ulong seed_len;
1116 0 : READ_U64( seed_len, data, sz );
1117 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L130 */
1118 0 : CHECK( seed_len<=FD_VOTE_INSTR_SEED_MAX );
1119 0 : out->current_authority_derived_key_seed_len = seed_len;
1120 :
1121 0 : READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
1122 0 : CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
1123 0 : return 0;
1124 0 : }
1125 :
1126 : /**********************************************************************/
1127 : /* Vote instruction -- top-level decoder */
1128 : /**********************************************************************/
1129 :
1130 : static int
1131 : fd_vote_instruction_deserialize_inner( fd_vote_instruction_t * instruction,
1132 : uchar const * data,
1133 114 : ulong data_sz ) {
1134 114 : fd_memset( instruction, 0, sizeof(fd_vote_instruction_t) );
1135 :
1136 114 : uchar const ** p = &data;
1137 114 : ulong * sz = &data_sz;
1138 :
1139 114 : READ_U32( instruction->discriminant, p, sz );
1140 :
1141 114 : switch( instruction->discriminant ) {
1142 :
1143 24 : case fd_vote_instruction_enum_initialize_account:
1144 24 : return deser_vote_init( &instruction->initialize_account, p, sz );
1145 :
1146 3 : case fd_vote_instruction_enum_authorize: {
1147 3 : READ_PUBKEY( instruction->authorize.pubkey, p, sz );
1148 3 : return deser_vote_authorize( &instruction->authorize.vote_authorize, p, sz );
1149 3 : }
1150 :
1151 0 : case fd_vote_instruction_enum_vote:
1152 0 : return deser_vote( &instruction->vote, p, sz );
1153 :
1154 0 : case fd_vote_instruction_enum_withdraw:
1155 0 : READ_U64( instruction->withdraw, p, sz );
1156 0 : return 0;
1157 :
1158 6 : case fd_vote_instruction_enum_update_validator_identity:
1159 6 : return 0;
1160 :
1161 0 : case fd_vote_instruction_enum_update_commission:
1162 0 : READ_U8( instruction->update_commission, p, sz );
1163 0 : return 0;
1164 :
1165 0 : case fd_vote_instruction_enum_vote_switch:
1166 0 : CHECK( !deser_vote( &instruction->vote_switch.vote, p, sz ) );
1167 0 : READ_HASH( instruction->vote_switch.hash, p, sz );
1168 0 : return 0;
1169 :
1170 0 : case fd_vote_instruction_enum_authorize_checked:
1171 0 : return deser_vote_authorize( &instruction->authorize_checked, p, sz );
1172 :
1173 0 : case fd_vote_instruction_enum_update_vote_state:
1174 0 : return deser_vote_state_update( &instruction->update_vote_state, p, sz );
1175 :
1176 0 : case fd_vote_instruction_enum_update_vote_state_switch:
1177 0 : CHECK( !deser_vote_state_update( &instruction->update_vote_state_switch.vote_state_update, p, sz ) );
1178 0 : READ_HASH( instruction->update_vote_state_switch.hash, p, sz );
1179 0 : return 0;
1180 :
1181 0 : case fd_vote_instruction_enum_authorize_with_seed:
1182 0 : return deser_vote_authorize_with_seed( &instruction->authorize_with_seed, p, sz );
1183 :
1184 0 : case fd_vote_instruction_enum_authorize_checked_with_seed:
1185 0 : return deser_vote_authorize_checked_with_seed( &instruction->authorize_checked_with_seed, p, sz );
1186 :
1187 0 : case fd_vote_instruction_enum_compact_update_vote_state:
1188 0 : return deser_compact_vote_state_update( &instruction->compact_update_vote_state, p, sz );
1189 :
1190 0 : case fd_vote_instruction_enum_compact_update_vote_state_switch:
1191 0 : CHECK( !deser_compact_vote_state_update( &instruction->compact_update_vote_state_switch.compact_vote_state_update, p, sz ) );
1192 0 : READ_HASH( instruction->compact_update_vote_state_switch.hash, p, sz );
1193 0 : return 0;
1194 :
1195 0 : case fd_vote_instruction_enum_tower_sync:
1196 0 : return deser_tower_sync( &instruction->tower_sync, p, sz );
1197 :
1198 0 : case fd_vote_instruction_enum_tower_sync_switch:
1199 0 : CHECK( !deser_tower_sync( &instruction->tower_sync_switch.tower_sync, p, sz ) );
1200 0 : READ_HASH( instruction->tower_sync_switch.hash, p, sz );
1201 0 : return 0;
1202 :
1203 6 : case fd_vote_instruction_enum_initialize_account_v2:
1204 6 : return deser_vote_init_v2( &instruction->initialize_account_v2, p, sz );
1205 :
1206 72 : case fd_vote_instruction_enum_update_commission_collector:
1207 72 : READ_ENUM( instruction->update_commission_collector.discriminant, 2U, p, sz );
1208 72 : return 0;
1209 :
1210 3 : case fd_vote_instruction_enum_update_commission_bps:
1211 3 : READ_U16( instruction->update_commission_bps.commission_bps, p, sz );
1212 3 : READ_ENUM( instruction->update_commission_bps.kind.discriminant, 2U, p, sz );
1213 3 : return 0;
1214 :
1215 0 : case fd_vote_instruction_enum_deposit_delegator_rewards:
1216 0 : READ_U64( instruction->deposit_delegator_rewards.deposit, p, sz );
1217 0 : return 0;
1218 :
1219 0 : default:
1220 0 : return 1;
1221 114 : }
1222 114 : }
1223 :
1224 : fd_vote_instruction_t *
1225 : fd_vote_instruction_deserialize( fd_vote_instruction_t * instruction,
1226 : uchar const * data,
1227 114 : ulong data_sz ) {
1228 : /* limited_deserialize(data, PACKET_DATA_SIZE)
1229 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L130 */
1230 114 : data_sz = fd_ulong_min( data_sz, FD_TXN_MTU_V0 );
1231 114 : CHECK_RET_NULL( !fd_vote_instruction_deserialize_inner( instruction, data, data_sz ) );
1232 114 : return instruction;
1233 114 : }
|