LCOV - code coverage report
Current view: top level - ballet/txn - fd_txn_parse.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 197 197 100.0 %
Date: 2026-08-14 04:54:57 Functions: 1 1 100.0 %

          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 : }

Generated by: LCOV version 1.14