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