Line data Source code
1 : /* Serialize a chunk of values.
2 : Each call may only produce up to FD_SSMANIFEST_BUF_MIN bytes worth of
3 : serialized data. Therefore, we split the serializer into multiple
4 : states. */
5 :
6 0 : ENCODE_FN {
7 0 : fd_bank_t const * bank = enc->bank;
8 :
9 0 : PREP
10 :
11 0 : switch( enc->state ) {
12 0 : case STATE_BLOCKHASH_QUEUE: {
13 0 : fd_blockhashes_t const * bhq = &bank->f.block_hash_queue;
14 0 : fd_blockhash_info_t const * deq = bhq->d.deque;
15 0 : ulong total = fd_blockhash_deq_cnt( deq );
16 :
17 : /* Write the newest FD_BLOCKHASHES_MAX entries by count, each at its
18 : own absolute hash_index. with skipped indices it can keep an
19 : entry older than max_age. */
20 0 : ulong cnt = fd_ulong_min( total, FD_BLOCKHASHES_MAX );
21 0 : ulong to_skip = total - cnt;
22 :
23 0 : fd_blockhash_info_t const * last = fd_blockhashes_peek_last( bhq );
24 0 : PUSH_VAL( ulong, last ? last->hash_index : 0UL ); /* last hash index */
25 0 : PUSH_VAL( uchar, !!last );
26 0 : if( last ) PUSH_VAL( fd_hash_t, last->hash );
27 :
28 0 : PUSH_VAL( ulong, cnt );
29 0 : for( ulong i=to_skip; i<total; i++ ) {
30 0 : fd_blockhash_info_t const * ele = fd_blockhash_deq_peek_index_const( deq, i );
31 0 : PUSH_VAL( fd_hash_t, ele->hash );
32 0 : PUSH_VAL( ulong, ele->lamports_per_signature );
33 0 : PUSH_VAL( ulong, ele->hash_index );
34 0 : PUSH_VAL( ulong, 0UL ); /* timestamp, ignored */
35 0 : }
36 0 : PUSH_VAL( ulong, FD_BLOCKHASHES_MAX-1UL ); /* max_age */
37 0 : enc->state = STATE_HASHES;
38 0 : break;
39 0 : }
40 0 : case STATE_HASHES: {
41 0 : PUSH_VAL( ulong, 0UL ); /* ancestors */
42 0 : PUSH_VAL( fd_hash_t, bank->f.bank_hash );
43 0 : PUSH_VAL( fd_hash_t, bank->f.prev_bank_hash );
44 0 : PUSH_VAL( ulong, bank->f.parent_slot );
45 0 : enc->state = STATE_HARD_FORKS;
46 0 : break;
47 0 : }
48 0 : case STATE_HARD_FORKS: {
49 0 : PUSH_VAL( ulong, bank->f.hard_fork_cnt );
50 0 : for( ulong i=0UL; i<bank->f.hard_fork_cnt; i++ ) {
51 0 : PUSH_VAL( ulong, bank->f.hard_forks[ i ].slot );
52 0 : PUSH_VAL( ulong, bank->f.hard_forks[ i ].cnt );
53 0 : }
54 0 : enc->state = STATE_COUNTERS;
55 0 : break;
56 0 : }
57 0 : case STATE_COUNTERS: {
58 0 : PUSH_VAL( ulong, bank->f.parent_txn_count + bank->f.txn_count );
59 0 : PUSH_VAL( ulong, bank->f.max_tick_height );
60 0 : PUSH_VAL( ulong, bank->f.signature_count );
61 0 : PUSH_VAL( ulong, bank->f.capitalization );
62 0 : PUSH_VAL( ulong, bank->f.max_tick_height );
63 0 : PUSH_VAL( uchar, 1 );
64 0 : PUSH_VAL( ulong, bank->f.slot_params.hashes_per_tick );
65 0 : PUSH_VAL( ulong, bank->f.ticks_per_slot );
66 0 : PUSH_VAL( fd_w_u128_t, (fd_w_u128_t){ .ud = bank->f.slot_params.ns_per_slot } );
67 0 : PUSH_VAL( ulong, bank->f.genesis_creation_time );
68 0 : PUSH_VAL( double, bank->f.slot_params.slots_per_year );
69 0 : PUSH_VAL( ulong, 0UL ); /* accounts_data_len, unused */
70 0 : PUSH_VAL( ulong, bank->f.slot );
71 0 : PUSH_VAL( ulong, bank->f.epoch );
72 0 : PUSH_VAL( ulong, bank->f.block_height );
73 0 : PUSH_VAL( fd_pubkey_t, enc->leader );
74 0 : PUSH_VAL( ulong, 0UL ); /* unused_collector_fees */
75 0 : PUSH_VAL( ulong, 0UL ); /* unused_fee_calculator */
76 :
77 0 : fd_fee_rate_governor_t const * frg = &bank->f.fee_rate_governor;
78 0 : PUSH_VAL( ulong, frg->target_lamports_per_signature );
79 0 : PUSH_VAL( ulong, frg->target_signatures_per_slot );
80 0 : PUSH_VAL( ulong, frg->min_lamports_per_signature );
81 0 : PUSH_VAL( ulong, frg->max_lamports_per_signature );
82 0 : PUSH_VAL( uchar, frg->burn_percent );
83 0 : PUSH_VAL( ulong, 0UL );
84 0 : PUSH_VAL( ulong, bank->f.epoch );
85 :
86 0 : fd_epoch_schedule_t const * es = &bank->f.epoch_schedule;
87 0 : fd_rent_t const * rent = &bank->f.rent;
88 0 : PUSH_VAL( ulong, es->slots_per_epoch );
89 0 : PUSH_VAL( ulong, es->leader_schedule_slot_offset );
90 0 : PUSH_VAL( uchar, es->warmup );
91 0 : PUSH_VAL( ulong, es->first_normal_epoch );
92 0 : PUSH_VAL( ulong, es->first_normal_slot );
93 0 : PUSH_VAL( double, bank->f.slot_params.slots_per_year );
94 0 : PUSH_VAL( ulong, rent->lamports_per_uint8_year );
95 0 : PUSH_VAL( double, rent->exemption_threshold );
96 0 : PUSH_VAL( uchar, rent->burn_percent );
97 0 : PUSH_VAL( ulong, es->slots_per_epoch );
98 0 : PUSH_VAL( ulong, es->leader_schedule_slot_offset );
99 0 : PUSH_VAL( uchar, es->warmup );
100 0 : PUSH_VAL( ulong, es->first_normal_epoch );
101 0 : PUSH_VAL( ulong, es->first_normal_slot );
102 :
103 0 : fd_inflation_t const * inf = &bank->f.inflation;
104 0 : PUSH_VAL( double, inf->initial );
105 0 : PUSH_VAL( double, inf->terminal );
106 0 : PUSH_VAL( double, inf->taper );
107 0 : PUSH_VAL( double, inf->foundation );
108 0 : PUSH_VAL( double, inf->foundation_term );
109 0 : PUSH_VAL( double, inf->unused );
110 :
111 0 : enc->state = STATE_VOTE_ACCOUNTS;
112 0 : break;
113 0 : }
114 0 : case STATE_VOTE_ACCOUNTS: {
115 0 : PUSH_VAL( ulong, 0UL ); /* zero vote_accounts */
116 0 : PUSH_VAL( ulong, 0UL ); /* zero stake delegations */
117 0 : PUSH_VAL( ulong, 0UL ); /* unused */
118 0 : PUSH_VAL( ulong, bank->f.epoch );
119 0 : enc->state = STATE_STAKE_HISTORY;
120 0 : break;
121 0 : }
122 0 : case STATE_STAKE_DELEGATION: { FD_LOG_ERR(( "TODO")); }
123 0 : case STATE_STAKE_EPOCH: { FD_LOG_ERR(( "TODO")); }
124 0 : case STATE_STAKE_HISTORY: {
125 0 : PUSH_VAL( ulong, 0UL ); /* zero stake history entries */
126 0 : enc->state = STATE_BANK_TRAILER;
127 0 : break;
128 0 : }
129 0 : case STATE_BANK_TRAILER: {
130 0 : PUSH_VAL( ulong, 0UL ); /* unused_accounts.unused1 */
131 0 : PUSH_VAL( ulong, 0UL ); /* unused_accounts.unused2 */
132 0 : PUSH_VAL( ulong, 0UL ); /* unused_accounts.unused3 */
133 0 : PUSH_VAL( ulong, 0UL ); /* unused_epoch_stakes */
134 0 : PUSH_VAL( uchar, 0 ); /* is_delta */
135 0 : PUSH_VAL( ulong, 0UL ); /* zero account_storage_entries */
136 0 : enc->state = STATE_BANK_HASH_INFO;
137 0 : break;
138 0 : }
139 0 : case STATE_ACCOUNT_STORAGE_ENTRY: { FD_LOG_ERR(( "TODO")); }
140 0 : case STATE_BANK_HASH_INFO: {
141 : /* AccountsDbFields */
142 0 : PUSH_VAL( ulong, 0UL ); /* write_version, unused */
143 0 : PUSH_VAL( ulong, bank->f.slot ); /* slot */
144 0 : PUSH_VAL( fd_hash_t, (fd_hash_t){0} ); /* unused_accounts_delta_hash */
145 0 : PUSH_VAL( fd_hash_t, (fd_hash_t){0} ); /* unused_accounts_hash */
146 0 : PUSH_VAL( ulong, 0UL ); /* num_updated_accounts, unused */
147 0 : PUSH_VAL( ulong, 0UL ); /* num_removed_accounts, unused */
148 0 : PUSH_VAL( ulong, 0UL ); /* num_lamports_stored, unused */
149 0 : PUSH_VAL( ulong, 0UL ); /* total_data_len, unused */
150 0 : PUSH_VAL( ulong, 0UL ); /* num_executable_accounts, unused */
151 0 : PUSH_VAL( ulong, 0UL ); /* historical_roots, unused */
152 0 : PUSH_VAL( ulong, 0UL ); /* historical_roots_with_hash, unused */
153 : /* ExtraFieldsToSerialize */
154 0 : PUSH_VAL( ulong, bank->f.rbh_lamports_per_sig );
155 0 : PUSH_VAL( uchar, 0 ); /* unused_incremental_snapshot_persistence */
156 0 : PUSH_VAL( uchar, 0 ); /* unused_epoch_accounts_hash */
157 :
158 0 : ulong epoch = bank->f.epoch;
159 0 : ulong epoch_cnt = fd_ulong_min( epoch, 3UL ) + 2UL; /* keys max(E-3,0)..E+1, as agave retains */
160 0 : PUSH_VAL( ulong, epoch_cnt );
161 0 : enc->epoch_cnt = (uchar)epoch_cnt;
162 0 : enc->epoch_idx = 0;
163 0 : enc->state = STATE_EPOCH_STAKES;
164 0 : break;
165 0 : }
166 0 : case STATE_EPOCH_STAKES: {
167 0 : ulong epoch_key = epoch_stakes_key ( bank, enc->epoch_idx );
168 0 : int iter_kind = epoch_stakes_iter_kind( bank, enc->epoch_idx );
169 0 : fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
170 0 : ulong fork_id = bank->vote_stakes_fork_id;
171 :
172 0 : uint vote_cnt = 0U;
173 0 : for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_iter_init( vote_stakes, fork_id, iter_kind, enc->vote_stakes_iter_mem );
174 0 : !fd_vote_stakes_iter_done( vote_stakes, fork_id, iter_kind, iter );
175 0 : fd_vote_stakes_iter_next( vote_stakes, fork_id, iter_kind, iter ) ) vote_cnt++;
176 0 : fd_vote_stakes_iter_init( vote_stakes, fork_id, iter_kind, enc->vote_stakes_iter_mem );
177 0 : enc->vote_cnt = vote_cnt;
178 0 : enc->vote_idx = 0;
179 0 : enc->total_stake = 0UL;
180 :
181 0 : PUSH_VAL( ulong, epoch_key );
182 0 : PUSH_VAL( uint, 0U ); /* variant = 0 (VersionedEpochStakes::Current) */
183 0 : PUSH_VAL( ulong, (ulong)vote_cnt );
184 :
185 0 : enc->state = (vote_cnt > 0U) ? STATE_EPOCH_STAKES_STAKES : STATE_EPOCH_STAKES_EPOCH;
186 0 : break;
187 0 : }
188 0 : case STATE_EPOCH_STAKES_STAKES: {
189 0 : int iter_kind = epoch_stakes_iter_kind( bank, enc->epoch_idx );
190 :
191 0 : ulong ec_cnt = 0UL;
192 0 : fd_epoch_credits_t const * ec = NULL;
193 0 : ulong ag_marker_idx = ULONG_MAX;
194 :
195 0 : fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
196 0 : ulong fork_id = bank->vote_stakes_fork_id;
197 :
198 0 : fd_vote_stakes_iter_t * iter = fd_type_pun( enc->vote_stakes_iter_mem );
199 0 : FD_TEST( !fd_vote_stakes_iter_done( vote_stakes, fork_id, iter_kind, iter ) );
200 0 : fd_vote_stakes_ele_t ele[1];
201 0 : fd_vote_stakes_iter_ele( vote_stakes, fork_id, iter_kind, iter, ele );
202 :
203 0 : if( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) {
204 0 : ec = find_epoch_credits( enc->bank, &ele->pubkey );
205 0 : FD_TEST( ec );
206 0 : ec_cnt = ec->cnt;
207 0 : if( FD_UNLIKELY( ec->has_ag_migration_marker ) ) {
208 0 : ulong ag_migration_slot = bank->f.alpenglow_migration_slot;
209 0 : FD_TEST( ag_migration_slot!=ULONG_MAX );
210 0 : ulong ag_migration_epoch = fd_slot_to_epoch( &bank->f.epoch_schedule, ag_migration_slot, NULL );
211 0 : ag_marker_idx = fd_epoch_credits_ag_marker_idx( ec, ag_migration_epoch );
212 0 : ec_cnt++;
213 0 : }
214 0 : }
215 :
216 : /* SIMD-0232 collectors. t-3..t-5 collectors are never consulted on
217 : reload; encoded as zero. */
218 0 : fd_pubkey_t inflation_collector = {0};
219 0 : fd_pubkey_t block_collector = {0};
220 0 : if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 || iter_kind==FD_VOTE_STAKES_ITER_T_2 ) ) {
221 0 : inflation_collector = ele->inflation_collector;
222 0 : block_collector = ele->block_collector;
223 0 : }
224 :
225 : /* A zeroed key means no BLS key is registered (serialized as None) */
226 0 : static uchar const no_bls_key[ FD_BLS_PUB_COMPRESSED_SZ ] = {0};
227 0 : int has_bls = !fd_memeq( ele->bls_key, no_bls_key, FD_BLS_PUB_COMPRESSED_SZ );
228 :
229 0 : enc->total_stake += ele->stake;
230 :
231 0 : FD_TEST( ec_cnt<=FD_EPOCH_CREDITS_MAX );
232 :
233 0 : ulong data_length = 186UL + fd_ulong_if( !!has_bls, FD_BLS_PUB_COMPRESSED_SZ, 0UL ) + 24UL * ec_cnt;
234 :
235 : /* Vote account key + stake */
236 0 : PUSH_VAL( fd_pubkey_t, ele->pubkey );
237 0 : PUSH_VAL( ulong, ele->stake );
238 :
239 : /* AccountSharedData: lamports, data_length */
240 0 : PUSH_VAL( ulong, 0UL );
241 0 : PUSH_VAL( ulong, data_length );
242 :
243 : /* VoteStateV4 */
244 0 : PUSH_VAL( uint, 3U ); /* variant = V4 */
245 0 : PUSH_VAL( fd_pubkey_t, ele->node_account ); /* node_pubkey */
246 0 : PUSH_VAL( fd_pubkey_t, (fd_pubkey_t){0} ); /* authorized_withdrawer */
247 0 : PUSH_VAL( fd_pubkey_t, inflation_collector ); /* inflation_rewards_collector */
248 0 : PUSH_VAL( fd_pubkey_t, block_collector ); /* block_revenue_collector */
249 0 : PUSH_VAL( ushort, ele->commission ); /* inflation_rewards_commission_bps */
250 0 : PUSH_VAL( ushort, ele->block_revenue_commission_bps ); /* block_revenue_commission_bps */
251 0 : PUSH_VAL( ulong, ele->pending_delegator_rewards ); /* pending_delegator_rewards */
252 0 : if( has_bls ) {
253 0 : typedef struct { uchar b[ FD_BLS_PUB_COMPRESSED_SZ ]; } bls_key_compressed_t;
254 0 : PUSH_VAL( uchar, 1 ); /* bls_pubkey_compressed = Some */
255 0 : PUSH_VAL( bls_key_compressed_t, *(bls_key_compressed_t const *)ele->bls_key );
256 0 : } else {
257 0 : PUSH_VAL( uchar, 0 ); /* bls_pubkey_compressed = None */
258 0 : }
259 0 : PUSH_VAL( ulong, 0UL ); /* votes_length = 0 */
260 0 : PUSH_VAL( uchar, 0 ); /* root_slot = None */
261 0 : PUSH_VAL( ulong, 0UL ); /* authorized_voters_length = 0 */
262 :
263 : /* Epoch credits */
264 0 : PUSH_VAL( ulong, ec_cnt );
265 0 : for( ulong k=0UL, j=0UL; k<ec_cnt; k++ ) {
266 0 : if( FD_UNLIKELY( k==ag_marker_idx ) ) {
267 0 : PUSH_VAL( ulong, ULONG_MAX );
268 0 : PUSH_VAL( ulong, ULONG_MAX );
269 0 : PUSH_VAL( ulong, ULONG_MAX );
270 0 : continue;
271 0 : }
272 0 : PUSH_VAL( ulong, (ulong)ec->epoch[j] );
273 0 : PUSH_VAL( ulong, ec->base_credits + (ulong)ec->credits_delta[j] );
274 0 : PUSH_VAL( ulong, ec->base_credits + (ulong)ec->prev_credits_delta[j] );
275 0 : j++;
276 0 : }
277 :
278 0 : PUSH_VAL( ulong, 0UL ); /* last_timestamp_slot */
279 0 : PUSH_VAL( ulong, 0UL ); /* last_timestamp_timestamp */
280 :
281 : /* AccountSharedData trailer */
282 0 : PUSH_VAL( fd_pubkey_t, fd_solana_vote_program_id ); /* owner */
283 0 : PUSH_VAL( uchar, 1 ); /* executable */
284 0 : PUSH_VAL( ulong, 0UL ); /* rent_epoch */
285 :
286 0 : enc->vote_idx++;
287 0 : fd_vote_stakes_iter_next( vote_stakes, fork_id, iter_kind, iter );
288 0 : if( enc->vote_idx >= enc->vote_cnt ) enc->state = STATE_EPOCH_STAKES_EPOCH;
289 0 : break;
290 0 : }
291 0 : case STATE_EPOCH_STAKES_EPOCH: {
292 0 : ulong epoch_key = epoch_stakes_key( bank, enc->epoch_idx );
293 :
294 0 : PUSH_VAL( ulong, 0UL ); /* stake_delegations_length = 0 */
295 0 : PUSH_VAL( ulong, 0UL ); /* unused */
296 0 : PUSH_VAL( ulong, epoch_key ); /* epoch */
297 0 : PUSH_VAL( ulong, 0UL ); /* stake_history_length = 0 */
298 0 : enc->state = STATE_EPOCH_TOTAL_STAKE;
299 0 : break;
300 0 : }
301 0 : case STATE_EPOCH_STAKE_HISTORY: { __builtin_unreachable(); }
302 0 : case STATE_EPOCH_TOTAL_STAKE: {
303 0 : int iter_kind = epoch_stakes_iter_kind( bank, enc->epoch_idx );
304 0 : ulong total_stake = (iter_kind==FD_VOTE_STAKES_ITER_T_1) ? bank->f.total_epoch_stake : enc->total_stake;
305 0 : PUSH_VAL( ulong, total_stake );
306 0 : enc->state = STATE_NODE_VOTE_ACCOUNTS;
307 0 : break;
308 0 : }
309 0 : case STATE_NODE_VOTE_ACCOUNTS: {
310 : /* node_id_to_vote_accounts: HashMap<node, (Vec<vote>, total_stake)> */
311 0 : fd_ssmanifest_epoch_map_t const * map = &enc->epoch_map[ enc->epoch_idx ];
312 0 : PUSH_VAL( ulong, map->node_cnt );
313 0 : for( ulong i=0UL; i<map->vote_cnt; ) {
314 0 : fd_pubkey_t const * node = &map->vote[ i ].node;
315 0 : ulong j = i;
316 0 : ulong total_stake = 0UL;
317 0 : for( ; j<map->vote_cnt && fd_memeq( map->vote[ j ].node.uc, node->uc, sizeof(fd_pubkey_t) ); j++ ) {
318 0 : total_stake += map->vote[ j ].stake;
319 0 : }
320 0 : PUSH_VAL( fd_pubkey_t, *node );
321 0 : PUSH_VAL( ulong, j-i );
322 0 : for( ulong k=i; k<j; k++ ) {
323 0 : PUSH_VAL( fd_pubkey_t, map->vote[ k ].vote );
324 0 : }
325 0 : PUSH_VAL( ulong, total_stake );
326 0 : i = j;
327 0 : }
328 0 : enc->state = STATE_AUTH_VOTER;
329 0 : break;
330 0 : }
331 0 : case STATE_AUTH_VOTER: {
332 : /* epoch_authorized_voters: HashMap<vote, voter> */
333 0 : fd_ssmanifest_epoch_map_t const * map = &enc->epoch_map[ enc->epoch_idx ];
334 0 : PUSH_VAL( ulong, map->vote_cnt );
335 0 : for( ulong i=0UL; i<map->vote_cnt; i++ ) {
336 0 : PUSH_VAL( fd_pubkey_t, map->vote[ i ].vote );
337 0 : PUSH_VAL( fd_pubkey_t, map->vote[ i ].voter );
338 0 : }
339 0 : enc->epoch_idx++;
340 0 : enc->state = (enc->epoch_idx < enc->epoch_cnt) ? STATE_EPOCH_STAKES : STATE_LTHASH;
341 0 : break;
342 0 : }
343 0 : case STATE_LTHASH: {
344 0 : PUSH_VAL( uchar, 1 );
345 0 : PUSH_VAL( fd_lthash_value_t, bank->f.lthash );
346 0 : enc->state = STATE_BLOCK_ID;
347 0 : break;
348 0 : }
349 0 : case STATE_BLOCK_ID: {
350 0 : int has_block_id = !fd_hash_check_zero( &bank->f.block_id );
351 0 : PUSH_VAL( uchar, (uchar)has_block_id );
352 0 : if( has_block_id ) PUSH_VAL( fd_hash_t, bank->f.block_id );
353 0 : enc->state = STATE_DONE;
354 0 : break;
355 0 : }
356 0 : case STATE_DONE:
357 0 : enc->state = STATE_INIT; /* ready for the next pass */
358 0 : return 0UL;
359 0 : default:
360 0 : FD_LOG_CRIT(( "invalid state reached (%u)", enc->state ));
361 0 : }
362 :
363 0 : return RET_EXPR;
364 0 : }
365 :
366 : #undef PREP
367 : #undef ENCODE_FN
368 : #undef PUSH_VAL
369 : #undef RET_EXPR
|