Line data Source code
1 : #include "fd_vote_codec_tmpl.h"
2 : #include "../../../../ballet/utf8/fd_utf8.h"
3 : #include "../../../../ballet/txn/fd_compact_u16.h"
4 : #include "../../../../ballet/txn/fd_txn.h"
5 :
6 : /**********************************************************************/
7 : /* Unified bincode (de)serialization macros. */
8 : /* All macros operate on a cursor (uchar const ** data, ulong * sz). */
9 : /* CHECK returns 1 on failure (non-zero == error). */
10 : /**********************************************************************/
11 :
12 116622 : #define CHECK( cond ) do { \
13 116622 : if( FD_UNLIKELY( !(cond) ) ) return 1; \
14 116622 : } while( 0 )
15 :
16 690 : #define CHECK_RET_NULL( cond ) do { \
17 690 : if( FD_UNLIKELY( !(cond) ) ) return NULL; \
18 690 : } while( 0 )
19 :
20 2241 : #define CHECK_U64_MUL_OVERFLOW( a, b ) do { \
21 2241 : ulong _dummy; \
22 2241 : CHECK( !__builtin_umull_overflow( (a), (b), &_dummy ) ); \
23 2241 : } while( 0 )
24 :
25 : /**********************************************************************/
26 : /* Read macros */
27 : /**********************************************************************/
28 :
29 1401 : #define READ_BYTES( dst, n, data, sz ) do { \
30 1401 : CHECK( (n)<=(*(sz)) ); \
31 1401 : fd_memcpy( (dst), *(data), (n) ); \
32 1392 : *(data) += (n); \
33 1392 : *(sz) -= (n); \
34 1392 : } while( 0 )
35 :
36 2931 : #define SKIP_BYTES( n, data, sz ) do { \
37 2931 : CHECK( (n)<=(*(sz)) ); \
38 2931 : *(data) += (n); \
39 2931 : *(sz) -= (n); \
40 2931 : } while( 0 )
41 :
42 1923 : #define READ_U8( dst, data, sz ) do { \
43 1923 : CHECK( 1UL<=(*(sz)) ); \
44 1923 : (dst) = FD_LOAD( uchar, *(data) ); \
45 1923 : *(data) += 1UL; \
46 1923 : *(sz) -= 1UL; \
47 1923 : } while( 0 )
48 :
49 405 : #define READ_U16( dst, data, sz ) do { \
50 405 : CHECK( 2UL<=(*(sz)) ); \
51 405 : (dst) = FD_LOAD( ushort, *(data) ); \
52 405 : *(data) += 2UL; \
53 405 : *(sz) -= 2UL; \
54 405 : } while( 0 )
55 :
56 1797 : #define READ_U32( dst, data, sz ) do { \
57 1797 : CHECK( 4UL<=(*(sz)) ); \
58 1797 : (dst) = FD_LOAD( uint, *(data) ); \
59 1797 : *(data) += 4UL; \
60 1797 : *(sz) -= 4UL; \
61 1797 : } while( 0 )
62 :
63 3270 : #define READ_U64( dst, data, sz ) do { \
64 3270 : CHECK( 8UL<=(*(sz)) ); \
65 3270 : (dst) = FD_LOAD( ulong, *(data) ); \
66 3270 : *(data) += 8UL; \
67 3270 : *(sz) -= 8UL; \
68 3270 : } 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 1032 : READ_BYTES( (dst).uc, 32UL, data, sz )
79 :
80 1032 : #define READ_PUBKEY READ_HASH
81 :
82 402 : #define READ_BOOL( dst, data, sz ) do { \
83 402 : READ_U8( dst, data, sz ); \
84 402 : CHECK( (dst)==0 || (dst)==1 ); \
85 402 : } while( 0 )
86 :
87 396 : #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 29394 : #define WRITE_BYTES( src, n, out, out_sz ) do { \
130 29394 : CHECK( (n)<=(*(out_sz)) ); \
131 29394 : fd_memcpy( *(out), (src), (n) ); \
132 29394 : *(out) += (n); \
133 29394 : *(out_sz) -= (n); \
134 29394 : } while( 0 )
135 :
136 8415 : #define WRITE_U8( val, out, out_sz ) do { \
137 8415 : CHECK( 1UL<=(*(out_sz)) ); \
138 8415 : FD_STORE( uchar, *(out), (val) ); \
139 8415 : *(out) += 1UL; \
140 8415 : *(out_sz) -= 1UL; \
141 8415 : } while( 0 )
142 :
143 8400 : #define WRITE_U16( val, out, out_sz ) do { \
144 8400 : CHECK( 2UL<=(*(out_sz)) ); \
145 8400 : FD_STORE( ushort, *(out), (val) ); \
146 8400 : *(out) += 2UL; \
147 8400 : *(out_sz) -= 2UL; \
148 8400 : } while( 0 )
149 :
150 4215 : #define WRITE_U32( val, out, out_sz ) do { \
151 4215 : CHECK( 4UL<=(*(out_sz)) ); \
152 4215 : FD_STORE( uint, *(out), (val) ); \
153 4215 : *(out) += 4UL; \
154 4215 : *(out_sz) -= 4UL; \
155 4215 : } while( 0 )
156 :
157 21021 : #define WRITE_U64( val, out, out_sz ) do { \
158 21021 : CHECK( 8UL<=(*(out_sz)) ); \
159 21021 : FD_STORE( ulong, *(out), (val) ); \
160 21021 : *(out) += 8UL; \
161 21021 : *(out_sz) -= 8UL; \
162 21021 : } 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 21000 : WRITE_BYTES( (src).uc, 32UL, out, out_sz )
173 :
174 : #define WRITE_BOOL( val, out, out_sz ) \
175 8400 : WRITE_U8( (uchar)!!(val), out, out_sz )
176 :
177 4200 : #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 201 : ulong * rem ) {
191 201 : ulong votes_len;
192 201 : READ_U64( votes_len, ptr, rem );
193 201 : CHECK( votes_len<=MAX_LOCKOUT_HISTORY );
194 :
195 222 : for( ulong i=0UL; i<votes_len; i++ ) {
196 21 : fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy( votes );
197 21 : if( is_v1_14_11 ) {
198 0 : elem->latency = 0; /* Unused field for v1_14_11 */
199 21 : } else {
200 21 : READ_U8( elem->latency, ptr, rem );
201 21 : }
202 21 : READ_U64( elem->lockout.slot, ptr, rem );
203 21 : READ_U32( elem->lockout.confirmation_count, ptr, rem );
204 21 : }
205 201 : return 0;
206 201 : }
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 201 : ulong * rem ) {
214 201 : uchar opt;
215 201 : READ_OPTION( opt, ptr, rem );
216 201 : *has_root_slot = opt;
217 201 : if( opt ) {
218 6 : READ_U64( *root_slot, ptr, rem );
219 6 : }
220 201 : return 0;
221 201 : }
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 201 : ulong * rem ) {
229 201 : ulong authorized_voters_len;
230 201 : READ_U64( authorized_voters_len, ptr, rem );
231 201 : CHECK( authorized_voters_len<=MAX_AUTHORIZED_VOTERS );
232 399 : for( ulong i=0UL; i<authorized_voters_len; i++ ) {
233 198 : fd_vote_authorized_voter_t * voter = fd_vote_authorized_voters_pool_ele_acquire( pool );
234 198 : READ_U64( voter->epoch, ptr, rem );
235 198 : READ_PUBKEY( voter->pubkey, ptr, rem );
236 198 : voter->prio = voter->pubkey.uc[0];
237 :
238 : /* Check for existing entries, overwrite if exists */
239 198 : fd_vote_authorized_voter_t * existing_voter = fd_vote_authorized_voters_treap_ele_query( treap, voter->epoch, pool );
240 198 : 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 198 : fd_vote_authorized_voters_treap_ele_insert( treap, voter, pool );
246 198 : }
247 201 : return 0;
248 201 : }
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 201 : ulong * rem ) {
269 201 : ulong epoch_credits_len;
270 201 : READ_U64( epoch_credits_len, ptr, rem );
271 201 : CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
272 225 : for( ulong i=0UL; i<epoch_credits_len; i++ ) {
273 24 : fd_vote_epoch_credits_t * elem = deq_fd_vote_epoch_credits_t_push_tail_nocopy( epoch_credits );
274 24 : READ_BYTES( elem, sizeof(fd_vote_epoch_credits_t), ptr, rem );
275 24 : }
276 201 : return 0;
277 201 : }
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 4200 : ulong * out_sz ) {
291 4200 : if( FD_UNLIKELY( votes==NULL ) ) {
292 0 : WRITE_U64( 0UL, out, out_sz );
293 0 : return 0;
294 0 : }
295 :
296 4200 : ulong votes_len = deq_fd_landed_vote_t_cnt( votes );
297 4200 : WRITE_U64( votes_len, out, out_sz );
298 :
299 4200 : for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
300 4215 : !deq_fd_landed_vote_t_iter_done( votes, iter );
301 4200 : iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
302 15 : fd_landed_vote_t const * elem = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
303 15 : if( !is_v1_14_11 ) {
304 15 : WRITE_U8( elem->latency, out, out_sz );
305 15 : }
306 15 : WRITE_U64( elem->lockout.slot, out, out_sz );
307 15 : WRITE_U32( elem->lockout.confirmation_count, out, out_sz );
308 15 : }
309 4200 : return 0;
310 4200 : }
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 4200 : ulong * out_sz ) {
318 4200 : WRITE_BOOL( has_root_slot, out, out_sz );
319 4200 : if( has_root_slot ) {
320 6 : WRITE_U64( root_slot, out, out_sz );
321 6 : }
322 4200 : return 0;
323 4200 : }
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 4200 : ulong * out_sz ) {
331 4200 : if( FD_UNLIKELY( treap==NULL ) ) {
332 0 : WRITE_U64( 0UL, out, out_sz );
333 0 : return 0;
334 0 : }
335 :
336 4200 : ulong len = fd_vote_authorized_voters_treap_ele_cnt( treap );
337 4200 : WRITE_U64( len, out, out_sz );
338 :
339 4200 : for( fd_vote_authorized_voters_treap_fwd_iter_t iter = fd_vote_authorized_voters_treap_fwd_iter_init( treap, pool );
340 8400 : !fd_vote_authorized_voters_treap_fwd_iter_done( iter );
341 4200 : iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, pool ) ) {
342 4200 : fd_vote_authorized_voter_t const * voter = fd_vote_authorized_voters_treap_fwd_iter_ele_const( iter, pool );
343 4200 : WRITE_U64( voter->epoch, out, out_sz );
344 4200 : WRITE_PUBKEY( voter->pubkey, out, out_sz );
345 4200 : }
346 4200 : return 0;
347 4200 : }
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 4200 : ulong * out_sz ) {
368 4200 : if( FD_UNLIKELY( epoch_credits==NULL ) ) {
369 0 : WRITE_U64( 0UL, out, out_sz );
370 0 : return 0;
371 0 : }
372 :
373 4200 : ulong len = deq_fd_vote_epoch_credits_t_cnt( epoch_credits );
374 4200 : WRITE_U64( len, out, out_sz );
375 :
376 4200 : for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( epoch_credits );
377 4269 : !deq_fd_vote_epoch_credits_t_iter_done( epoch_credits, iter );
378 4200 : iter = deq_fd_vote_epoch_credits_t_iter_next( epoch_credits, iter ) ) {
379 69 : fd_vote_epoch_credits_t const * elem = deq_fd_vote_epoch_credits_t_iter_ele_const( epoch_credits, iter );
380 69 : WRITE_BYTES( elem, sizeof(fd_vote_epoch_credits_t), out, out_sz );
381 69 : }
382 4200 : return 0;
383 4200 : }
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 339 : #define WIRE_OFF_NODE_PUBKEY (4UL)
397 0 : #define WIRE_OFF_V1V3_COMMISSION (68UL) /* 4 + 32 + 32 */
398 339 : #define WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR (68UL) /* 4 + 32 + 32 */
399 339 : #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 1437 : #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 339 : fd_pubkey_t * out ) {
426 339 : CHECK( data_sz>=WIRE_OFF_NODE_PUBKEY+32UL );
427 339 : fd_memcpy( out, data+WIRE_OFF_NODE_PUBKEY, 32UL );
428 339 : return 0;
429 339 : }
430 :
431 : int
432 : fd_vote_account_commission_bps( uchar const * data,
433 : ulong data_sz,
434 : int commission_rate_in_basis_points,
435 339 : ushort * out ) {
436 339 : uchar const * ptr = data;
437 339 : ulong remaining = data_sz;
438 :
439 339 : uint discriminant;
440 339 : READ_U32( discriminant, &ptr, &remaining );
441 :
442 339 : 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 339 : case fd_vote_state_versioned_enum_v4:
449 339 : CHECK( data_sz>=WIRE_OFF_V4_COMMISSION_BPS+2UL );
450 339 : *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 339 : if( !commission_rate_in_basis_points ) {
455 267 : *out = (*out / 100U) * 100U;
456 267 : }
457 339 : return 0;
458 0 : default:
459 0 : return 1;
460 339 : }
461 339 : }
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 339 : fd_pubkey_t * block_revenue_collector_out ) {
470 339 : uchar const * ptr = data;
471 339 : ulong remaining = data_sz;
472 :
473 339 : uint discriminant;
474 339 : READ_U32( discriminant, &ptr, &remaining );
475 :
476 339 : 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 339 : case fd_vote_state_versioned_enum_v4:
486 339 : CHECK( data_sz>=WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR+32UL );
487 339 : fd_memcpy( inflation_rewards_collector_out, data+WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR, 32UL );
488 339 : fd_memcpy( block_revenue_collector_out, data+WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR, 32UL );
489 339 : return 0;
490 0 : default:
491 0 : return 1;
492 339 : }
493 339 : }
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 747 : ulong * out_cnt ) {
504 747 : uchar const * ptr = data;
505 747 : ulong remaining = data_sz;
506 :
507 747 : uint discriminant;
508 747 : READ_U32( discriminant, &ptr, &remaining );
509 :
510 747 : 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 747 : case fd_vote_state_versioned_enum_v4: {
566 747 : SKIP_BYTES( WIRE_BLS_OFF_V4-4UL, &ptr, &remaining );
567 :
568 : /* Skip Option<bls_pubkey_compressed> */
569 747 : uchar has_bls_pubkey;
570 747 : READ_U8( has_bls_pubkey, &ptr, &remaining );
571 747 : if( has_bls_pubkey ) {
572 690 : SKIP_BYTES( FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &remaining );
573 690 : }
574 :
575 : /* Skip votes Vec<LandedVote> */
576 747 : ulong votes_len;
577 747 : READ_U64( votes_len, &ptr, &remaining );
578 747 : CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LANDED_VOTE_SZ );
579 747 : SKIP_BYTES( votes_len*WIRE_LANDED_VOTE_SZ, &ptr, &remaining );
580 :
581 : /* Skip root_slot Option<u64> */
582 747 : uchar has_root_slot;
583 747 : READ_U8( has_root_slot, &ptr, &remaining );
584 747 : if( has_root_slot ) {
585 0 : SKIP_BYTES( 8UL, &ptr, &remaining );
586 0 : }
587 :
588 : /* Skip authorized_voters BTreeMap<u64, Pubkey> */
589 747 : ulong authorized_voters_len;
590 747 : READ_U64( authorized_voters_len, &ptr, &remaining );
591 747 : CHECK_U64_MUL_OVERFLOW( authorized_voters_len, WIRE_AUTH_VOTER_SZ );
592 747 : SKIP_BYTES( authorized_voters_len*WIRE_AUTH_VOTER_SZ, &ptr, &remaining );
593 747 : break;
594 747 : }
595 :
596 747 : default:
597 0 : return 1;
598 747 : }
599 :
600 : /* Now at epoch_credits deque */
601 747 : ulong epoch_credits_len;
602 747 : READ_U64( epoch_credits_len, &ptr, &remaining );
603 747 : CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
604 747 : CHECK_U64_MUL_OVERFLOW( epoch_credits_len, sizeof(fd_vote_epoch_credits_t) );
605 747 : CHECK( epoch_credits_len*sizeof(fd_vote_epoch_credits_t)<=remaining );
606 :
607 747 : *out_ptr = (fd_vote_epoch_credits_t const *)ptr;
608 747 : *out_cnt = epoch_credits_len;
609 747 : return 0;
610 747 : }
611 :
612 : int
613 : fd_vote_account_last_timestamp( uchar const * data,
614 : ulong data_sz,
615 408 : fd_vote_block_timestamp_t * out ) {
616 408 : fd_vote_epoch_credits_t const * epoch_credits_ptr;
617 408 : ulong epoch_credits_len;
618 408 : CHECK( !seek_epoch_credits( data, data_sz, &epoch_credits_ptr, &epoch_credits_len ) );
619 :
620 408 : uchar const * timestamp_ptr = (uchar const *)( epoch_credits_ptr + epoch_credits_len );
621 408 : CHECK( timestamp_ptr+sizeof(fd_vote_block_timestamp_t)<=data+data_sz );
622 :
623 408 : fd_memcpy( out, timestamp_ptr, sizeof(fd_vote_block_timestamp_t) );
624 408 : return 0;
625 408 : }
626 :
627 : int
628 : fd_vote_account_is_v4_with_bls_pubkey( uchar const * data,
629 1026 : ulong data_sz ) {
630 1026 : if( FD_UNLIKELY( data_sz<WIRE_BLS_OFF_V4+1UL ) ) return 0;
631 1026 : uint discriminant = FD_LOAD( uint, data );
632 1026 : if( discriminant!=fd_vote_state_versioned_enum_v4 ) return 0;
633 1026 : return !!data[ WIRE_BLS_OFF_V4 ];
634 1026 : }
635 :
636 : int
637 : fd_vote_account_bls_pubkey( uchar const * data,
638 : ulong data_sz,
639 339 : uchar out[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
640 339 : if( FD_UNLIKELY( !fd_vote_account_is_v4_with_bls_pubkey( data, data_sz ) ) ) return 1;
641 339 : if( FD_UNLIKELY( data_sz<WIRE_BLS_OFF_V4+1UL+FD_BLS_PUBKEY_COMPRESSED_SZ ) ) return 1;
642 339 : fd_memcpy( out, data+WIRE_BLS_OFF_V4+1UL, FD_BLS_PUBKEY_COMPRESSED_SZ );
643 339 : return 0;
644 339 : }
645 :
646 : fd_vote_epoch_credits_t const *
647 : fd_vote_account_epoch_credits( uchar const * data,
648 : ulong data_sz,
649 339 : ulong * cnt ) {
650 339 : fd_vote_epoch_credits_t const * ptr;
651 339 : CHECK_RET_NULL( !seek_epoch_credits( data, data_sz, &ptr, cnt ) );
652 339 : return ptr;
653 339 : }
654 :
655 : /**********************************************************************/
656 : /* Vote account state -- public API */
657 : /**********************************************************************/
658 :
659 : fd_vote_state_versioned_t *
660 : fd_vote_state_versioned_new( fd_vote_state_versioned_t * self,
661 4323 : uint kind ) {
662 4323 : if( FD_UNLIKELY( !self ) ) {
663 0 : FD_LOG_WARNING(( "NULL mem" ));
664 0 : return NULL;
665 0 : }
666 :
667 : /* Init and join data structures */
668 4323 : fd_landed_vote_t * votes = deq_fd_landed_vote_t_join(
669 4323 : deq_fd_landed_vote_t_new( self->landed_votes_mem, MAX_LOCKOUT_HISTORY_CAPACITY )
670 4323 : );
671 4323 : fd_vote_epoch_credits_t * epoch_credits = deq_fd_vote_epoch_credits_t_join(
672 4323 : deq_fd_vote_epoch_credits_t_new( self->epoch_credits_mem )
673 4323 : );
674 4323 : fd_vote_authorized_voter_t * authorized_voters_pool = fd_vote_authorized_voters_pool_join(
675 4323 : fd_vote_authorized_voters_pool_new( self->authorized_voters_pool_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
676 4323 : );
677 4323 : fd_vote_authorized_voters_treap_t * authorized_voters_treap = fd_vote_authorized_voters_treap_join(
678 4323 : fd_vote_authorized_voters_treap_new( self->authorized_voters_treap_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
679 4323 : );
680 :
681 4323 : self->kind = kind;
682 4323 : switch( kind ) {
683 27 : case fd_vote_state_versioned_enum_uninitialized:
684 27 : break;
685 3 : case fd_vote_state_versioned_enum_v1_14_11:
686 3 : memset( &self->v1_14_11, 0, sizeof(fd_vote_state_1_14_11_t) );
687 3 : self->v1_14_11.votes = votes;
688 3 : self->v1_14_11.epoch_credits = epoch_credits;
689 3 : self->v1_14_11.authorized_voters.pool = authorized_voters_pool;
690 3 : self->v1_14_11.authorized_voters.treap = authorized_voters_treap;
691 3 : break;
692 9 : case fd_vote_state_versioned_enum_v3:
693 9 : memset( &self->v3, 0, sizeof(fd_vote_state_v3_t) );
694 9 : self->v3.votes = votes;
695 9 : self->v3.epoch_credits = epoch_credits;
696 9 : self->v3.authorized_voters.pool = authorized_voters_pool;
697 9 : self->v3.authorized_voters.treap = authorized_voters_treap;
698 9 : break;
699 4284 : case fd_vote_state_versioned_enum_v4:
700 4284 : memset( &self->v4, 0, sizeof(fd_vote_state_v4_t) );
701 4284 : self->v4.votes = votes;
702 4284 : self->v4.epoch_credits = epoch_credits;
703 4284 : self->v4.authorized_voters.pool = authorized_voters_pool;
704 4284 : self->v4.authorized_voters.treap = authorized_voters_treap;
705 4284 : break;
706 0 : default:
707 0 : return NULL;
708 4323 : }
709 :
710 4323 : return self;
711 4323 : }
712 :
713 : static int
714 : fd_vote_state_versioned_deserialize_inner( fd_vote_state_versioned_t * self,
715 : uchar const * payload,
716 237 : ulong payload_sz ) {
717 237 : CHECK( self!=NULL );
718 237 : CHECK( payload!=NULL );
719 :
720 237 : uchar const * ptr = payload;
721 237 : ulong rem = payload_sz;
722 :
723 237 : uint kind;
724 237 : READ_U32( kind, &ptr, &rem );
725 :
726 237 : CHECK( fd_vote_state_versioned_new( self, kind )!=NULL );
727 :
728 237 : switch( self->kind ) {
729 27 : case fd_vote_state_versioned_enum_uninitialized:
730 : /* No-op, nothing to decode */
731 27 : return 0;
732 :
733 3 : case fd_vote_state_versioned_enum_v1_14_11: {
734 3 : READ_PUBKEY( self->v1_14_11.node_pubkey, &ptr, &rem );
735 0 : READ_PUBKEY( self->v1_14_11.authorized_withdrawer, &ptr, &rem );
736 0 : READ_U8( self->v1_14_11.commission, &ptr, &rem );
737 0 : CHECK( !deser_votes( self->v1_14_11.votes, 1, &ptr, &rem ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
738 0 : CHECK( !deser_root_slot( &self->v1_14_11.has_root_slot, &self->v1_14_11.root_slot, &ptr, &rem ) );
739 0 : CHECK( !deser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &ptr, &rem ) );
740 0 : CHECK( !deser_prior_voters( &self->v1_14_11.prior_voters, &ptr, &rem ) );
741 0 : CHECK( !deser_epoch_credits( self->v1_14_11.epoch_credits, &ptr, &rem ) );
742 0 : READ_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
743 0 : break;
744 0 : }
745 :
746 9 : case fd_vote_state_versioned_enum_v3: {
747 9 : READ_PUBKEY( self->v3.node_pubkey, &ptr, &rem );
748 6 : READ_PUBKEY( self->v3.authorized_withdrawer, &ptr, &rem );
749 6 : READ_U8( self->v3.commission, &ptr, &rem );
750 6 : CHECK( !deser_votes( self->v3.votes, 0, &ptr, &rem ) );
751 6 : CHECK( !deser_root_slot( &self->v3.has_root_slot, &self->v3.root_slot, &ptr, &rem ) );
752 6 : CHECK( !deser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &ptr, &rem ) );
753 6 : CHECK( !deser_prior_voters( &self->v3.prior_voters, &ptr, &rem ) );
754 6 : CHECK( !deser_epoch_credits( self->v3.epoch_credits, &ptr, &rem ) );
755 6 : READ_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
756 6 : break;
757 6 : }
758 :
759 198 : case fd_vote_state_versioned_enum_v4: {
760 198 : READ_PUBKEY( self->v4.node_pubkey, &ptr, &rem );
761 195 : READ_PUBKEY( self->v4.authorized_withdrawer, &ptr, &rem );
762 195 : READ_PUBKEY( self->v4.inflation_rewards_collector, &ptr, &rem );
763 195 : READ_PUBKEY( self->v4.block_revenue_collector, &ptr, &rem );
764 195 : READ_U16( self->v4.inflation_rewards_commission_bps, &ptr, &rem );
765 195 : READ_U16( self->v4.block_revenue_commission_bps, &ptr, &rem );
766 195 : READ_U64( self->v4.pending_delegator_rewards, &ptr, &rem );
767 :
768 : /* Option<[u8; 48]> */
769 195 : READ_OPTION( self->v4.has_bls_pubkey_compressed, &ptr, &rem );
770 195 : if( self->v4.has_bls_pubkey_compressed ) {
771 102 : READ_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &rem );
772 102 : }
773 :
774 195 : CHECK( !deser_votes( self->v4.votes, 0, &ptr, &rem ) );
775 195 : CHECK( !deser_root_slot( &self->v4.has_root_slot, &self->v4.root_slot, &ptr, &rem ) );
776 195 : CHECK( !deser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &ptr, &rem ) );
777 195 : CHECK( !deser_epoch_credits( self->v4.epoch_credits, &ptr, &rem ) ); /* v4 has no prior_voters */
778 195 : READ_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
779 195 : break;
780 195 : }
781 :
782 195 : default:
783 0 : return 1;
784 237 : }
785 :
786 201 : return 0;
787 237 : }
788 :
789 : fd_vote_state_versioned_t *
790 : fd_vote_state_versioned_deserialize( fd_vote_state_versioned_t * self,
791 : uchar const * payload,
792 237 : ulong payload_sz ) {
793 237 : CHECK_RET_NULL( !fd_vote_state_versioned_deserialize_inner( self, payload, payload_sz ) );
794 228 : return self;
795 237 : }
796 :
797 : int
798 : fd_vote_state_versioned_serialize( fd_vote_state_versioned_t const * self,
799 : uchar * buf,
800 4200 : ulong buf_sz ) {
801 4200 : CHECK( self!=NULL );
802 4200 : CHECK( buf!=NULL );
803 :
804 4200 : uchar * out = buf;
805 4200 : ulong out_sz = buf_sz;
806 :
807 4200 : WRITE_U32( self->kind, &out, &out_sz );
808 :
809 4200 : switch( self->kind ) {
810 0 : case fd_vote_state_versioned_enum_uninitialized:
811 : /* No-op, nothing to encode */
812 0 : return 0;
813 :
814 0 : case fd_vote_state_versioned_enum_v1_14_11: {
815 0 : WRITE_PUBKEY( self->v1_14_11.node_pubkey, &out, &out_sz );
816 0 : WRITE_PUBKEY( self->v1_14_11.authorized_withdrawer, &out, &out_sz );
817 0 : WRITE_U8( self->v1_14_11.commission, &out, &out_sz );
818 0 : CHECK( !ser_votes( self->v1_14_11.votes, 1, &out, &out_sz ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
819 0 : CHECK( !ser_root_slot( self->v1_14_11.has_root_slot, self->v1_14_11.root_slot, &out, &out_sz ) );
820 0 : CHECK( !ser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &out, &out_sz ) );
821 0 : CHECK( !ser_prior_voters( &self->v1_14_11.prior_voters, &out, &out_sz ) );
822 0 : CHECK( !ser_epoch_credits( self->v1_14_11.epoch_credits, &out, &out_sz ) );
823 0 : WRITE_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
824 0 : break;
825 0 : }
826 :
827 0 : case fd_vote_state_versioned_enum_v3: {
828 0 : WRITE_PUBKEY( self->v3.node_pubkey, &out, &out_sz );
829 0 : WRITE_PUBKEY( self->v3.authorized_withdrawer, &out, &out_sz );
830 0 : WRITE_U8( self->v3.commission, &out, &out_sz );
831 0 : CHECK( !ser_votes( self->v3.votes, 0, &out, &out_sz ) );
832 0 : CHECK( !ser_root_slot( self->v3.has_root_slot, self->v3.root_slot, &out, &out_sz ) );
833 0 : CHECK( !ser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &out, &out_sz ) );
834 0 : CHECK( !ser_prior_voters( &self->v3.prior_voters, &out, &out_sz ) );
835 0 : CHECK( !ser_epoch_credits( self->v3.epoch_credits, &out, &out_sz ) );
836 0 : WRITE_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
837 0 : break;
838 0 : }
839 :
840 4200 : case fd_vote_state_versioned_enum_v4: {
841 4200 : WRITE_PUBKEY( self->v4.node_pubkey, &out, &out_sz );
842 4200 : WRITE_PUBKEY( self->v4.authorized_withdrawer, &out, &out_sz );
843 4200 : WRITE_PUBKEY( self->v4.inflation_rewards_collector, &out, &out_sz );
844 4200 : WRITE_PUBKEY( self->v4.block_revenue_collector, &out, &out_sz );
845 4200 : WRITE_U16( self->v4.inflation_rewards_commission_bps, &out, &out_sz );
846 4200 : WRITE_U16( self->v4.block_revenue_commission_bps, &out, &out_sz );
847 4200 : WRITE_U64( self->v4.pending_delegator_rewards, &out, &out_sz );
848 :
849 : /* Option<[u8; 48]> */
850 4200 : WRITE_OPTION( self->v4.has_bls_pubkey_compressed, &out, &out_sz );
851 4200 : if( self->v4.has_bls_pubkey_compressed ) {
852 4125 : WRITE_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &out, &out_sz );
853 4125 : }
854 :
855 4200 : CHECK( !ser_votes( self->v4.votes, 0, &out, &out_sz ) );
856 4200 : CHECK( !ser_root_slot( self->v4.has_root_slot, self->v4.root_slot, &out, &out_sz ) );
857 4200 : CHECK( !ser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &out, &out_sz ) );
858 4200 : CHECK( !ser_epoch_credits( self->v4.epoch_credits, &out, &out_sz ) ); /* v4 has no prior_voters */
859 4200 : WRITE_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
860 4200 : break;
861 4200 : }
862 :
863 4200 : default:
864 0 : return 1;
865 4200 : }
866 :
867 4200 : return 0;
868 4200 : }
869 :
870 : ulong
871 72 : fd_vote_state_versioned_serialized_size( fd_vote_state_versioned_t const * self ) {
872 72 : switch( self->kind ) {
873 :
874 0 : case fd_vote_state_versioned_enum_uninitialized:
875 0 : return 4UL;
876 :
877 0 : case fd_vote_state_versioned_enum_v1_14_11: {
878 0 : ulong votes_cnt = self->v1_14_11.votes ? deq_fd_landed_vote_t_cnt( self->v1_14_11.votes ) : 0UL;
879 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;
880 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;
881 0 : return WIRE_VOTES_OFF_V1_V3
882 0 : + 8UL + votes_cnt * WIRE_LOCKOUT_SZ
883 0 : + 1UL + (ulong)self->v1_14_11.has_root_slot * 8UL
884 0 : + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
885 0 : + WIRE_PRIOR_VOTERS_SZ
886 0 : + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
887 0 : + sizeof(fd_vote_block_timestamp_t);
888 0 : }
889 :
890 0 : case fd_vote_state_versioned_enum_v3: {
891 0 : ulong votes_cnt = self->v3.votes ? deq_fd_landed_vote_t_cnt( self->v3.votes ) : 0UL;
892 0 : ulong auth_voters_cnt = self->v3.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v3.authorized_voters.treap ) : 0UL;
893 0 : ulong epoch_credits_cnt = self->v3.epoch_credits ? deq_fd_vote_epoch_credits_t_cnt( self->v3.epoch_credits ) : 0UL;
894 0 : return WIRE_VOTES_OFF_V1_V3
895 0 : + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
896 0 : + 1UL + (ulong)self->v3.has_root_slot * 8UL
897 0 : + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
898 0 : + WIRE_PRIOR_VOTERS_SZ
899 0 : + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
900 0 : + sizeof(fd_vote_block_timestamp_t);
901 0 : }
902 :
903 72 : case fd_vote_state_versioned_enum_v4: {
904 72 : ulong votes_cnt = self->v4.votes ? deq_fd_landed_vote_t_cnt( self->v4.votes ) : 0UL;
905 72 : ulong auth_voters_cnt = self->v4.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v4.authorized_voters.treap ) : 0UL;
906 72 : ulong epoch_credits_cnt = self->v4.epoch_credits ? deq_fd_vote_epoch_credits_t_cnt( self->v4.epoch_credits ) : 0UL;
907 72 : return WIRE_BLS_OFF_V4
908 72 : + 1UL + (ulong)self->v4.has_bls_pubkey_compressed * FD_BLS_PUBKEY_COMPRESSED_SZ
909 72 : + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
910 72 : + 1UL + (ulong)self->v4.has_root_slot * 8UL
911 72 : + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
912 72 : + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
913 72 : + sizeof(fd_vote_block_timestamp_t);
914 0 : }
915 :
916 0 : default:
917 0 : return 0UL;
918 72 : }
919 72 : }
920 :
921 : /**********************************************************************/
922 : /* Vote instruction -- per-variant deserializers */
923 : /**********************************************************************/
924 :
925 : static int
926 : deser_vote_authorize( fd_vote_authorize_t * out,
927 : uchar const ** data,
928 3 : ulong * sz ) {
929 3 : READ_ENUM( out->discriminant, 3U, data, sz );
930 3 : if( out->discriminant==fd_vote_authorize_enum_voter_with_bls ) {
931 0 : READ_BYTES( out->voter_with_bls.bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
932 0 : READ_BYTES( out->voter_with_bls.bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
933 0 : }
934 3 : return 0;
935 3 : }
936 :
937 : static int
938 : deser_vote_init( fd_vote_init_t * out,
939 : uchar const ** data,
940 24 : ulong * sz ) {
941 24 : READ_BYTES( out, sizeof(fd_vote_init_t), data, sz );
942 24 : return 0;
943 24 : }
944 :
945 : static int
946 : deser_vote_init_v2( fd_vote_init_v2_t * out,
947 : uchar const ** data,
948 6 : ulong * sz ) {
949 6 : READ_PUBKEY( out->node_pubkey, data, sz );
950 6 : READ_PUBKEY( out->authorized_voter, data, sz );
951 6 : READ_BYTES( out->authorized_voter_bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
952 6 : READ_BYTES( out->authorized_voter_bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
953 6 : READ_PUBKEY( out->authorized_withdrawer, data, sz );
954 6 : READ_U16( out->inflation_rewards_commission_bps, data, sz );
955 6 : READ_PUBKEY( out->inflation_rewards_collector, data, sz );
956 6 : READ_U16( out->block_revenue_commission_bps, data, sz );
957 6 : READ_PUBKEY( out->block_revenue_collector, data, sz );
958 6 : return 0;
959 6 : }
960 :
961 : static int
962 : deser_vote( fd_vote_t * out,
963 : uchar const ** data,
964 0 : ulong * sz ) {
965 0 : ulong slots_len;
966 0 : READ_U64( slots_len, data, sz );
967 0 : CHECK( slots_len<=FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN );
968 :
969 0 : out->slots = deq_ulong_join( deq_ulong_new( out->slots_mem, FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN ) );
970 :
971 0 : for( ulong i=0UL; i<slots_len; i++ ) {
972 0 : ulong * elem = deq_ulong_push_tail_nocopy( out->slots );
973 0 : READ_U64( *elem, data, sz );
974 0 : }
975 :
976 0 : READ_HASH( out->hash, data, sz );
977 0 : READ_OPTION( out->has_timestamp, data, sz );
978 0 : if( out->has_timestamp ) {
979 0 : READ_I64( out->timestamp, data, sz );
980 0 : }
981 0 : return 0;
982 0 : }
983 :
984 : static int
985 : deser_vote_state_update( fd_vote_state_update_t * out,
986 : uchar const ** data,
987 0 : ulong * sz ) {
988 0 : ulong lockouts_len;
989 0 : READ_U64( lockouts_len, data, sz );
990 0 : CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUTS_LEN );
991 :
992 0 : out->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( out->lockouts_mem, FD_VOTE_INSTR_MAX_LOCKOUTS_LEN ) );
993 :
994 0 : for( ulong i=0; i<lockouts_len; i++ ) {
995 0 : fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
996 0 : READ_U64( elem->slot, data, sz );
997 0 : READ_U32( elem->confirmation_count, data, sz );
998 0 : }
999 :
1000 0 : READ_OPTION( out->has_root, data, sz );
1001 0 : if( out->has_root ) {
1002 0 : READ_U64( out->root, data, sz );
1003 0 : }
1004 0 : READ_HASH( out->hash, data, sz );
1005 0 : READ_OPTION( out->has_timestamp, data, sz );
1006 0 : if( out->has_timestamp ) {
1007 0 : READ_I64( out->timestamp, data, sz );
1008 0 : }
1009 0 : return 0;
1010 0 : }
1011 :
1012 : /* This function contains slightly custom deserialization logic to
1013 : adhere to serde_compact_vote_state_update in Agave.
1014 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L265-L299 */
1015 : static int
1016 : deser_compact_vote_state_update( fd_compact_vote_state_update_t * out,
1017 : uchar const ** data,
1018 0 : ulong * sz ) {
1019 0 : READ_U64( out->root, data, sz );
1020 :
1021 0 : ushort lockouts_len;
1022 0 : READ_COMPACT_U16( lockouts_len, data, sz );
1023 0 : CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
1024 0 : out->lockouts_len = lockouts_len;
1025 :
1026 0 : out->lockouts = (fd_lockout_offset_t *)out->lockouts_mem;
1027 :
1028 0 : ulong slot = out->root!=ULONG_MAX ? out->root : 0UL;
1029 0 : for( ushort i=0; i<lockouts_len; i++ ) {
1030 0 : READ_U64_VARINT( out->lockouts[i].offset, data, sz );
1031 0 : READ_U8( out->lockouts[i].confirmation_count, data, sz );
1032 :
1033 : /* Custom logic: check that slot+lockout_offset
1034 : does not overflow */
1035 0 : CHECK( !__builtin_uaddl_overflow( slot, out->lockouts[i].offset, &slot ) );
1036 0 : }
1037 :
1038 0 : READ_HASH( out->hash, data, sz );
1039 0 : READ_OPTION( out->has_timestamp, data, sz );
1040 0 : if( out->has_timestamp ) {
1041 0 : READ_I64( out->timestamp, data, sz );
1042 0 : }
1043 0 : return 0;
1044 0 : }
1045 :
1046 : /* Similar to above. Tower sync uses delta-encoded lockout offsets,
1047 : converting them to absolute slot numbers on the fly. Mirrors the
1048 : checked arithmetic from Agave's custom deserializer:
1049 : https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L360-L396 */
1050 :
1051 : static int
1052 : deser_tower_sync( fd_tower_sync_t * out,
1053 : uchar const ** data,
1054 0 : ulong * sz ) {
1055 0 : READ_U64( out->root, data, sz );
1056 0 : out->has_root = 1;
1057 :
1058 : /* First bit of custom logic: if root is ULONG_MAX, set root to 0. */
1059 0 : if( FD_UNLIKELY( out->root==ULONG_MAX ) ) {
1060 0 : out->has_root = 0;
1061 0 : out->root = 0UL;
1062 0 : }
1063 :
1064 0 : ushort lockout_offsets_len;
1065 0 : READ_COMPACT_U16( lockout_offsets_len, data, sz );
1066 0 : CHECK( lockout_offsets_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
1067 :
1068 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 ) );
1069 :
1070 0 : ulong last_slot = out->root;
1071 0 : for( ushort i=0; i<lockout_offsets_len; i++ ) {
1072 0 : fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
1073 :
1074 0 : ulong offset;
1075 0 : READ_U64_VARINT( offset, data, sz );
1076 :
1077 0 : uchar confirmation_count;
1078 0 : READ_U8( confirmation_count, data, sz );
1079 :
1080 : /* Second bit of custom logic: check that last_slot+offset does not
1081 : overflow */
1082 0 : CHECK( !__builtin_uaddl_overflow( last_slot, offset, &elem->slot ) );
1083 0 : elem->confirmation_count = (uint)confirmation_count;
1084 0 : last_slot = elem->slot;
1085 0 : }
1086 :
1087 0 : out->lockouts_cnt = (ulong)lockout_offsets_len;
1088 :
1089 0 : READ_HASH( out->hash, data, sz );
1090 0 : READ_OPTION( out->has_timestamp, data, sz );
1091 0 : if( out->has_timestamp ) {
1092 0 : READ_I64( out->timestamp, data, sz );
1093 0 : }
1094 0 : READ_HASH( out->block_id, data, sz );
1095 0 : return 0;
1096 0 : }
1097 :
1098 : static int
1099 : deser_vote_authorize_with_seed( fd_vote_authorize_with_seed_args_t * out,
1100 : uchar const ** data,
1101 0 : ulong * sz ) {
1102 0 : CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
1103 0 : READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
1104 :
1105 0 : ulong seed_len;
1106 0 : READ_U64( seed_len, data, sz );
1107 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L42-L49 */
1108 0 : CHECK( seed_len<=FD_VOTE_INSTR_SEED_MAX );
1109 0 : out->current_authority_derived_key_seed_len = seed_len;
1110 :
1111 0 : READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
1112 0 : CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
1113 :
1114 0 : READ_PUBKEY( out->new_authority, data, sz );
1115 0 : return 0;
1116 0 : }
1117 :
1118 : static int
1119 : deser_vote_authorize_checked_with_seed( fd_vote_authorize_checked_with_seed_args_t * out,
1120 : uchar const ** data,
1121 0 : ulong * sz ) {
1122 0 : CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
1123 0 : READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
1124 :
1125 0 : ulong seed_len;
1126 0 : READ_U64( seed_len, data, sz );
1127 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L130 */
1128 0 : CHECK( seed_len<=FD_VOTE_INSTR_SEED_MAX );
1129 0 : out->current_authority_derived_key_seed_len = seed_len;
1130 :
1131 0 : READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
1132 0 : CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
1133 0 : return 0;
1134 0 : }
1135 :
1136 : /**********************************************************************/
1137 : /* Vote instruction -- top-level decoder */
1138 : /**********************************************************************/
1139 :
1140 : static int
1141 : fd_vote_instruction_deserialize_inner( fd_vote_instruction_t * instruction,
1142 : uchar const * data,
1143 114 : ulong data_sz ) {
1144 114 : fd_memset( instruction, 0, sizeof(fd_vote_instruction_t) );
1145 :
1146 114 : uchar const ** p = &data;
1147 114 : ulong * sz = &data_sz;
1148 :
1149 114 : READ_U32( instruction->discriminant, p, sz );
1150 :
1151 114 : switch( instruction->discriminant ) {
1152 :
1153 24 : case fd_vote_instruction_enum_initialize_account:
1154 24 : return deser_vote_init( &instruction->initialize_account, p, sz );
1155 :
1156 3 : case fd_vote_instruction_enum_authorize: {
1157 3 : READ_PUBKEY( instruction->authorize.pubkey, p, sz );
1158 3 : return deser_vote_authorize( &instruction->authorize.vote_authorize, p, sz );
1159 3 : }
1160 :
1161 0 : case fd_vote_instruction_enum_vote:
1162 0 : return deser_vote( &instruction->vote, p, sz );
1163 :
1164 0 : case fd_vote_instruction_enum_withdraw:
1165 0 : READ_U64( instruction->withdraw, p, sz );
1166 0 : return 0;
1167 :
1168 6 : case fd_vote_instruction_enum_update_validator_identity:
1169 6 : return 0;
1170 :
1171 0 : case fd_vote_instruction_enum_update_commission:
1172 0 : READ_U8( instruction->update_commission, p, sz );
1173 0 : return 0;
1174 :
1175 0 : case fd_vote_instruction_enum_vote_switch:
1176 0 : CHECK( !deser_vote( &instruction->vote_switch.vote, p, sz ) );
1177 0 : READ_HASH( instruction->vote_switch.hash, p, sz );
1178 0 : return 0;
1179 :
1180 0 : case fd_vote_instruction_enum_authorize_checked:
1181 0 : return deser_vote_authorize( &instruction->authorize_checked, p, sz );
1182 :
1183 0 : case fd_vote_instruction_enum_update_vote_state:
1184 0 : return deser_vote_state_update( &instruction->update_vote_state, p, sz );
1185 :
1186 0 : case fd_vote_instruction_enum_update_vote_state_switch:
1187 0 : CHECK( !deser_vote_state_update( &instruction->update_vote_state_switch.vote_state_update, p, sz ) );
1188 0 : READ_HASH( instruction->update_vote_state_switch.hash, p, sz );
1189 0 : return 0;
1190 :
1191 0 : case fd_vote_instruction_enum_authorize_with_seed:
1192 0 : return deser_vote_authorize_with_seed( &instruction->authorize_with_seed, p, sz );
1193 :
1194 0 : case fd_vote_instruction_enum_authorize_checked_with_seed:
1195 0 : return deser_vote_authorize_checked_with_seed( &instruction->authorize_checked_with_seed, p, sz );
1196 :
1197 0 : case fd_vote_instruction_enum_compact_update_vote_state:
1198 0 : return deser_compact_vote_state_update( &instruction->compact_update_vote_state, p, sz );
1199 :
1200 0 : case fd_vote_instruction_enum_compact_update_vote_state_switch:
1201 0 : CHECK( !deser_compact_vote_state_update( &instruction->compact_update_vote_state_switch.compact_vote_state_update, p, sz ) );
1202 0 : READ_HASH( instruction->compact_update_vote_state_switch.hash, p, sz );
1203 0 : return 0;
1204 :
1205 0 : case fd_vote_instruction_enum_tower_sync:
1206 0 : return deser_tower_sync( &instruction->tower_sync, p, sz );
1207 :
1208 0 : case fd_vote_instruction_enum_tower_sync_switch:
1209 0 : CHECK( !deser_tower_sync( &instruction->tower_sync_switch.tower_sync, p, sz ) );
1210 0 : READ_HASH( instruction->tower_sync_switch.hash, p, sz );
1211 0 : return 0;
1212 :
1213 6 : case fd_vote_instruction_enum_initialize_account_v2:
1214 6 : return deser_vote_init_v2( &instruction->initialize_account_v2, p, sz );
1215 :
1216 72 : case fd_vote_instruction_enum_update_commission_collector:
1217 72 : READ_ENUM( instruction->update_commission_collector.discriminant, 2U, p, sz );
1218 72 : return 0;
1219 :
1220 3 : case fd_vote_instruction_enum_update_commission_bps:
1221 3 : READ_U16( instruction->update_commission_bps.commission_bps, p, sz );
1222 3 : READ_ENUM( instruction->update_commission_bps.kind.discriminant, 2U, p, sz );
1223 3 : return 0;
1224 :
1225 0 : case fd_vote_instruction_enum_deposit_delegator_rewards:
1226 0 : READ_U64( instruction->deposit_delegator_rewards.deposit, p, sz );
1227 0 : return 0;
1228 :
1229 0 : default:
1230 0 : return 1;
1231 114 : }
1232 114 : }
1233 :
1234 : fd_vote_instruction_t *
1235 : fd_vote_instruction_deserialize( fd_vote_instruction_t * instruction,
1236 : uchar const * data,
1237 114 : ulong data_sz ) {
1238 : /* limited_deserialize(data, PACKET_DATA_SIZE)
1239 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L130 */
1240 114 : data_sz = fd_ulong_min( data_sz, FD_TXN_MTU_V0 );
1241 114 : CHECK_RET_NULL( !fd_vote_instruction_deserialize_inner( instruction, data, data_sz ) );
1242 114 : return instruction;
1243 114 : }
|