Line data Source code
1 : /* https://docs.solana.com/developing/programming-model/transactions#anatomy-of-a-transaction */ 2 : 3 : #include "fd_txn.h" 4 : #include "fd_txn_v1.h" 5 : #include "fd_compact_u16.h" 6 : 7 : ulong 8 : fd_txn_parse_core( uchar const * payload, 9 : ulong original_payload_sz, 10 : void * out_buf, 11 : fd_txn_parse_counters_t * counters_opt, 12 61334064 : ulong * payload_sz_opt ) { 13 61334064 : ulong i = 0UL; 14 : /* This code does non-trivial parsing of untrusted user input, which 15 : is a potentially dangerous thing. The main invariants we need to 16 : ensure are 17 : A) i<=payload_sz at all times 18 : B) i< payload_sz prior to reading 19 : As long as these invariants hold, it's safe to read payload[ i ]. 20 : To ensure this, we force the following discipline for all parsing 21 : steps: 22 : Step 1. Assert there are enough bytes to read the field 23 : Step 2. Read the field 24 : Step 3. Advance i 25 : Step 4. Validate the field (if there's anything to do) 26 : This code is structured highly horizontally to make it very clear 27 : that it is correct. 28 : 29 : The first 3 steps are in three columns. The variable `i` only 30 : appears in very specific locations on the line (try searching for 31 : \<i\> in VIM to see this). 32 : 33 : The CHECK_LEFT( x ) call in the first column and the i+=x in the 34 : third column always have the same argument, which ensures invariant 35 : A holds. "Prior to reading" from invariant B corresponds to the 36 : middle column, which is the only place `i` is read. Because x is 37 : positive, the CHECK_LEFT( x ) in the first column ensures invariant 38 : B holds. 39 : 40 : Unfortunately for variable length integers, we have to combine the 41 : first two columns into a call to READ_CHECKED_COMPACT_U16 that also 42 : promises not to use any out-of-bounds data. 43 : 44 : The assignments are done in chunks in as close to the same order as 45 : possible as the variables are declared in the struct, making it 46 : very clear every variable has been initialized. */ 47 : 48 : /* A temporary for storing the return value of fd_cu16_dec_sz */ 49 61334064 : ulong bytes_consumed = 0UL; 50 : 51 : /* Increment counters and return immediately if cond is false. */ 52 4109234727 : #define CHECK( cond ) do { \ 53 4109098005 : if( FD_UNLIKELY( !(cond) ) ) { \ 54 99210 : if( FD_LIKELY( counters_opt ) ) { \ 55 87627 : counters_opt->failure_ring[ ( counters_opt->failure_cnt++ )%FD_TXN_PARSE_COUNTERS_RING_SZ ] = __LINE__; \ 56 87627 : } \ 57 99210 : return 0UL; \ 58 99210 : } \ 59 4109098005 : } while( 0 ) 60 : /* CHECK that it is safe to read at least n more bytes assuming i is 61 : the current location. n is untrusted and could trigger overflow, so 62 : don't do i+n<=payload_sz */ 63 1656736290 : #define CHECK_LEFT( n ) CHECK( (n)<=(payload_sz-i) ) 64 : /* READ_CHECKED_COMPACT_U16 safely reads a compact-u16 from the 65 : indicated location in the payload. It stores the resulting value 66 : in the ushort variable called var_name. It stores the size in 67 : out_sz. */ 68 61334064 : #define READ_CHECKED_COMPACT_U16( out_sz, var_name, where ) \ 69 889460136 : do { \ 70 889460136 : ulong _where = (where); \ 71 889460136 : ulong _out_sz = fd_cu16_dec_sz( payload+_where, payload_sz-_where ); \ 72 889460136 : CHECK( _out_sz ); \ 73 889460136 : (var_name) = fd_cu16_dec_fixed( payload+_where, _out_sz ); \ 74 889449735 : (out_sz) = _out_sz; \ 75 889449735 : } while( 0 ) 76 : 77 : /* Minimal instr has 1B for program id, 1B for an acct_addr list 78 : containing no accounts, 1B for length-0 instruction data */ 79 61334064 : #define MIN_INSTR_SZ (3UL) 80 : 81 : /* Determine the transaction version. 82 : 83 : For V1 transactions, the message is moved to the front of the payload, 84 : whereas for legacy and V0 transactions the first byte is the signature 85 : count. 86 : 87 : Because for legacy and V0 transactions the signature count is <128, 88 : Agave uses the high bit to determine if the transaction is V1 or 89 : legacy/V0: 90 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/transaction-view/src/transaction_frame.rs#L194-L203 91 : 92 : High bit 1 = V1 93 : High bit 0 = Legacy/V0 */ 94 61334064 : CHECK( original_payload_sz ); ulong payload_msb = payload[ 0UL ]; 95 61334058 : int is_v1 = payload_msb&0x80; 96 : 97 : /* For V1 transactions, the payload size can be up to FD_TXN_MTU. 98 : For V0/legacy transactions, the maximum payload size is limited 99 : to FD_TXN_MTU_V0. */ 100 61334058 : ulong maximum_payload_sz = is_v1 ? FD_TXN_MTU : FD_TXN_MTU_V0; 101 61334058 : ulong payload_sz = fd_ulong_min( original_payload_sz, maximum_payload_sz ); 102 : 103 61334058 : if( is_v1 ) { 104 : /* ------------------------------ V1 transaction parser ------------------------------ */ 105 : 106 : /* Check that the version byte is correct 107 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/transaction-view/src/transaction_frame.rs#L117-L122 */ 108 18717 : CHECK_LEFT( 1UL ); uchar version_byte = payload[ i ]; i++; 109 18717 : CHECK( (version_byte&0x7FU)==FD_TXN_V1 ); 110 : 111 : /* Must have at least 1 signer (the fee payer) and no more than FD_TXN_SIG_MAX */ 112 17952 : CHECK_LEFT( 1UL ); uchar signature_cnt = payload[ i ]; i++; 113 17952 : CHECK( (1UL<=signature_cnt) & (signature_cnt<=FD_TXN_SIG_MAX) ); 114 10278 : CHECK_LEFT( 1UL ); uchar ro_signed_cnt = payload[ i ]; i++; 115 : 116 : /* Fee payer must be a writable signer */ 117 10278 : CHECK( ro_signed_cnt<signature_cnt ); 118 10275 : CHECK_LEFT( 1UL ); uchar ro_unsigned_cnt= payload[ i ]; i++; 119 : 120 : /* Check the transaction config mask is valid: 121 : - Only bits 0-4 are used 122 : - Bits 0 and 1 must either be both set or both clear 123 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/transaction-view/src/transaction_config_frame.rs#L90-L112 */ 124 10275 : CHECK_LEFT( 4UL ); uint config_mask = fd_uint_load_4( payload+i ); i+=4UL; 125 10275 : CHECK( (config_mask & ~0x1FU)==0U ); 126 10272 : CHECK( ((config_mask>>0)&1U)==((config_mask>>1)&1U) ); 127 : 128 10269 : CHECK_LEFT( FD_TXN_BLOCKHASH_SZ ); ulong recent_blockhash_off = i; i+=FD_TXN_BLOCKHASH_SZ; 129 : 130 10269 : CHECK_LEFT( 1UL ); uchar instr_cnt = payload[ i ]; i++; 131 10269 : CHECK( (ulong)instr_cnt<=FD_TXN_INSTR_MAX ); 132 : 133 : /* V1 has no ALTs, so acct_addr_cnt is the total address count in this transaction */ 134 7713 : CHECK_LEFT( 1UL ); uchar acct_addr_cnt = payload[ i ]; i++; 135 7713 : CHECK( (signature_cnt<=acct_addr_cnt) & (acct_addr_cnt<=FD_TXN_ACCT_ADDR_MAX) ); 136 6360 : CHECK( (ulong)signature_cnt+(ulong)ro_unsigned_cnt<=(ulong)acct_addr_cnt ); 137 : 138 6360 : CHECK_LEFT( FD_TXN_ACCT_ADDR_SZ*acct_addr_cnt ); ulong acct_addr_off = i ; i+=FD_TXN_ACCT_ADDR_SZ*acct_addr_cnt; 139 : 140 : /* Config values: 4 bytes per set mask bit */ 141 6360 : ulong config_values_off = i; 142 6360 : ulong num_config_values = (ulong)fd_uint_popcnt( config_mask ); 143 6360 : CHECK_LEFT( 4UL*num_config_values ); i+=4UL*num_config_values; 144 : 145 : /* If the requested heap size (bit 4) is set, it must be a multiple 146 : of 1 KiB in the range [32 KiB, 256 KiB]. 147 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/runtime-transaction/src/transaction_meta.rs#L156-L164 */ 148 6360 : if( (config_mask>>4)&1U ) { 149 24 : uint requested_heap = fd_uint_load_4( payload + config_values_off + 4UL*(num_config_values-1UL) ); 150 24 : CHECK( (requested_heap>=FD_TXN_V1_MIN_HEAP_SZ ) & 151 24 : (requested_heap<=FD_TXN_V1_MAX_HEAP_SZ ) & 152 24 : ((requested_heap%FD_TXN_V1_HEAP_GRANULARITY)==0U) ); 153 24 : } 154 : 155 6351 : fd_txn_t * parsed = (fd_txn_t *)out_buf; 156 : 157 : /* Instructions are serialized as a block of fixed-size 4-byte headers 158 : followed by a block of variable-size instruction payloads. The headers 159 : are serialized separately to the instruction payloads, so we parse them in 160 : two passes: first the headers, then the payloads. */ 161 : 162 : /* Pass 1: instruction headers (program_id_index, num_accounts, data_len) */ 163 143073 : for( ulong j=0UL; j<instr_cnt; j++ ) { 164 136734 : CHECK_LEFT( 1UL ); uchar program_id = payload[ i ]; i++; 165 : /* program_id must be a static account and can't be the fee payer 166 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/transaction-view/src/sanitize.rs#L163-L172 */ 167 136734 : CHECK( (0UL<(ulong)program_id) & ((ulong)program_id<(ulong)acct_addr_cnt) ); 168 136722 : CHECK_LEFT( 1UL ); uchar acct_cnt = payload[ i ]; i++; 169 136722 : CHECK_LEFT( 2UL ); ushort data_sz = fd_ushort_load_2( payload+i ); i+=2UL; 170 : 171 136722 : parsed->instr[ j ].program_id = program_id; 172 136722 : parsed->instr[ j ]._padding_reserved_1 = (uchar)0; 173 136722 : parsed->instr[ j ].acct_cnt = (ushort)acct_cnt; 174 136722 : parsed->instr[ j ].data_sz = data_sz; 175 136722 : } 176 : 177 : /* Pass 2: instruction payloads */ 178 143058 : for( ulong j=0UL; j<instr_cnt; j++ ) { 179 136722 : ushort acct_cnt = parsed->instr[ j ].acct_cnt; 180 136722 : ushort data_sz = parsed->instr[ j ].data_sz; 181 : 182 136722 : CHECK_LEFT( acct_cnt ); ulong acct_off = i ; 183 136722 : ulong end = i+acct_cnt; 184 136731 : for( ; i<end; i++ ) CHECK( payload[ i ]<acct_addr_cnt ); 185 136719 : CHECK_LEFT( data_sz ); ulong data_off = i ; i+=data_sz; 186 : 187 136719 : parsed->instr[ j ].acct_off = (ushort)acct_off; 188 136719 : parsed->instr[ j ].data_off = (ushort)data_off; 189 136719 : } 190 : 191 6336 : CHECK_LEFT( FD_TXN_SIGNATURE_SZ*signature_cnt ); ulong signature_off = i ; i+=FD_TXN_SIGNATURE_SZ*signature_cnt; 192 : 193 6333 : parsed->transaction_version = FD_TXN_V1; 194 6333 : parsed->signature_cnt = signature_cnt; 195 : /* In V1, the message is at the front of the packet and the 196 : signatures are at the end. So message_off = 0. */ 197 6333 : parsed->signature_off = (ushort)signature_off; 198 6333 : parsed->message_off = 0; 199 6333 : parsed->readonly_signed_cnt = ro_signed_cnt; 200 6333 : parsed->readonly_unsigned_cnt = ro_unsigned_cnt; 201 6333 : parsed->acct_addr_cnt = (ushort)acct_addr_cnt; 202 6333 : parsed->acct_addr_off = (ushort)acct_addr_off; 203 6333 : parsed->recent_blockhash_off = (ushort)recent_blockhash_off; 204 : /* No ALTs in V1 */ 205 6333 : parsed->addr_table_lookup_cnt = (uchar)0; 206 6333 : parsed->addr_table_adtl_writable_cnt = (uchar)0; 207 6333 : parsed->addr_table_adtl_cnt = (uchar)0; 208 6333 : parsed->_padding_reserved_1 = (uchar)0; 209 6333 : parsed->v1_txn_config_values_off = (ushort)config_values_off; 210 6333 : parsed->instr_cnt = (ushort)instr_cnt; 211 : 212 : /* Check for leftover bytes if payload_sz_opt not specified. */ 213 6333 : CHECK( (payload_sz_opt!=NULL) | (i==original_payload_sz) ); 214 : 215 6330 : if( FD_LIKELY( counters_opt ) ) counters_opt->success_cnt++; 216 6330 : if( FD_LIKELY( payload_sz_opt ) ) *payload_sz_opt = i; 217 6330 : return fd_txn_footprint( instr_cnt, 0 ); 218 6333 : } 219 : 220 : /* ------------------------------ Legacy/V0 transaction parser ------------------------------ */ 221 : 222 : /* The documentation sometimes calls signature_cnt a compact-u16 and 223 : sometimes a u8. Because of transaction size limits, even allowing 224 : for a 3k transaction caps the signatures at 48, so we're 225 : comfortably in the range where a compact-u16 and a u8 are 226 : represented the same way. */ 227 61315341 : CHECK_LEFT( 1UL ); uchar signature_cnt = payload[ i ]; i++; 228 : /* Must have at least one signer for the fee payer */ 229 61315341 : CHECK( (1UL<=signature_cnt) & (signature_cnt<=FD_TXN_SIG_MAX) ); 230 61314645 : CHECK_LEFT( FD_TXN_SIGNATURE_SZ*signature_cnt ); ulong signature_off = i ; i+=FD_TXN_SIGNATURE_SZ*signature_cnt; 231 : 232 : /* Not actually parsing anything, just store. */ ulong message_off = i ; 233 61313670 : CHECK_LEFT( 1UL ); uchar header_b0 = payload[ i ]; i++; 234 : 235 61313664 : uchar transaction_version; 236 61313664 : if( FD_LIKELY( (ulong)header_b0 & 0x80UL ) ) { 237 : /* This is a versioned transaction */ 238 30388620 : transaction_version = header_b0 & 0x7F; 239 30388620 : CHECK( transaction_version==FD_TXN_V0 ); /* Only recognized one so far */ 240 : 241 30387840 : CHECK_LEFT( 1UL ); CHECK( signature_cnt==payload[ i ] ); i++; 242 30925044 : } else { 243 30925044 : transaction_version = FD_TXN_VLEGACY; 244 30925044 : CHECK( signature_cnt==header_b0 ); 245 30925044 : } 246 61311318 : CHECK_LEFT( 1UL ); uchar ro_signed_cnt = payload[ i ]; i++; 247 : /* Must have at least one writable signer for the fee payer */ 248 61311312 : CHECK( ro_signed_cnt<signature_cnt ); 249 : 250 61309788 : CHECK_LEFT( 1UL ); uchar ro_unsigned_cnt= payload[ i ]; i++; 251 : 252 61309782 : ushort acct_addr_cnt = (ushort)0; 253 61309782 : READ_CHECKED_COMPACT_U16( bytes_consumed, acct_addr_cnt, i ); i+=bytes_consumed; 254 61309008 : CHECK( (signature_cnt<=acct_addr_cnt) & (acct_addr_cnt<=FD_TXN_ACCT_ADDR_MAX) ); 255 61308615 : CHECK( (ulong)signature_cnt+(ulong)ro_unsigned_cnt<=(ulong)acct_addr_cnt ); 256 : 257 61307118 : CHECK_LEFT( FD_TXN_ACCT_ADDR_SZ*acct_addr_cnt ); ulong acct_addr_off = i ; i+=FD_TXN_ACCT_ADDR_SZ*acct_addr_cnt; 258 61304076 : CHECK_LEFT( FD_TXN_BLOCKHASH_SZ ); ulong recent_blockhash_off = i ; i+=FD_TXN_BLOCKHASH_SZ; 259 : 260 61303878 : ushort instr_cnt = (ushort)0; 261 61303878 : READ_CHECKED_COMPACT_U16( bytes_consumed, instr_cnt, i ); i+=bytes_consumed; 262 : 263 61303842 : CHECK( (ulong)instr_cnt<=FD_TXN_INSTR_MAX ); 264 : 265 61302687 : CHECK_LEFT( MIN_INSTR_SZ*instr_cnt ); 266 : /* If it has >0 instructions, it must have at least one other account 267 : address (the program id) that can't be the fee payer */ 268 61302579 : CHECK( (ulong)acct_addr_cnt>(!!instr_cnt) ); 269 : 270 61302576 : fd_txn_t * parsed = (fd_txn_t *)out_buf; 271 : 272 61302576 : parsed->transaction_version = transaction_version; 273 61302576 : parsed->signature_cnt = signature_cnt; 274 61302576 : parsed->signature_off = (ushort)signature_off; 275 61302576 : parsed->message_off = (ushort)message_off; 276 61302576 : parsed->readonly_signed_cnt = ro_signed_cnt; 277 61302576 : parsed->readonly_unsigned_cnt = ro_unsigned_cnt; 278 61302576 : parsed->acct_addr_cnt = acct_addr_cnt; 279 61302576 : parsed->acct_addr_off = (ushort)acct_addr_off; 280 61302576 : parsed->recent_blockhash_off = (ushort)recent_blockhash_off; 281 61302576 : parsed->_padding_reserved_1 = (uchar)0; 282 : /* v1_txn_config_values_off is a V1-specific field */ 283 61302576 : parsed->v1_txn_config_values_off = (ushort)0; 284 : /* Need to assign addr_table_lookup_cnt, 285 : addr_table_adtl_writable_cnt, addr_table_adtl_cnt later */ 286 61302576 : parsed->instr_cnt = instr_cnt; 287 : 288 61302576 : uchar max_acct = 0UL; 289 338392827 : for( ulong j=0UL; j<instr_cnt; j++ ) { 290 : 291 : /* Parsing instruction */ 292 277110792 : ushort acct_cnt = (ushort)0; 293 277110792 : ushort data_sz = (ushort)0; 294 277110792 : CHECK_LEFT( MIN_INSTR_SZ ); uchar program_id = payload[ i ]; i++; 295 277110468 : READ_CHECKED_COMPACT_U16( bytes_consumed, acct_cnt, i ); i+=bytes_consumed; 296 277108365 : CHECK( acct_cnt<=FD_TXN_INSTR_ACCT_MAX ); 297 277104774 : CHECK_LEFT( acct_cnt ); ulong acct_off = i ; 298 2358231741 : for( ulong k=0; k<acct_cnt; k++ ) { max_acct=fd_uchar_max( max_acct, payload[ k+i ] ); } i+=acct_cnt; 299 277103829 : READ_CHECKED_COMPACT_U16( bytes_consumed, data_sz, i ); i+=bytes_consumed; 300 277098051 : CHECK_LEFT( data_sz ); ulong data_off = i ; i+=data_sz; 301 : 302 : /* Account 0 is the fee payer and the program can't be the fee 303 : payer. The fee payer account must be owned by the system 304 : program, but the program must be an executable account and the 305 : system program is not permitted to own any executable account. 306 : As of https://github.com/solana-labs/solana/issues/25034, the 307 : program ID can't come from a table. */ 308 277096812 : CHECK( (0UL < (ulong)program_id) & ((ulong)program_id < (ulong)acct_addr_cnt) ); 309 : 310 277090251 : parsed->instr[ j ].program_id = program_id; 311 277090251 : parsed->instr[ j ]._padding_reserved_1 = (uchar)0; 312 277090251 : parsed->instr[ j ].acct_cnt = acct_cnt; 313 277090251 : parsed->instr[ j ].data_sz = data_sz; 314 : /* By our invariant, i<size when it was copied into acct_off and 315 : data_off, and size<=USHORT_MAX from above, so this cast is safe */ 316 277090251 : parsed->instr[ j ].acct_off = (ushort)acct_off; 317 277090251 : parsed->instr[ j ].data_off = (ushort)data_off; 318 277090251 : } 319 61282035 : #undef MIN_INSTR_SIZE 320 : 321 61282035 : ushort addr_table_cnt = 0; 322 61282035 : ulong addr_table_adtl_writable_cnt = 0; 323 61282035 : ulong addr_table_adtl_cnt = 0; 324 : 325 : /* parsed->instr_cnt set above, so calling get_address_tables is safe */ 326 61282035 : fd_txn_acct_addr_lut_t * address_tables = fd_txn_get_address_tables( parsed ); 327 61282035 : if( FD_LIKELY( transaction_version==FD_TXN_V0 ) ) { 328 30379104 : #define MIN_ADDR_LUT_SIZE (34UL) 329 30379104 : READ_CHECKED_COMPACT_U16( bytes_consumed, addr_table_cnt, i ); i+=bytes_consumed; 330 30378822 : CHECK( addr_table_cnt <= FD_TXN_ADDR_TABLE_LOOKUP_MAX ); 331 30378360 : CHECK_LEFT( MIN_ADDR_LUT_SIZE*addr_table_cnt ); 332 : 333 121500552 : for( ulong j=0; j<addr_table_cnt; j++ ) { 334 91127760 : CHECK_LEFT( FD_TXN_ACCT_ADDR_SZ ); ulong addr_off = i ; i+=FD_TXN_ACCT_ADDR_SZ; 335 : 336 91127580 : ushort writable_cnt = 0; 337 91127580 : ushort readonly_cnt = 0; 338 91127580 : READ_CHECKED_COMPACT_U16( bytes_consumed, writable_cnt, i ); i+=bytes_consumed; 339 91126725 : CHECK_LEFT( writable_cnt ); ulong writable_off = i ; i+=writable_cnt; 340 91125495 : READ_CHECKED_COMPACT_U16( bytes_consumed, readonly_cnt, i ); i+=bytes_consumed; 341 91124922 : CHECK_LEFT( readonly_cnt ); ulong readonly_off = i ; i+=readonly_cnt; 342 : 343 91123194 : CHECK( writable_cnt<=FD_TXN_ACCT_ADDR_MAX-acct_addr_cnt ); /* implies <256 ... */ 344 91123185 : CHECK( readonly_cnt<=FD_TXN_ACCT_ADDR_MAX-acct_addr_cnt ); 345 91123101 : CHECK( (ushort)1 <=writable_cnt+readonly_cnt ); /* ... so the sum can't overflow */ 346 91123101 : address_tables[ j ].addr_off = (ushort)addr_off; 347 91123101 : address_tables[ j ].writable_cnt = (uchar )writable_cnt; 348 91123101 : address_tables[ j ].readonly_cnt = (uchar )readonly_cnt; 349 91123101 : address_tables[ j ].writable_off = (ushort)writable_off; 350 91123101 : address_tables[ j ].readonly_off = (ushort)readonly_off; 351 : 352 91123101 : addr_table_adtl_writable_cnt += (ulong)writable_cnt; 353 91123101 : addr_table_adtl_cnt += (ulong)writable_cnt + (ulong)readonly_cnt; 354 91123101 : } 355 30377451 : } 356 61275723 : #undef MIN_ADDR_LUT_SIZE 357 : /* Check for leftover bytes if out_sz_opt not specified. */ 358 61275723 : CHECK( (payload_sz_opt!=NULL) | (i==original_payload_sz) ); 359 : 360 61275588 : CHECK( acct_addr_cnt+addr_table_adtl_cnt<=FD_TXN_ACCT_ADDR_MAX ); /* implies addr_table_adtl_cnt<256 */ 361 : 362 : /* Final validation that all the account address indices are in range */ 363 61275588 : CHECK( max_acct < acct_addr_cnt + addr_table_adtl_cnt ); 364 : 365 : /* Assign final variables */ 366 61228524 : parsed->addr_table_lookup_cnt = (uchar)addr_table_cnt; 367 61228524 : parsed->addr_table_adtl_writable_cnt = (uchar)addr_table_adtl_writable_cnt; 368 61228524 : parsed->addr_table_adtl_cnt = (uchar)addr_table_adtl_cnt; 369 : 370 61228524 : if( FD_LIKELY( counters_opt ) ) counters_opt->success_cnt++; 371 61228524 : if( FD_LIKELY( payload_sz_opt ) ) *payload_sz_opt = i; 372 : 373 61228524 : return fd_txn_footprint( instr_cnt, addr_table_cnt ); 374 : 375 61275588 : #undef CHECK 376 61275588 : #undef CHECK_LEFT 377 61275588 : #undef READ_CHECKED_COMPACT_U16 378 61275588 : }