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