LCOV - code coverage report
Current view: top level - disco/keyguard - fd_keyguard_match.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 187 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 15 0.0 %

          Line data    Source code
       1             : #include "fd_keyguard.h"
       2             : #include "../../ballet/shred/fd_shred.h"
       3             : #include "../../ballet/txn/fd_compact_u16.h"
       4             : #include "../../flamenco/gossip/fd_gossip_value.h"
       5             : #include "../../discof/repair/fd_repair.h"
       6             : 
       7             : /* fd_keyguard_match fingerprints signing requests and checks them for
       8             :    ambiguity.
       9             : 
      10             :    Supported message types are as follows:
      11             : 
      12             :    - Legacy transaction messages
      13             :    - Version 0 transaction messages
      14             :    - Version 1 transaction messages
      15             :    - Legacy shred signed payloads
      16             :    - Merkle shred roots
      17             :    - TLS CertificateVerify challenges
      18             :    - Gossip message signed payloads (CrdsData)
      19             : 
      20             :    ### Fake Signing Attacks
      21             : 
      22             :    The main goal of fd_keyguard_match is to defeat "fake signing"
      23             :    attacks.  These are attacks in which the keyguard signs a request for
      24             :    which the client is not authorized.  Such attacks use a combination
      25             :    of vulnerabilities:  Key reuse, and type confusion.
      26             : 
      27             :    Key reuse is particularly prevalent with the validator identity key,
      28             :    the hot Ed25519 key that a validator uses in almost all protocols
      29             :    that it actively participates in.
      30             : 
      31             :    Type confusion occurs when the message payload being signed can be
      32             :    interpreted as multiple different message types.  Usually, this is
      33             :    categorically prevented by using "signing domains".
      34             : 
      35             :    Such attacks are particularly dangerous to validators because their
      36             :    validator identity key holds an amount of native tokens to
      37             :    participate in Tower BFT voting.  In the worst case, an attacker
      38             :    could trick a validator into signing an innocuous message (e.g. a
      39             :    gossip message) that can also be interpreted as a transaction
      40             :    withdrawing these tokens.
      41             : 
      42             :    ### Code Verification
      43             : 
      44             :    The safety of this module can be verified using a number of CBMC
      45             :    proofs composed via deductive reasoning.
      46             : 
      47             :    - fd_txn_minsz_proof verifies the constant FD_TXN_MIN_SERIALIZED_SZ.
      48             :    - fd_txn_ambiguity_gossip_proof verifies that gossip messages cannot
      49             :      be parsed as transactions.
      50             :    - fd_keyguard_match_txn_harness verifies that the txn fingerprinting
      51             :      logic is free of false negatives.
      52             :    - fd_keyguard_ambiguity_proof verifies that any input up to 2048 byte
      53             :      size are unambiguous, i.e. either detected by one or none of the
      54             :      fingerprinting functions.
      55             : 
      56             :    Under the hood, CBMC executes the keyguard logic with all possible
      57             :    inputs (>=2^16384 unique inputs) via symbolic execution.  The CBMC
      58             :    machine model also verifies that the code is free of common
      59             :    vulnerability classes (memory unsoundness, undefined behavior, …).
      60             : 
      61             :    As a result, we know with a high degree of certainty that type
      62             :    detection logic is free of false negatives.  For example, when
      63             :    fd_keyguard_match sees a transaction, it will always reliably detect
      64             :    it as one.  (fd_keyguard_match might also wrongly fingerprint
      65             :    arbitrary other inputs as, e.g. transactions.  But this is not a
      66             :    problem, as strict checks follow later on in fd_keyguard_authorize.)
      67             : 
      68             :    ### Deployment Context
      69             : 
      70             :    fd_keyguard_match is exposed to untrusted "signing request" inputs
      71             :    and implements the first line of authorization checks in the
      72             :    keyguard.  It is thus a critical component for securing the identity
      73             :    key.
      74             : 
      75             :    ### Implementation Approach
      76             : 
      77             :    This code looks awful and scary, but is carefully crafted to meet the
      78             :    aforementioned high assurance and formal verification requirements.
      79             : 
      80             :    Although parsers for the supported message types are available
      81             :    elsewhere in the codebase, they were not used here due to their time
      82             :    complexity exceeding the capabilities of CBMC.  The time complexity
      83             :    of all parsers in this compile unit is O(1), which allowed for
      84             :    complete CBMC coverage.
      85             : 
      86             :    TLDR:  The following code implements the least possible logic
      87             :           required to reliably detect types of identity key signing
      88             :           payloads without false negatives. */
      89             : 
      90             : FD_FN_PURE static int
      91             : fd_keyguard_payload_matches_txn_msg( uchar const * data,
      92             :                                      ulong         sz,
      93           0 :                                      int           sign_type ) {
      94             : 
      95           0 :   uchar const * end = data + sz;
      96             : 
      97           0 :   if( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
      98             : 
      99             :   /* txn_msg_min_sz is the smallest valid size of a transaction msg. A
     100             :      transaction is the concatenation of (signature count, signatures,
     101             :      msg).  The smallest size of a txn is FD_TXN_MIN_SERIALIZED_SZ
     102             :      (formally proven with CBMC in fd_txn_minsz_proof.c).  We know the
     103             :      smallest sizes of "signature count" and "signatures", thus we can
     104             :      derive the smallest size of "msg". */
     105             : 
     106           0 :   ulong const txn_msg_min_sz =
     107           0 :       FD_TXN_MIN_SERIALIZED_SZ
     108           0 :     -  1UL   /* min sz of signature count (compact_u16 encoding) */
     109           0 :     - 64UL;  /* min sz of signature list (array of Ed25519 sigs) */
     110           0 :   if( sz<txn_msg_min_sz ) return 0;
     111             : 
     112             :   /* Message type check.
     113             : 
     114             :      Bit patterns of first bytes are as follows
     115             : 
     116             :      - 0aaaaaaa bbbbbbbb cccccccc           (Legacy txns)
     117             :      - 10000000 aaaaaaaa bbbbbbbb cccccccc  (v0     txns)
     118             :      - 10000001 aaaaaaaa bbbbbbbb cccccccc  (v1     txns)
     119             : 
     120             :      Where 'a' are the bits that make up the 'required signature count'
     121             :        ... 'b'         ....                  'readonly signed count'
     122             :        ... 'c'         ....                  'readonly unsigned count' */
     123             : 
     124           0 :   uchar const * cursor    = data;
     125           0 :   uint          header_b0 = *cursor;
     126           0 :   cursor++;
     127           0 :   uint          sig_cnt;  /* sig count (ignoring compact_u16 encoding) */
     128           0 :   if( header_b0 & 0x80UL ) {
     129             :     /* Versioned message, v0 and v1 recognized so far */
     130           0 :     uint version = header_b0 & 0x7F;
     131           0 :     if( version!=FD_TXN_V0 && version!=FD_TXN_V1 ) return 0;
     132             : 
     133             :     /* Check transaction V1 separately because the layout is
     134             :        different. We do the same checks for V1 and V0/legacy, just in
     135             :        a different code branch. */
     136           0 :     if( version==FD_TXN_V1 ) {
     137           0 :       sig_cnt = *cursor;
     138           0 :       cursor++;
     139             : 
     140             :       /* There must be at least one signature. */
     141           0 :       if( sig_cnt==0U ) return 0;
     142             : 
     143             :       /* Check if the signatures exceed the V1 limit */
     144           0 :       if( sig_cnt>FD_TXN_SIG_MAX ) return 0;
     145             : 
     146             :       /* Skip other fields */
     147             :       //uint ro_signed_cnt      = *cursor;
     148           0 :       cursor++;
     149             :       //uint ro_unsigned_cnt    = *cursor;
     150           0 :       cursor++;
     151             :       //uint config_mask        = fd_uint_load_4( cursor );
     152           0 :       cursor += 4UL;
     153             :       //uchar const * blockhash = cursor;
     154           0 :       cursor += FD_TXN_BLOCKHASH_SZ;
     155             : 
     156           0 :       if( cursor + 2 > end ) return 0;
     157           0 :       ulong instr_cnt = *cursor;
     158           0 :       cursor++;
     159           0 :       ulong addr_cnt  = *cursor;
     160           0 :       cursor++;
     161             : 
     162             :       /* Check if the instructions exceed the V1 limit */
     163           0 :       if( instr_cnt>FD_TXN_INSTR_MAX ) return 0;
     164             : 
     165             :       /* Check if the addresses exceed the V1 limit */
     166           0 :       if( addr_cnt>FD_TXN_ACCT_ADDR_MAX ) return 0;
     167             : 
     168           0 :       if( sig_cnt>addr_cnt ) return 0;
     169             : 
     170           0 :       return 1;
     171           0 :     }
     172             : 
     173           0 :     sig_cnt = *cursor;
     174           0 :     cursor++;
     175           0 :   } else {
     176             :     /* Legacy message */
     177           0 :     sig_cnt = header_b0;
     178           0 :   }
     179             : 
     180             :   /* Legacy and v0 txns are at most FD_TXN_MTU_V0 bytes. */
     181           0 :   if( sz>FD_TXN_MTU_V0 ) return 0;
     182             : 
     183             :   /* There must be at least one signature. */
     184           0 :   if( sig_cnt==0U ) return 0;
     185             : 
     186             :   /* Check if signatures exceed txn size limit */
     187           0 :   ulong sig_sz;
     188           0 :   if( __builtin_umull_overflow( sig_cnt, 64UL, &sig_sz ) ) return 0;
     189           0 :   if( sig_sz > (FD_TXN_MTU_V0-txn_msg_min_sz) ) return 0;
     190             : 
     191             :   /* Skip other fields */
     192             :   //uint ro_signed_cnt   = *cursor;
     193           0 :   cursor++;
     194             :   //uint ro_unsigned_cnt = *cursor;
     195           0 :   cursor++;
     196             : 
     197           0 :   if( cursor + 3 > end ) return 0;
     198           0 :   ulong addr_cnt_sz = fd_cu16_dec_sz( cursor, 3UL );
     199           0 :   if( !addr_cnt_sz ) return 0;
     200           0 :   ulong addr_cnt    = fd_cu16_dec_fixed( cursor, addr_cnt_sz );
     201           0 :   cursor += addr_cnt_sz;
     202             : 
     203           0 :   if( sig_cnt>addr_cnt ) return 0;
     204             : 
     205           0 :   return 1;
     206           0 : }
     207             : 
     208             : FD_FN_PURE static int
     209             : fd_keyguard_payload_matches_ping_msg( uchar const * data,
     210             :                                       ulong         sz,
     211           0 :                                       int           sign_type ) {
     212           0 :   return sign_type==FD_KEYGUARD_SIGN_TYPE_ED25519 &&
     213           0 :          sz==32UL &&
     214           0 :          (memcmp( data, "SOLANA_PING_PONG", 16UL ) == 0);
     215           0 : }
     216             : 
     217             : FD_FN_PURE static int
     218             : fd_keyguard_payload_matches_pong_msg( uchar const * data,
     219             :                                       ulong         sz,
     220           0 :                                       int           sign_type ) {
     221           0 :   return sign_type==FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519 &&
     222           0 :          sz==48UL &&
     223           0 :          (memcmp( data, "SOLANA_PING_PONG", 16UL ) == 0);
     224           0 : }
     225             : 
     226             : FD_FN_PURE static int
     227             : fd_keyguard_payload_matches_prune_data( uchar const * data,
     228             :                                         ulong         sz,
     229           0 :                                         int           sign_type ) {
     230           0 :   if( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
     231             : 
     232           0 :   ulong const static_sz = 106UL;
     233           0 :   if( sz < static_sz ) return 0;
     234           0 :   if( sz > FD_GOSSIP_MTU ) return 0; /* prune messages fit in a gossip packet */
     235             : 
     236           0 :   if( FD_LOAD( ulong, data )!=18UL ) return 0;
     237           0 :   if(  memcmp( data+8UL, "\xffSOLANA_PRUNE_DATA", 18UL ) ) return 0;
     238             : 
     239           0 :   ulong prune_cnt = FD_LOAD( ulong, data+58UL );
     240           0 :   ulong expected_sz;
     241           0 :   if( __builtin_umull_overflow( prune_cnt,   32UL,      &expected_sz ) ) return 0;
     242           0 :   if( __builtin_uaddl_overflow( expected_sz, static_sz, &expected_sz ) ) return 0;
     243           0 :   if( sz != expected_sz ) return 0;
     244             : 
     245           0 :   return 1;
     246           0 : }
     247             : 
     248             : FD_FN_PURE static int
     249             : fd_keyguard_payload_matches_gossip( uchar const * data,
     250             :                                     ulong         sz,
     251           0 :                                     int           sign_type ) {
     252             : 
     253             :   /* All gossip messages except pings use raw signing */
     254           0 :   if( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
     255             : 
     256             :   /* Every gossip message contains a 4 byte enum variant tag (at the
     257             :      beginning of the message) and a 32 byte public key (at an arbitrary
     258             :      location), and fits in a gossip packet. */
     259           0 :   if( sz<36UL          ) return 0;
     260           0 :   if( sz>FD_GOSSIP_MTU ) return 0;
     261             : 
     262           0 :   uint tag = FD_LOAD( uint, data );
     263             : 
     264           0 :   return tag<FD_GOSSIP_VALUE_CNT;
     265           0 : }
     266             : 
     267             : FD_FN_PURE static int
     268             : fd_keyguard_payload_matches_repair( uchar const * data,
     269             :                                     ulong         sz,
     270           0 :                                     int           sign_type ) {
     271             : 
     272             :   /* All repair messages except pings use raw signing */
     273           0 :   if( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
     274             : 
     275             :   /* Every repair message contains a 4 byte enum variant tag (at the
     276             :      beginning of the message) and a 32 byte public key (at an arbitrary
     277             :      location), and is at most FD_REPAIR_MAX_PREIMAGE_SZ bytes. */
     278           0 :   if( sz<36UL                      ) return 0;
     279           0 :   if( sz>FD_REPAIR_MAX_PREIMAGE_SZ ) return 0;
     280             : 
     281             :   /* Ensure that the kind matches a possible repair request. */
     282           0 :   uint kind = FD_LOAD( uint, data );
     283           0 :   if( (kind==FD_REPAIR_KIND_SHRED)
     284           0 :     | (kind==FD_REPAIR_KIND_HIGHEST_SHRED)
     285           0 :     | (kind==FD_REPAIR_KIND_ORPHAN)
     286           0 :     | (kind==AG_REPAIR_KIND_PARENT_FEC_COUNT)
     287           0 :     | (kind==AG_REPAIR_KIND_FEC_ROOT)
     288           0 :     | (kind==AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID) )
     289           0 :     return 1;
     290             : 
     291           0 :   return 0;
     292           0 : }
     293             : 
     294             : FD_FN_PURE int
     295             : fd_keyguard_payload_matches_shred( uchar const * data,
     296             :                                    ulong         sz,
     297           0 :                                    int           sign_type ) {
     298           0 :   (void)data;
     299             : 
     300             :   /* Note: Legacy shreds no longer relevant (drop_legacy_shreds) */
     301             : 
     302             :   /* FIXME: Sign Merkle shreds using SIGN_TYPE_SHA256_ED25519 (!!!) */
     303           0 :   if( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
     304           0 :   if( sz != 32 ) return 0;
     305             : 
     306           0 :   return 1;
     307           0 : }
     308             : 
     309             : FD_FN_PURE int
     310             : fd_keyguard_payload_matches_tls_cv( uchar const * data,
     311             :                                     ulong         sz,
     312           0 :                                     int           sign_type ) {
     313             : 
     314           0 :   if( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
     315             : 
     316             :   /* TLS CertificateVerify signing payload one of 3 sizes
     317             :      depending on hash function chosen */
     318           0 :   switch( sz ) {
     319           0 :   case 130UL: break;  /* Prefix + 32 byte hash */
     320           0 :   case 146UL: break;  /* Prefix + 48 byte hash */
     321           0 :   case 162UL: break;  /* Prefix + 64 byte hash */
     322           0 :   default:
     323           0 :     return 0;
     324           0 :   }
     325             : 
     326             :   /* Always prefixed with client or server pattern */
     327           0 :   static char const client_prefix[ 98 ] =
     328           0 :     "                                "  /* 32 spaces */
     329           0 :     "                                "  /* 32 spaces */
     330           0 :     "TLS 1.3, client CertificateVerify";
     331             : 
     332           0 :   static char const server_prefix[ 98 ] =
     333           0 :     "                                "  /* 32 spaces */
     334           0 :     "                                "  /* 32 spaces */
     335           0 :     "TLS 1.3, server CertificateVerify";
     336           0 :   int is_client = 0==memcmp( data, client_prefix, 98UL );
     337           0 :   int is_server = 0==memcmp( data, server_prefix, 98UL );
     338           0 :   return (is_client)|(is_server);
     339           0 : }
     340             : 
     341             : FD_FN_PURE int
     342             : fd_keyguard_payload_matches_ag_vote( uchar const * data,
     343             :                                      ulong         sz,
     344           0 :                                      int           sign_type ) {
     345             : 
     346             :   /* Alpenglow vote payload produced by ag_vote_signing_ser:
     347             : 
     348             :      u8  tag            (1..5, WireConsensusMessageKind vote tags)
     349             :      u64 slot
     350             :      [32 bytes block id] only for notar (1) and notar fallback (4)
     351             :      u16 shred_version */
     352             : 
     353           0 :   if( sign_type != FD_KEYGUARD_SIGN_TYPE_BLS ) return 0;
     354           0 :   if( sz!=11UL && sz!=43UL ) return 0;
     355           0 :   uchar tag = data[ 0 ];
     356           0 :   if( tag<1 || tag>5 ) return 0;
     357           0 :   int has_hash = ( tag==1 ) | ( tag==4 );
     358           0 :   return has_hash ? ( sz==43UL ) : ( sz==11UL );
     359           0 : }
     360             : 
     361             : FD_FN_PURE int
     362             : fd_keyguard_payload_matches_bls_pubkey( uchar const * data FD_PARAM_UNUSED,
     363             :                                         ulong         sz,
     364           0 :                                         int           sign_type ) {
     365             : 
     366             :   /* Alpenglow BLS public key query: u64 authority index.  Its range is
     367             :      checked by fd_keyguard_authorize_bls_pubkey. */
     368             : 
     369           0 :   return sign_type==FD_KEYGUARD_SIGN_TYPE_BLS_PUBKEY && sz==sizeof(ulong);
     370           0 : }
     371             : 
     372             : FD_FN_PURE int
     373             : fd_keyguard_payload_matches_bundle( uchar const * data,
     374             :                                     ulong         sz,
     375           0 :                                     int           sign_type ) {
     376           0 :   (void)data;
     377             : 
     378           0 :   if( sign_type != FD_KEYGUARD_SIGN_TYPE_PUBKEY_CONCAT_ED25519 ) return 0;
     379           0 :   if( sz!=9UL ) return 0;
     380             : 
     381           0 :   return 1;
     382           0 : }
     383             : 
     384             : FD_FN_PURE int
     385             : fd_keyguard_payload_matches_event( uchar const * data,
     386             :                                    ulong         sz,
     387           0 :                                    int           sign_type ) {
     388           0 :   static char const sign_prefix[ 100 ] =
     389           0 :     "                                "  /* 32 spaces */
     390           0 :     "                                "  /* 32 spaces */
     391           0 :     "Firedancer event challenge-response";
     392             : 
     393           0 :   if( sz!=sizeof(sign_prefix)+217UL ) return 0;
     394           0 :   if( sign_type!=FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
     395           0 :   if( 0!=memcmp( data, sign_prefix, sizeof(sign_prefix) ) ) return 0;
     396           0 :   return 1;
     397           0 : }
     398             : 
     399             : /* Tower1_14_11: node_pubkey, threshold_depth, threshold_size, ... */
     400             : 
     401             : FD_FN_PURE static int
     402             : fd_keyguard_payload_matches_tower( uchar const * data,
     403             :                                    ulong         sz,
     404           0 :                                    int           sign_type ) {
     405           0 :   static uchar const zeros[ 65 ] = {0};
     406           0 :   if( sign_type!=FD_KEYGUARD_SIGN_TYPE_ED25519          ) return 0;
     407           0 :   if( sz<1807UL || sz>2522UL                            ) return 0; /* smallest and largest the tower tile writes */
     408           0 :   if( FD_LOAD( ulong, data+32UL )!=8UL                  ) return 0; /* threshold_depth */
     409           0 :   if( FD_LOAD( ulong, data+40UL )!=0x3FE5555555555555UL ) return 0; /* threshold_size, 2/3 as f64 bits */
     410           0 :   if( memcmp( data+48UL, zeros, sizeof(zeros) )         ) return 0;
     411           0 :   ulong votes_cnt = FD_LOAD( ulong, data+113UL );
     412           0 :   return votes_cnt>=1UL && votes_cnt<=31UL;
     413           0 : }
     414             : 
     415             : /* VoteHistory, as the votor writes it: node_pubkey, nine empty
     416             :    collections, root */
     417             : 
     418             : FD_FN_PURE static int
     419             : fd_keyguard_payload_matches_vote_history( uchar const * data,
     420             :                                           ulong         sz,
     421           0 :                                           int           sign_type ) {
     422           0 :   if( sign_type!=FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
     423           0 :   if( sz!=32UL+9UL*8UL+8UL                     ) return 0;
     424           0 :   for( ulong i=0UL; i<9UL; i++ ) if( FD_LOAD( ulong, data+32UL+i*8UL ) ) return 0;
     425           0 :   return 1;
     426           0 : }
     427             : 
     428             : FD_FN_PURE ulong
     429             : fd_keyguard_payload_match( uchar const * data,
     430             :                            ulong         sz,
     431           0 :                            int           sign_type ) {
     432           0 :   ulong res = 0UL;
     433           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_txn_msg     ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_TXN,          0 );
     434           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_gossip      ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_GOSSIP,       0 );
     435           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_repair      ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_REPAIR,       0 );
     436           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_prune_data  ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_PRUNE,        0 );
     437           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_shred       ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_SHRED,        0 );
     438           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_tls_cv      ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_TLS_CV,       0 );
     439           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_ping_msg    ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_PING,         0 );
     440           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_pong_msg    ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_PONG,         0 );
     441           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_bundle      ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_BUNDLE,       0 );
     442           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_event       ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_EVENT,        0 );
     443           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_ag_vote     ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_AG_VOTE,      0 );
     444           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_bls_pubkey  ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_BLS_PUBKEY,   0 );
     445           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_tower       ( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_TOWER,        0 );
     446           0 :   res |= fd_ulong_if( fd_keyguard_payload_matches_vote_history( data, sz, sign_type ), FD_KEYGUARD_PAYLOAD_VOTE_HISTORY, 0 );
     447           0 :   return res;
     448           0 : }

Generated by: LCOV version 1.14