LCOV - code coverage report
Current view: top level - discof/repair - fd_repair.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 1 41 2.4 %
Date: 2025-10-27 04:40:00 Functions: 0 35 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_discof_repair_fd_repair_h
       2             : #define HEADER_fd_src_discof_repair_fd_repair_h
       3             : 
       4             : /* fd_repair implements the Solana Repair protocol.  In a nutshell,
       5             :    Repair is a protocol for recovering shreds that the validator is
       6             :    expecting but has not received from Turbine.  Neither the logic for
       7             :    how the validator determines it should be expecting a shred nor the
       8             :    logic for determining which peer validator to request the shred from
       9             :    is implemented in this file (see fd_policy.h instead); rather, this
      10             :    is an implementation of the protocol itself for requesting shreds
      11             :    from other validators.
      12             : 
      13             :    The repair protocol supports four different message types on the
      14             :    client side:
      15             : 
      16             :    - Pong( ping_token )
      17             : 
      18             :      This is a response message to address validation via a Ping-Pong
      19             :      protocol.  When a validator receives a repair request from another
      20             :      validator it does not recognize, it will ignore the request and
      21             :      instead respond with its own Ping message to the requesting
      22             :      validator.  The Ping contains a 32-byte token that the requesting
      23             :      validator needs to hash and sign as part of a Pong response.  The
      24             :      scheme is as follows: the Ping token is concatenated with the
      25             :      prefix "SOLANA_PING_PONG", which is then piped in as the preimage
      26             :      into SHA-256.  The resulting hash is then signed and the signature
      27             :      along with the actual hash is packed into a Pong.
      28             : 
      29             :    - Shred( slot, shred_idx )
      30             : 
      31             :      This is a request for a specific shred in the provided slot.  The
      32             :      (slot, shred index) generally uniquely identifies a shred except in
      33             :      certain exceptional conditions (equivocation).  The responding
      34             :      validator will return the shred if it has it.
      35             : 
      36             :    - HighestShred( slot, shred_idx )
      37             : 
      38             :      This is a request for the highest shred in the provided slot that
      39             :      is greater than or equal to shred_idx.  Note this is not
      40             :      necessarily the last shred in the slot, as it depends on what the
      41             :      responding validator has available.  The responding validator will
      42             :      return the highest shred it has that meets the condition.
      43             : 
      44             :    - Orphan( slot )
      45             : 
      46             :      This is a request for up to 10 shreds, where each shred is the
      47             :      prior one's ancestor, beginning from but excluding slot.  For
      48             :      example, an orphan request for slot 10 will return a single shred
      49             :      for slots 9, 8, 7, 6, 5, 4, 3, 2 and 1 (assuming no skips).  Also,
      50             :      the responding validator will return the highest shred index it has
      51             :      for every ancestor it knows about.
      52             : 
      53             :     All 3 repair request types are prefixed with a common header of from
      54             :     pubkey, to pubkey, ulong timestamp and uint nonce.  The timestamp is
      55             :     a standard UNIX epoch (milliseconds since 1970-01-01T00:00:00Z) and
      56             :     the nonce is echoed back by the responding validator in the repair
      57             :     response.  Unlike a typical cryptographic nonce that prevents replay
      58             :     attacks, the repair server implementation ignores the nonce and this
      59             :     implementation leaves it up to the calling application to manage the
      60             :     nonce.  Note the 4 nonce bytes are appended after the end of a shred
      61             :     in a repair response.
      62             : 
      63             :     All communication across the wire is done with bincode serialization
      64             :     encoding.  Serialization and deserialization is code-generated by
      65             :     fd_types. */
      66             : 
      67             : #include "../../ballet/ed25519/fd_ed25519.h"
      68             : #include "../../flamenco/types/fd_types_custom.h"
      69             : 
      70             : /* FD_REPAIR_USE_HANDHOLDING:  Define this to non-zero at compile time
      71             :    to turn on additional runtime checks and logging. */
      72             : 
      73             : #ifndef FD_REPAIR_USE_HANDHOLDING
      74             : #define FD_REPAIR_USE_HANDHOLDING 1
      75             : #endif
      76             : 
      77             : /* FD_REPAIR_KIND_{PONG,SHRED,HIGHEST_SHRED,ORPHAN} specify discriminant
      78             :    values the protocol uses to distinguish message types. */
      79             : 
      80           0 : #define FD_REPAIR_KIND_PING          (0U)
      81           0 : #define FD_REPAIR_KIND_PONG          (7U)
      82           0 : #define FD_REPAIR_KIND_SHRED         (8U)
      83           0 : #define FD_REPAIR_KIND_HIGHEST_SHRED (9U)
      84           0 : #define FD_REPAIR_KIND_ORPHAN        (10U)
      85             : 
      86             : /* fd_repair_pong describes the schema of a Pong. */
      87             : 
      88             : struct __attribute__((packed)) fd_repair_pong {
      89             :   fd_pubkey_t      from; /* pubkey of the validator responding with the pong */
      90             :   fd_hash_t        hash; /* sha-256 hash generated from a ping hash */
      91             :   fd_ed25519_sig_t sig;  /* from's signature over the preceding hash field */
      92             : };
      93             : typedef struct fd_repair_pong fd_repair_pong_t;
      94             : 
      95             : /* REQ_HDR defines the common header of Repair request types. */
      96             : 
      97             : #define REQ_HDR                                                                     \
      98             :   fd_ed25519_sig_t sig;   /* ed25519 signature over all the subsequent fields */    \
      99             :   fd_pubkey_t      from;  /* pubkey of the validator that sent the request */       \
     100             :   fd_pubkey_t      to;    /* pubkey of the validator that is being requested */     \
     101             :   ulong            ts;    /* timestamp in milliseconds since unix epoch */          \
     102             :   uint             nonce; /* nonce to be echoed back by the responding validator */ \
     103             : 
     104             : /* fd_repair_shred requests the specific shred at slot and shred_idx
     105             :    from a peer validator. */
     106             : 
     107             : /* TODO: remove _req suffix from below after we remove all fd_types inclusions in repair_tile */
     108             : struct __attribute__((packed)) fd_repair_shred_req {
     109             :   REQ_HDR
     110             :   ulong slot;
     111             :   ulong shred_idx;
     112             : };
     113             : typedef struct fd_repair_shred_req fd_repair_shred_req_t;
     114             : 
     115             : /* fd_repair_highest_shred requests the highest shred in slot greater
     116             :    than shred_idx that a peer validator has.  Note this is not
     117             :    necessarily the last shred in the slot, as it depends on what the
     118             :    peer has available. */
     119             : 
     120             : struct __attribute__((packed)) fd_repair_highest_shred_req {
     121             :   REQ_HDR
     122             :   ulong slot;
     123             :   ulong shred_idx;
     124             : };
     125             : typedef struct fd_repair_highest_shred_req fd_repair_highest_shred_req_t;
     126             : 
     127             : /* fd_repair_orphan requests the ancestors of slot (an "orphaned" slot)
     128             :    from a peer validator.  The peer can respond with shreds for up to 10
     129             :    ancestor slots, where every shred is the last shred for that slot. */
     130             : 
     131             : struct __attribute__((packed)) fd_repair_orphan_req {
     132             :   REQ_HDR
     133             :   ulong slot;
     134             : };
     135             : typedef struct fd_repair_orphan_req fd_repair_orphan_req_t;
     136             : 
     137             : /* fd_repair_msg_t defines the schema of all Repair message types. */
     138             : 
     139             : struct __attribute__((packed)) fd_repair_msg {
     140             :   uint kind; /* FD_REPAIR_KIND_{PONG,SHRED,HIGHEST_SHRED,ORPHAN} */
     141             :   union {
     142             :     fd_repair_pong_t              pong;
     143             :     fd_repair_shred_req_t         shred;
     144             :     fd_repair_highest_shred_req_t highest_shred;
     145             :     fd_repair_orphan_req_t        orphan;
     146             :   };
     147             : };
     148             : typedef struct fd_repair_msg fd_repair_msg_t;
     149             : 
     150             : struct __attribute__((packed)) fd_repair_ping {
     151             :   uint kind;
     152             :   fd_repair_pong_t ping;
     153             : };
     154             : typedef struct fd_repair_ping fd_repair_ping_t;
     155             : 
     156             : /* FD_REPAIR_PONG_PREIMAGE_PREFIX is used by Repair's Ping-Pong protocol.
     157             :    Both a Ping and Pong contain a hash token, that is generated from a
     158             :    preimage prefixed with the below.  */
     159             : 
     160           0 : #define FD_REPAIR_PONG_PREIMAGE_PREFIX "SOLANA_PING_PONG"
     161           0 : #define FD_REPAIR_PONG_PREIMAGE_SZ (48UL)
     162             : 
     163             : /* FD_REPAIR_MAX_PREIMAGE_SZ is the maximum size of a preimage for a
     164             :    repair request. This is the size of the largest repair request
     165             :    (highest_shred or shred) without the signature. */
     166             : 
     167           3 : #define FD_REPAIR_MAX_PREIMAGE_SZ (sizeof(fd_repair_msg_t) - sizeof(fd_ed25519_sig_t))
     168             : 
     169             : static const fd_pubkey_t null_pubkey = {{ 0 }};
     170             : 
     171             : /* fd_repair_sign_fn defines the function signature for a user-provided
     172             :    signing callback. */
     173             : 
     174             : typedef void (fd_repair_sign_fn)( void * ctx, fd_repair_msg_t * msg, uchar * sig );
     175             : 
     176             : struct fd_repair {
     177             :   fd_pubkey_t         identity_key; /* validator identity key */
     178             :   fd_repair_sign_fn * sign_fn;      /* user-provided signing callback */
     179             :   void *              sign_ctx;     /* user-provided context for signing callback */
     180             :   fd_repair_msg_t     msg;          /* buffer for outgoing repair requests */
     181             : };
     182             : typedef struct fd_repair fd_repair_t;
     183             : 
     184             : /* Constructors */
     185             : 
     186             : /* fd_repair_{align,footprint} return the required alignment and
     187             :    footprint of a memory region suitable for use as repair.  Declaration
     188             :    friendly (e.g. a memory region declared as "fd_repair_t repair[1];"
     189             :    will automatically have the needed alignment and footprint). */
     190             : 
     191             : FD_FN_CONST static inline ulong
     192           0 : fd_repair_align( void ) {
     193           0 :   return alignof(fd_repair_t);
     194           0 : }
     195             : 
     196             : FD_FN_CONST static inline ulong
     197           0 : fd_repair_footprint( void ) {
     198           0 :   return sizeof(fd_repair_t);
     199           0 : }
     200             : 
     201             : /* fd_repair_new formats an unused memory region for use as a repair.
     202             :    mem is a non-NULL pointer to this region in the local address space
     203             :    with the required footprint and alignment.
     204             :    Initializes repair with the public identity key. */
     205             : 
     206             : void *
     207             : fd_repair_new( void * shmem, fd_pubkey_t * identity_key );
     208             : 
     209             : /* fd_repair_join joins the caller to the repair.  repair points to the
     210             :    first byte of the memory region backing the repair in the caller's
     211             :    address space.  Returns a pointer in the local address space to
     212             :    repair on success. */
     213             : 
     214             : fd_repair_t *
     215             : fd_repair_join( void * repair );
     216             : 
     217             : /* fd_repair_leave leaves a current local join.  Returns a pointer to
     218             :    the underlying shared memory region on success and NULL on failure
     219             :    (logs details).  Reasons for failure include repair is NULL. */
     220             : 
     221             : void *
     222             : fd_repair_leave( fd_repair_t const * repair );
     223             : 
     224             : /* fd_repair_delete unformats a memory region used as a repair.  Assumes
     225             :    only the nobody is joined to the region.  Returns a pointer to the
     226             :    underlying shared memory region or NULL if used obviously in error
     227             :    (e.g. repair is obviously not a repair ... logs details).  The
     228             :    ownership of the memory region is transferred to the caller. */
     229             : 
     230             : void *
     231             : fd_repair_delete( void * repair );
     232             : 
     233             : /* fd_repair_{pong,shred,highest_shred,orphan} creates and returns a
     234             :    pointer to a serialized {Pong,Shred,HighestShred,Orphan} message.
     235             :    Does not require the caller to provide memory, as Repair itself
     236             :    maintains a dedicated memory region (repair->msg) for buffering
     237             :    requests.  Assumes repair->msg is not already buffering an existing
     238             :    request and can be overwritten.  Returns a pointer to repair->msg on
     239             :    success, NULL on failure. */
     240             : 
     241             : 
     242             : fd_repair_msg_t * fd_repair_pong         ( fd_repair_t * repair, fd_hash_t * ping_token );
     243             : fd_repair_msg_t * fd_repair_shred        ( fd_repair_t * repair, fd_pubkey_t const * to, ulong ts, uint nonce, ulong slot, ulong shred_idx );
     244             : fd_repair_msg_t * fd_repair_highest_shred( fd_repair_t * repair, fd_pubkey_t const * to, ulong ts, uint nonce, ulong slot, ulong shred_idx );
     245             : fd_repair_msg_t * fd_repair_orphan       ( fd_repair_t * repair, fd_pubkey_t const * to, ulong ts, uint nonce, ulong slot );
     246             : 
     247             : /* fd_repair_sz returns the bincode-serialized sz of msg. */
     248             : 
     249             : FD_FN_PURE static inline ulong
     250           0 : fd_repair_sz( fd_repair_msg_t const * msg ) {
     251           0 :    switch( msg->kind ) {
     252           0 :      case FD_REPAIR_KIND_PONG:          return sizeof(uint) + sizeof(fd_repair_pong_t);
     253           0 :      case FD_REPAIR_KIND_SHRED:         return sizeof(uint) + sizeof(fd_repair_shred_req_t);
     254           0 :      case FD_REPAIR_KIND_HIGHEST_SHRED: return sizeof(uint) + sizeof(fd_repair_highest_shred_req_t);
     255           0 :      case FD_REPAIR_KIND_ORPHAN:        return sizeof(uint) + sizeof(fd_repair_orphan_req_t);
     256           0 :      default:                           FD_LOG_ERR(( "Unhandled repair kind %u", msg->kind ));
     257           0 :    }
     258           0 : }
     259             : 
     260             : /* preimage_pong takes a pong and takes the token in a ping and a
     261             :    FD_REPAIR_PONG_PREIMAGE_SZ-byte buffer and returns a pointer to a
     262             :    preimage that can be signed. */
     263             : 
     264             : static inline uchar *
     265           0 : preimage_pong( fd_hash_t const * ping_token, uchar * preimage_buf, ulong preimage_sz ) {
     266           0 : # if FD_REPAIR_USE_HANDHOLDING
     267           0 :   if( FD_UNLIKELY( preimage_sz != FD_REPAIR_PONG_PREIMAGE_SZ ) ) {
     268           0 :     FD_LOG_ERR(( "preimage_sz %lu must be %lu", preimage_sz, FD_REPAIR_PONG_PREIMAGE_SZ ));
     269           0 :   }
     270           0 : # endif
     271           0 :   ulong prefix_sz = sizeof(FD_REPAIR_PONG_PREIMAGE_PREFIX) - 1 /* subtract NUL */;
     272           0 :   memcpy( preimage_buf,        FD_REPAIR_PONG_PREIMAGE_PREFIX, prefix_sz         );
     273           0 :   memcpy( preimage_buf + prefix_sz, ping_token, sizeof(fd_hash_t) );
     274           0 :   return preimage_buf;
     275           0 : }
     276             : 
     277             : /* preimage_req takes a repair request populated with all fields except
     278             :    for the signature, and returns a pointer to a preimage that can be
     279             :    signed.  Modifies the msg in place.
     280             : 
     281             :    At the start of this function, the repair_msg_t should contain
     282             : 
     283             :      [ discriminant ] [ empty sig ] [ repair request fields ]
     284             :      ^                ^             ^
     285             :      0                4             68
     286             : 
     287             :      https://github.com/solana-labs/solana/blob/master/core/src/repair/serve_repair.rs#L1258
     288             : 
     289             :      We want to sign over
     290             :      [ discriminant ] [ payload ]
     291             :       ^                ^
     292             :       buffer           buffer+4
     293             : 
     294             :    We can do this without using any extra memory copying the discriminant
     295             :    to the last 4 bytes of the sig field, and returning a pointer to
     296             :    that copied location.  The sig field should be overwritten with the
     297             :    actual signature value later, else the fd_repair_msg_t will be
     298             :    incorrect. */
     299             : 
     300             : static inline uchar *
     301           0 : preimage_req( fd_repair_msg_t * msg, ulong * preimage_sz ) {
     302           0 :   uchar * preimage = (uchar *)fd_type_pun(msg);
     303           0 :   preimage += sizeof(fd_ed25519_sig_t);
     304           0 :   FD_STORE( uint, preimage, msg->kind ); /* copy discriminant over */
     305           0 :   *preimage_sz = fd_repair_sz( msg ) - sizeof(fd_ed25519_sig_t);
     306           0 :   return preimage;
     307           0 : }
     308             : #endif /* HEADER_fd_src_discof_repair_fd_repair_h */

Generated by: LCOV version 1.14