Line data Source code
1 : #include "fd_ssparse.h"
2 :
3 : #include "../../../util/log/fd_log.h"
4 : #include "../../../util/archive/fd_tar.h"
5 : #include "../../../flamenco/runtime/fd_runtime_const.h"
6 : #include "../../../flamenco/runtime/fd_system_ids.h"
7 :
8 : #include <stdio.h>
9 :
10 0 : #define FD_SSPARSE_STATE_TAR_HEADER (0)
11 0 : #define FD_SSPARSE_STATE_SCROLL_TAR_HEADER (1)
12 0 : #define FD_SSPARSE_STATE_VERSION (2)
13 0 : #define FD_SSPARSE_STATE_MANIFEST (3)
14 0 : #define FD_SSPARSE_STATE_ACCOUNT_HEADER (4)
15 0 : #define FD_SSPARSE_STATE_ACCOUNT_DATA (5)
16 0 : #define FD_SSPARSE_STATE_ACCOUNT_PADDING (6)
17 0 : #define FD_SSPARSE_STATE_STATUS_CACHE (7)
18 0 : #define FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE (8)
19 : #define FD_SSPARSE_STATE_ACCOUNT_BATCH (9)
20 :
21 : struct fd_ssparse_private {
22 : int state;
23 : uint batch_enabled : 1;
24 :
25 : struct {
26 : int seen_zero_tar_frame;
27 : int seen_manifest;
28 : int seen_status_cache;
29 : int seen_version;
30 : } flags;
31 :
32 : uchar version[ 5UL ];
33 :
34 : struct {
35 : acc_vec_map_t * acc_vec_map;
36 : acc_vec_t * acc_vec_pool;
37 : } manifest;
38 :
39 : struct {
40 : uchar header[ 512UL ];
41 : ulong file_bytes;
42 : ulong file_bytes_consumed;
43 : ulong header_bytes_consumed;
44 : } tar;
45 :
46 : struct {
47 : uchar const * owner;
48 : uchar header[ 136UL ];
49 : ulong header_bytes_consumed;
50 : ulong data_bytes_consumed;
51 : ulong data_len;
52 : } account;
53 :
54 : ulong acc_vec_bytes;
55 : ulong slot;
56 : ulong bytes_consumed;
57 :
58 : ulong seed;
59 : ulong max_acc_vecs;
60 : ulong magic;
61 : };
62 :
63 : FD_FN_CONST ulong
64 0 : fd_ssparse_align( void ) {
65 0 : return fd_ulong_max( alignof(fd_ssparse_t), fd_ulong_max( acc_vec_pool_align(), acc_vec_map_align() ) );
66 0 : }
67 :
68 : FD_FN_CONST ulong
69 0 : fd_ssparse_footprint( ulong max_acc_vecs ) {
70 0 : ulong l = FD_LAYOUT_INIT;
71 0 : l = FD_LAYOUT_APPEND( l, fd_ssparse_align(), sizeof(fd_ssparse_t) );
72 0 : l = FD_LAYOUT_APPEND( l, acc_vec_pool_align(), acc_vec_pool_footprint( max_acc_vecs ) );
73 0 : l = FD_LAYOUT_APPEND( l, acc_vec_map_align(), acc_vec_map_footprint( max_acc_vecs ) );
74 0 : return FD_LAYOUT_FINI( l, fd_ssparse_align() );
75 0 : }
76 :
77 : void *
78 : fd_ssparse_new( void * shmem,
79 : ulong max_acc_vecs,
80 0 : ulong seed ) {
81 0 : if( FD_UNLIKELY( !shmem ) ) {
82 0 : FD_LOG_WARNING(( "NULL shmem" ));
83 0 : return NULL;
84 0 : }
85 :
86 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_ssparse_align() ) ) ) {
87 0 : FD_LOG_WARNING(( "unaligned shmem" ));
88 0 : return NULL;
89 0 : }
90 :
91 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
92 0 : fd_ssparse_t * ssparse = FD_SCRATCH_ALLOC_APPEND( l, fd_ssparse_align(), sizeof(fd_ssparse_t) );
93 0 : void * _acc_vec_pool = FD_SCRATCH_ALLOC_APPEND( l, acc_vec_pool_align(), acc_vec_pool_footprint( max_acc_vecs ) );
94 0 : void * _acc_vec_map = FD_SCRATCH_ALLOC_APPEND( l, acc_vec_map_align(), acc_vec_map_footprint( max_acc_vecs ) );
95 :
96 0 : ssparse->manifest.acc_vec_pool = acc_vec_pool_join( acc_vec_pool_new( _acc_vec_pool, max_acc_vecs ) );
97 0 : FD_TEST( ssparse->manifest.acc_vec_pool );
98 :
99 0 : ssparse->manifest.acc_vec_map = acc_vec_map_join( acc_vec_map_new( _acc_vec_map, max_acc_vecs, seed ) );
100 0 : FD_TEST( ssparse->manifest.acc_vec_map );
101 :
102 0 : ssparse->state = FD_SSPARSE_STATE_TAR_HEADER;
103 0 : fd_memset( &ssparse->flags, 0, sizeof(ssparse->flags) );
104 :
105 0 : ssparse->bytes_consumed = 0UL;
106 0 : ssparse->seed = seed;
107 0 : ssparse->max_acc_vecs = max_acc_vecs;
108 :
109 0 : ssparse->tar.header_bytes_consumed = 0UL;
110 0 : ssparse->tar.file_bytes_consumed = 0UL;
111 0 : ssparse->tar.file_bytes = 0UL;
112 :
113 0 : ssparse->account.owner = NULL;
114 0 : ssparse->account.header_bytes_consumed = 0UL;
115 0 : ssparse->account.data_bytes_consumed = 0UL;
116 0 : ssparse->account.data_len = 0UL;
117 0 : ssparse->acc_vec_bytes = 0UL;
118 :
119 0 : FD_COMPILER_MFENCE();
120 0 : ssparse->magic = FD_SSPARSE_MAGIC;
121 0 : FD_COMPILER_MFENCE();
122 :
123 0 : return (void *)ssparse;
124 0 : }
125 :
126 : fd_ssparse_t *
127 0 : fd_ssparse_join( void * shssparse ) {
128 0 : if( FD_UNLIKELY( !shssparse ) ) {
129 0 : FD_LOG_WARNING(( "NULL shssparse" ));
130 0 : return NULL;
131 0 : }
132 :
133 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shssparse, fd_ssparse_align() ) ) ) {
134 0 : FD_LOG_WARNING(( "misaligned shssparse" ));
135 0 : return NULL;
136 0 : }
137 :
138 0 : fd_ssparse_t * ssparse = (fd_ssparse_t *)shssparse;
139 :
140 0 : if( FD_UNLIKELY( ssparse->magic!=FD_SSPARSE_MAGIC ) ) {
141 0 : FD_LOG_WARNING(( "bad magic" ));
142 0 : return NULL;
143 0 : }
144 :
145 0 : return ssparse;
146 0 : }
147 :
148 : void
149 0 : fd_ssparse_reset( fd_ssparse_t * ssparse ) {
150 0 : ssparse->state = FD_SSPARSE_STATE_TAR_HEADER;
151 0 : fd_memset( &ssparse->flags, 0, sizeof(ssparse->flags) );
152 0 : ssparse->bytes_consumed = 0UL;
153 :
154 0 : ssparse->tar.header_bytes_consumed = 0UL;
155 0 : ssparse->tar.file_bytes_consumed = 0UL;
156 0 : ssparse->tar.file_bytes = 0UL;
157 :
158 0 : ssparse->account.owner = NULL;
159 0 : ssparse->account.header_bytes_consumed = 0UL;
160 0 : ssparse->account.data_bytes_consumed = 0UL;
161 0 : ssparse->account.data_len = 0UL;
162 0 : ssparse->acc_vec_bytes = 0UL;
163 :
164 0 : acc_vec_map_reset( ssparse->manifest.acc_vec_map );
165 0 : acc_vec_pool_reset( ssparse->manifest.acc_vec_pool );
166 0 : }
167 :
168 : static int
169 : advance_tar( fd_ssparse_t * ssparse,
170 : uchar const * data,
171 : ulong data_sz,
172 0 : fd_ssparse_advance_result_t * result ) {
173 0 : ulong consume = fd_ulong_min( data_sz, 512UL - ssparse->tar.header_bytes_consumed );
174 0 : if( FD_LIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
175 :
176 0 : fd_memcpy( ssparse->tar.header+ssparse->tar.header_bytes_consumed, data, consume );
177 0 : ssparse->bytes_consumed += consume;
178 0 : result->bytes_consumed = consume;
179 0 : ssparse->tar.header_bytes_consumed += consume;
180 :
181 0 : if( FD_UNLIKELY( ssparse->tar.header_bytes_consumed<512UL ) ) return FD_SSPARSE_ADVANCE_AGAIN;
182 :
183 0 : fd_tar_meta_t const * hdr = (fd_tar_meta_t const *)ssparse->tar.header;
184 0 : ssparse->tar.header_bytes_consumed = 0UL;
185 :
186 : /* "ustar\x00" and "ustar \x00" (overlaps with version) are both
187 : valid values for magic. These are POSIX ustar and OLDGNU versions
188 : respectively. */
189 0 : if( FD_UNLIKELY( memcmp( hdr->magic, FD_TAR_MAGIC, 5UL ) ) ) {
190 0 : int not_zero = 0;
191 0 : for( ulong i=0UL; i<512UL; i++ ) not_zero |= ssparse->tar.header[ i ];
192 0 : if( FD_UNLIKELY( not_zero ) ) {
193 0 : FD_LOG_WARNING(( "invalid tar header magic `%s`", hdr->magic ));
194 0 : return FD_SSPARSE_ADVANCE_ERROR;
195 0 : }
196 :
197 0 : if( FD_LIKELY( ssparse->flags.seen_zero_tar_frame ) ) {
198 0 : if( FD_UNLIKELY( !ssparse->flags.seen_version || !ssparse->flags.seen_manifest || !ssparse->flags.seen_status_cache ) ) {
199 0 : FD_LOG_WARNING(( "unexpected end of file before version or manifest or status cache" ));
200 0 : return FD_SSPARSE_ADVANCE_ERROR;
201 0 : }
202 :
203 0 : return FD_SSPARSE_ADVANCE_DONE;
204 0 : }
205 :
206 0 : ssparse->flags.seen_zero_tar_frame = 1;
207 0 : return FD_SSPARSE_ADVANCE_AGAIN;
208 0 : }
209 :
210 0 : if( FD_UNLIKELY( ssparse->flags.seen_zero_tar_frame ) ) {
211 0 : FD_LOG_WARNING(( "unexpected valid tar header after zero frame" ));
212 0 : return FD_SSPARSE_ADVANCE_ERROR;
213 0 : }
214 :
215 0 : ssparse->tar.file_bytes = fd_tar_meta_get_size( hdr );
216 0 : if( FD_UNLIKELY( ssparse->tar.file_bytes==ULONG_MAX ) ) {
217 0 : FD_LOG_WARNING(( "invalid tar header size" ));
218 0 : return FD_SSPARSE_ADVANCE_ERROR;
219 0 : }
220 :
221 0 : if( FD_UNLIKELY( hdr->typeflag==FD_TAR_TYPE_DIR ) ) return FD_SSPARSE_ADVANCE_AGAIN;
222 :
223 0 : if( FD_UNLIKELY( !fd_tar_meta_is_reg( hdr ) ) ) {
224 0 : FD_LOG_WARNING(( "invalid tar header type %d", hdr->typeflag ));
225 0 : return FD_SSPARSE_ADVANCE_ERROR;
226 0 : }
227 0 : if( FD_UNLIKELY( !ssparse->tar.file_bytes ) ) {
228 0 : FD_LOG_WARNING(( "invalid tar header size %lu", ssparse->tar.file_bytes ));
229 0 : return FD_SSPARSE_ADVANCE_ERROR;
230 0 : }
231 :
232 : /* TODO: Check every header field here for validity? */
233 :
234 0 : int desired_state;
235 0 : if( FD_LIKELY( !strncmp( hdr->name, "version", 7UL ) ) ) {
236 0 : desired_state = FD_SSPARSE_STATE_VERSION;
237 0 : if( FD_UNLIKELY( ssparse->tar.file_bytes!=5UL ) ) {
238 0 : FD_LOG_WARNING(( "invalid version file size %lu", ssparse->tar.file_bytes ));
239 0 : return FD_SSPARSE_ADVANCE_ERROR;
240 0 : }
241 0 : } else if( FD_LIKELY( !strncmp( hdr->name, "accounts/", 9UL ) ) ) {
242 0 : ssparse->account.header_bytes_consumed = 0UL;
243 0 : desired_state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
244 0 : ulong id, slot;
245 0 : if( FD_UNLIKELY( sscanf( hdr->name, "accounts/%lu.%lu", &slot, &id )!=2 ) ) {
246 0 : FD_LOG_WARNING(( "invalid account append vec name %s", hdr->name ));
247 0 : return FD_SSPARSE_ADVANCE_ERROR;
248 0 : }
249 :
250 0 : acc_vec_key_t key = { .slot = slot, .id = id };
251 0 : acc_vec_t const * acc_vec = acc_vec_map_ele_query_const( ssparse->manifest.acc_vec_map, &key, NULL, ssparse->manifest.acc_vec_pool );
252 0 : if( FD_UNLIKELY( !acc_vec ) ) {
253 0 : FD_LOG_WARNING(( "append vec %lu.%lu not found in manifest", slot, id ));
254 0 : return FD_SSPARSE_ADVANCE_ERROR;
255 0 : }
256 :
257 0 : ssparse->acc_vec_bytes = acc_vec->file_sz;
258 0 : if( FD_UNLIKELY( ssparse->acc_vec_bytes>ssparse->tar.file_bytes ) ) {
259 0 : FD_LOG_WARNING(( "invalid append vec file size %lu > %lu", ssparse->acc_vec_bytes, ssparse->tar.file_bytes ));
260 0 : return FD_SSPARSE_ADVANCE_ERROR;
261 0 : }
262 :
263 0 : ssparse->slot = slot;
264 0 : } else if( FD_LIKELY( !strncmp( hdr->name, "snapshots/status_cache", 22UL ) ) ) desired_state = FD_SSPARSE_STATE_STATUS_CACHE;
265 0 : else if( FD_LIKELY( !strncmp( hdr->name, "snapshots/", 10UL ) ) ) {
266 0 : desired_state = FD_SSPARSE_STATE_MANIFEST;
267 0 : } else {
268 0 : FD_LOG_WARNING(( "unexpected tar header name %s", hdr->name ));
269 0 : return FD_SSPARSE_ADVANCE_ERROR;
270 0 : }
271 :
272 0 : ssparse->tar.file_bytes_consumed = 0UL;
273 :
274 0 : switch( desired_state ) {
275 0 : case FD_SSPARSE_STATE_VERSION:
276 0 : if( FD_UNLIKELY( ssparse->flags.seen_version ) ) {
277 0 : FD_LOG_WARNING(( "unexpected duplicate version file" ));
278 0 : return FD_SSPARSE_ADVANCE_ERROR;
279 0 : }
280 :
281 0 : ssparse->flags.seen_version = 1;
282 0 : ssparse->state = FD_SSPARSE_STATE_VERSION;
283 0 : break;
284 0 : case FD_SSPARSE_STATE_MANIFEST:
285 0 : if( FD_UNLIKELY( ssparse->flags.seen_manifest ) ) {
286 0 : FD_LOG_WARNING(( "unexpected duplicate manifest file" ));
287 0 : return FD_SSPARSE_ADVANCE_ERROR;
288 0 : }
289 :
290 0 : ssparse->flags.seen_manifest = 1;
291 0 : ssparse->state = FD_SSPARSE_STATE_MANIFEST;
292 0 : break;
293 0 : case FD_SSPARSE_STATE_ACCOUNT_HEADER:
294 0 : if( FD_UNLIKELY( !ssparse->flags.seen_manifest ) ) {
295 0 : FD_LOG_WARNING(( "unexpected account append vec file before manifest" ));
296 0 : return FD_SSPARSE_ADVANCE_ERROR;
297 0 : }
298 :
299 0 : ssparse->account.header_bytes_consumed = 0UL;
300 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
301 0 : break;
302 0 : case FD_SSPARSE_STATE_STATUS_CACHE:
303 0 : if( FD_UNLIKELY( ssparse->flags.seen_status_cache ) ) {
304 0 : FD_LOG_WARNING(( "unexpected status cache file" ));
305 0 : return FD_SSPARSE_ADVANCE_ERROR;
306 0 : }
307 :
308 0 : ssparse->flags.seen_status_cache = 1;
309 0 : ssparse->state = FD_SSPARSE_STATE_STATUS_CACHE;
310 0 : break;
311 0 : default:
312 0 : FD_LOG_ERR(( "unexpected tar header desired state %d", desired_state ));
313 0 : break;
314 0 : }
315 :
316 0 : return FD_SSPARSE_ADVANCE_AGAIN;
317 0 : }
318 :
319 : static int
320 : advance_version( fd_ssparse_t * ssparse,
321 : uchar const * data,
322 : ulong data_sz,
323 0 : fd_ssparse_advance_result_t * result ) {
324 0 : ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
325 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
326 :
327 0 : fd_memcpy( ssparse->version+ssparse->tar.file_bytes_consumed, data, consume );
328 :
329 0 : ssparse->tar.file_bytes_consumed += consume;
330 0 : ssparse->bytes_consumed += consume;
331 0 : result->bytes_consumed = consume;
332 :
333 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) return FD_SSPARSE_ADVANCE_AGAIN;
334 :
335 0 : FD_TEST( ssparse->tar.file_bytes_consumed==ssparse->tar.file_bytes );
336 0 : FD_TEST( ssparse->tar.file_bytes_consumed==5UL );
337 :
338 0 : if( FD_UNLIKELY( memcmp( ssparse->version, "1.2.0", 5UL ) ) ) {
339 0 : FD_LOG_WARNING(( "invalid version file %s", ssparse->version ));
340 0 : return FD_SSPARSE_ADVANCE_ERROR;
341 0 : }
342 :
343 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
344 0 : return FD_SSPARSE_ADVANCE_AGAIN;
345 0 : }
346 :
347 : static int
348 : advance_status_cache( fd_ssparse_t * ssparse,
349 : uchar const * data,
350 : ulong data_sz,
351 0 : fd_ssparse_advance_result_t * result ) {
352 0 : ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
353 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
354 :
355 0 : ssparse->tar.file_bytes_consumed += consume;
356 0 : ssparse->bytes_consumed += consume;
357 :
358 0 : result->bytes_consumed = consume;
359 0 : result->status_cache.data = data;
360 0 : result->status_cache.data_sz = consume;
361 :
362 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) {
363 0 : return FD_SSPARSE_ADVANCE_STATUS_CACHE;
364 0 : }
365 0 : else { /* ssparse->tar.file_bytes_consumed==ssparse->tar.file_bytes */
366 : /* finished parsing status cache */
367 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
368 0 : return FD_SSPARSE_ADVANCE_STATUS_CACHE;
369 0 : }
370 0 : }
371 :
372 : static int
373 : advance_manifest( fd_ssparse_t * ssparse,
374 : uchar const * data,
375 : ulong data_sz,
376 0 : fd_ssparse_advance_result_t * result ) {
377 0 : ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
378 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
379 :
380 0 : ssparse->tar.file_bytes_consumed += consume;
381 0 : ssparse->bytes_consumed += consume;
382 :
383 0 : result->bytes_consumed = consume;
384 0 : result->manifest.data = data;
385 0 : result->manifest.data_sz = consume;
386 0 : result->manifest.acc_vec_map = ssparse->manifest.acc_vec_map;
387 0 : result->manifest.acc_vec_pool = ssparse->manifest.acc_vec_pool;
388 :
389 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) {
390 0 : return FD_SSPARSE_ADVANCE_MANIFEST;
391 0 : }
392 0 : else { /* ssparse->tar.file_bytes_consumed==ssparse->tar.file_bytes */
393 : /* finished parsing manifest */
394 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
395 0 : return FD_SSPARSE_ADVANCE_MANIFEST;
396 0 : }
397 0 : }
398 :
399 : static int
400 : advance_next_tar( fd_ssparse_t * ssparse,
401 : uchar const * data,
402 : ulong data_sz,
403 0 : fd_ssparse_advance_result_t * result ) {
404 0 : (void)data;
405 : /* skip padding */
406 0 : ulong bytes_remaining = fd_ulong_align_up( ssparse->bytes_consumed, 512UL ) - ssparse->bytes_consumed;
407 0 : ulong pad_sz = bytes_remaining;
408 0 : pad_sz = fd_ulong_min( pad_sz, data_sz );
409 0 : if( FD_UNLIKELY( !pad_sz && bytes_remaining ) ) return FD_SSPARSE_ADVANCE_ERROR;
410 :
411 0 : ssparse->bytes_consumed += pad_sz;
412 0 : result->bytes_consumed = pad_sz;
413 0 : bytes_remaining -= pad_sz;
414 :
415 0 : if( FD_LIKELY( !bytes_remaining ) ) ssparse->state = FD_SSPARSE_STATE_TAR_HEADER;
416 0 : return FD_SSPARSE_ADVANCE_AGAIN;
417 0 : }
418 :
419 : static int
420 : advance_account_batch( fd_ssparse_t * ssparse,
421 : uchar const * data,
422 : ulong data_sz,
423 0 : fd_ssparse_advance_result_t * result ) {
424 : /* Cannot create a batch unless the parser is aligned to an account. */
425 0 : if( FD_UNLIKELY( ssparse->account.header_bytes_consumed ) ) return FD_SSPARSE_ADVANCE_AGAIN;
426 :
427 : /* Each account is at least 136 bytes large. Don't attempt to create
428 : a batch unless at least 4 accounts fit. */
429 0 : ulong avail = fd_ulong_min( data_sz, ssparse->acc_vec_bytes - ssparse->tar.file_bytes_consumed );
430 0 : if( FD_UNLIKELY( avail<(4*136UL) ) ) return FD_SSPARSE_ADVANCE_AGAIN;
431 :
432 : /* Skip over accounts until we reached EOF or batch is full */
433 0 : result->account_batch.batch_cnt = 0;
434 0 : ulong off = 0UL;
435 0 : for( ulong idx=0UL; idx<FD_SSPARSE_ACC_BATCH_MAX && off+136UL<=avail; idx++ ) {
436 0 : uchar const * acc_hdr = (uchar *)data+off;
437 :
438 : /* We want ConfigProgram accounts to go through the slow path,
439 : since they are published from there to consumers for monitoring. */
440 0 : if( FD_UNLIKELY( !memcmp( acc_hdr+64UL, fd_solana_config_program_id.key, sizeof(fd_hash_t) ) ) ) {
441 0 : if( FD_UNLIKELY( idx==0UL ) ) return FD_SSPARSE_ADVANCE_AGAIN; /* At the front of the batch, abort */
442 0 : else break; /* otherwise, break early. */
443 0 : }
444 :
445 0 : ulong acc_data_sz = fd_ulong_load_8_fast( acc_hdr+8 );
446 0 : ulong next_off = off+136UL+acc_data_sz;
447 0 : ulong pad_sz = fd_ulong_align_up( ssparse->tar.file_bytes_consumed+next_off, 8UL ) -
448 0 : ( ssparse->tar.file_bytes_consumed+next_off );
449 0 : next_off += pad_sz;
450 0 : if( FD_UNLIKELY( next_off>avail ) ) break; /* account is fragmented */
451 0 : if( FD_UNLIKELY( acc_data_sz > (24UL<<20) ) ) {
452 0 : FD_LOG_ERR(( "invalid account data size %lu", acc_data_sz ));
453 0 : }
454 0 : result->account_batch.batch_cnt = idx+1UL;
455 0 : result->account_batch.batch[ idx ] = acc_hdr;
456 0 : ssparse->account.header_bytes_consumed = 136UL;
457 0 : ssparse->account.data_bytes_consumed = acc_data_sz;
458 0 : ssparse->account.data_len = acc_data_sz;
459 0 : off = next_off;
460 0 : }
461 :
462 : /* Not worth batching if current chunk contains too few accounts. */
463 0 : if( FD_UNLIKELY( result->account_batch.batch_cnt!=FD_SSPARSE_ACC_BATCH_MAX ) ) {
464 0 : return FD_SSPARSE_ADVANCE_AGAIN;
465 0 : }
466 :
467 0 : ssparse->tar.file_bytes_consumed += off;
468 0 : ssparse->bytes_consumed += off;
469 0 : result->bytes_consumed = off;
470 :
471 : /* reset state */
472 :
473 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_PADDING;
474 :
475 0 : result->account_batch.slot = ssparse->slot;
476 :
477 0 : return FD_SSPARSE_ADVANCE_ACCOUNT_BATCH;
478 0 : }
479 :
480 : static int
481 : advance_account_header( fd_ssparse_t * ssparse,
482 : uchar const * data,
483 : ulong data_sz,
484 0 : fd_ssparse_advance_result_t * result ) {
485 0 : ulong consume = fd_ulong_min( 136UL-ssparse->account.header_bytes_consumed, fd_ulong_min( data_sz, ssparse->acc_vec_bytes-ssparse->tar.file_bytes_consumed ) );
486 :
487 0 : if( FD_UNLIKELY( !consume ) ) {
488 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed==ssparse->acc_vec_bytes ) ) {
489 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE;
490 0 : return FD_SSPARSE_ADVANCE_AGAIN;
491 0 : } else {
492 0 : return FD_SSPARSE_ADVANCE_ERROR;
493 0 : }
494 0 : }
495 :
496 0 : if( FD_UNLIKELY( consume<136UL ) ) {
497 0 : fd_memcpy( ssparse->account.header+ssparse->account.header_bytes_consumed, data, consume );
498 0 : } else if( ssparse->batch_enabled ) {
499 : /* fast path */
500 0 : int res = advance_account_batch( ssparse, data, data_sz, result );
501 0 : if( res==FD_SSPARSE_ADVANCE_ACCOUNT_BATCH ) return res;
502 : /* fall through and continue processing account header */
503 0 : }
504 :
505 0 : ssparse->account.header_bytes_consumed += consume;
506 0 : ssparse->tar.file_bytes_consumed += consume;
507 0 : ssparse->bytes_consumed += consume;
508 0 : result->bytes_consumed = consume;
509 :
510 0 : if( FD_UNLIKELY( ssparse->account.header_bytes_consumed<136UL ) ) return FD_SSPARSE_ADVANCE_AGAIN;
511 :
512 0 : uchar const * hdr = ssparse->account.header;
513 0 : if( FD_LIKELY( consume==136UL ) ) hdr = data;
514 :
515 0 : result->account_header.data_len = fd_ulong_load_8_fast( hdr+8UL );
516 0 : if( FD_UNLIKELY( result->account_header.data_len>FD_RUNTIME_ACC_SZ_MAX ) ) {
517 0 : FD_LOG_WARNING(( "invalid account header data length %lu", result->account_header.data_len ));
518 0 : return FD_SSPARSE_ADVANCE_ERROR;
519 0 : }
520 :
521 0 : result->account_header.pubkey = hdr+16UL;
522 0 : result->account_header.lamports = fd_ulong_load_8_fast( hdr+48UL );
523 0 : result->account_header.rent_epoch = fd_ulong_load_8_fast( hdr+56UL );
524 0 : result->account_header.owner = hdr+64UL;
525 0 : result->account_header.executable = hdr[ 96UL ];
526 0 : if( FD_UNLIKELY( result->account_header.executable>1 ) ) {
527 0 : char pubkey_str[ FD_BASE58_ENCODED_32_SZ ];
528 0 : fd_base58_encode_32( result->account_header.pubkey, NULL, pubkey_str );
529 0 : FD_LOG_WARNING(( "invalid account header executable %d for account %s", result->account_header.executable, pubkey_str ));
530 0 : return FD_SSPARSE_ADVANCE_ERROR;
531 0 : }
532 0 : result->account_header.hash = hdr+104UL;
533 0 : result->account_header.slot = ssparse->slot;
534 :
535 0 : ssparse->account.owner = hdr+64UL;
536 0 : ssparse->account.data_len = result->account_header.data_len;
537 0 : ssparse->account.data_bytes_consumed = 0UL;
538 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_DATA;
539 :
540 0 : return FD_SSPARSE_ADVANCE_ACCOUNT_HEADER;
541 0 : }
542 :
543 : static int
544 : advance_account_data( fd_ssparse_t * ssparse,
545 : uchar const * data,
546 : ulong data_sz,
547 0 : fd_ssparse_advance_result_t * result ) {
548 0 : if( FD_UNLIKELY( ssparse->account.data_bytes_consumed==ssparse->account.data_len ) ) {
549 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_PADDING;
550 0 : return FD_SSPARSE_ADVANCE_AGAIN;
551 0 : }
552 :
553 0 : ulong consume = fd_ulong_min( data_sz, ssparse->acc_vec_bytes-ssparse->tar.file_bytes_consumed );
554 0 : if( FD_UNLIKELY( !consume ) ) {
555 0 : FD_LOG_WARNING(( "account data extends beyond append vec size" ));
556 0 : return FD_SSPARSE_ADVANCE_ERROR;
557 0 : }
558 :
559 0 : consume = fd_ulong_min( consume, ssparse->account.data_len-ssparse->account.data_bytes_consumed );
560 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
561 :
562 0 : ssparse->tar.file_bytes_consumed += consume;
563 0 : ssparse->bytes_consumed += consume;
564 0 : ssparse->account.data_bytes_consumed += consume;
565 0 : result->bytes_consumed = consume;
566 :
567 0 : result->account_data.owner = ssparse->account.owner;
568 0 : result->account_data.data_sz = consume;
569 0 : result->account_data.data = data;
570 :
571 0 : FD_TEST( ssparse->account.data_bytes_consumed<=ssparse->account.data_len );
572 0 : if( FD_LIKELY( ssparse->account.data_bytes_consumed==ssparse->account.data_len ) ) {
573 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_PADDING;
574 0 : }
575 :
576 0 : return FD_SSPARSE_ADVANCE_ACCOUNT_DATA;
577 0 : }
578 :
579 : static int
580 : advance_account_padding( fd_ssparse_t * ssparse,
581 : uchar const * data,
582 : ulong data_sz,
583 0 : fd_ssparse_advance_result_t * result ) {
584 0 : (void)data;
585 :
586 0 : ulong pad_sz = fd_ulong_align_up( ssparse->tar.file_bytes_consumed, 8UL ) - ssparse->tar.file_bytes_consumed;
587 0 : pad_sz = fd_ulong_min( pad_sz, ssparse->acc_vec_bytes - ssparse->tar.file_bytes_consumed );
588 0 : if( FD_UNLIKELY( !pad_sz ) ) {
589 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed==ssparse->acc_vec_bytes ) ) ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
590 0 : else ssparse->state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
591 :
592 0 : ssparse->account.header_bytes_consumed = 0UL;
593 0 : return FD_SSPARSE_ADVANCE_AGAIN;
594 0 : }
595 :
596 0 : ulong consume = fd_ulong_min( data_sz, pad_sz );
597 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
598 :
599 0 : ssparse->tar.file_bytes_consumed += consume;
600 0 : ssparse->bytes_consumed += consume;
601 0 : result->bytes_consumed = consume;
602 :
603 0 : ulong remaining = fd_ulong_align_up( ssparse->tar.file_bytes_consumed, 8UL ) - ssparse->tar.file_bytes_consumed;
604 0 : if( FD_LIKELY( !remaining ) ) {
605 0 : ssparse->account.header_bytes_consumed = 0UL;
606 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
607 0 : }
608 0 : return FD_SSPARSE_ADVANCE_AGAIN;
609 0 : }
610 :
611 : static int
612 : advance_account_garbage( fd_ssparse_t * ssparse,
613 : uchar const * data,
614 : ulong data_sz,
615 0 : fd_ssparse_advance_result_t * result ) {
616 0 : (void)data;
617 0 : ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
618 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
619 :
620 0 : ssparse->tar.file_bytes_consumed += consume;
621 0 : ssparse->bytes_consumed += consume;
622 0 : result->bytes_consumed = consume;
623 :
624 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) return FD_SSPARSE_ADVANCE_AGAIN;
625 :
626 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
627 0 : return FD_SSPARSE_ADVANCE_AGAIN;
628 0 : }
629 :
630 : int
631 : fd_ssparse_advance( fd_ssparse_t * ssparse,
632 : uchar const * data,
633 : ulong data_sz,
634 0 : fd_ssparse_advance_result_t * result ) {
635 0 : result->bytes_consumed = 0UL;
636 :
637 0 : switch( ssparse->state ) {
638 0 : case FD_SSPARSE_STATE_TAR_HEADER: return advance_tar( ssparse, data, data_sz, result );
639 0 : case FD_SSPARSE_STATE_SCROLL_TAR_HEADER: return advance_next_tar( ssparse, data, data_sz, result );
640 0 : case FD_SSPARSE_STATE_VERSION: return advance_version( ssparse, data, data_sz, result );
641 0 : case FD_SSPARSE_STATE_MANIFEST: return advance_manifest( ssparse, data, data_sz, result );
642 0 : case FD_SSPARSE_STATE_ACCOUNT_HEADER: return advance_account_header( ssparse, data, data_sz, result );
643 0 : case FD_SSPARSE_STATE_ACCOUNT_DATA: return advance_account_data( ssparse, data, data_sz, result );
644 0 : case FD_SSPARSE_STATE_ACCOUNT_PADDING: return advance_account_padding( ssparse, data, data_sz, result );
645 0 : case FD_SSPARSE_STATE_STATUS_CACHE: return advance_status_cache( ssparse, data, data_sz, result );
646 0 : case FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE: return advance_account_garbage( ssparse, data, data_sz, result );
647 0 : default: FD_LOG_ERR(( "invalid state %d", ssparse->state ));
648 0 : }
649 0 : }
650 :
651 : void
652 : fd_ssparse_batch_enable( fd_ssparse_t * ssparse,
653 0 : int enabled ) {
654 0 : ssparse->batch_enabled = !!enabled;
655 0 : }
656 :
657 : int
658 : fd_ssparse_populate_acc_vec_map( fd_ssparse_t * ssparse,
659 : ulong * slots,
660 : ulong * ids,
661 : ulong * file_szs,
662 0 : ulong cnt ) {
663 0 : for( ulong i=0UL; i<cnt; i++ ) {
664 0 : acc_vec_key_t key = { .slot=slots[ i ], .id=ids[ i ] };
665 0 : if( FD_UNLIKELY( acc_vec_map_ele_query( ssparse->manifest.acc_vec_map, &key, NULL, ssparse->manifest.acc_vec_pool ) ) ) return -1;
666 0 : acc_vec_t * acc_vec = acc_vec_pool_ele_acquire( ssparse->manifest.acc_vec_pool );
667 0 : acc_vec->key.id = ids[ i ];
668 0 : acc_vec->key.slot = slots[ i ];
669 0 : acc_vec->file_sz = file_szs[ i ];
670 0 : acc_vec_map_ele_insert( ssparse->manifest.acc_vec_map, acc_vec, ssparse->manifest.acc_vec_pool );
671 0 : }
672 0 : return 0;
673 0 : }
|