LCOV - code coverage report
Current view: top level - discof/repair - fd_repair.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 41 0.0 %
Date: 2026-05-17 07:08:10 Functions: 0 60 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. */
      65             : 
      66             : #include "../../ballet/ed25519/fd_ed25519.h"
      67             : #include "../../flamenco/fd_flamenco_base.h"
      68             : 
      69             : /* FD_REPAIR_USE_HANDHOLDING:  Define this to non-zero at compile time
      70             :    to turn on additional runtime checks and logging. */
      71             : 
      72             : #ifndef FD_REPAIR_USE_HANDHOLDING
      73             : #define FD_REPAIR_USE_HANDHOLDING 1
      74             : #endif
      75             : 
      76             : /* FD_REPAIR_KIND_{PONG,SHRED,HIGHEST_SHRED,ORPHAN} specify discriminant
      77             :    values the protocol uses to distinguish message types. */
      78             : 
      79           0 : #define FD_REPAIR_KIND_PING          (0U)
      80           0 : #define FD_REPAIR_KIND_PONG          (7U)
      81           0 : #define FD_REPAIR_KIND_SHRED         (8U)
      82           0 : #define FD_REPAIR_KIND_HIGHEST_SHRED (9U)
      83           0 : #define FD_REPAIR_KIND_ORPHAN        (10U)
      84             : 
      85             : /* fd_repair_pong describes the schema of a Pong. */
      86             : 
      87             : struct __attribute__((packed)) fd_repair_pong {
      88             :   fd_pubkey_t      from; /* pubkey of the validator responding with the pong */
      89             :   fd_hash_t        hash; /* sha-256 hash generated from a ping hash */
      90             :   fd_ed25519_sig_t sig;  /* from's signature over the preceding hash field */
      91             : };
      92             : typedef struct fd_repair_pong fd_repair_pong_t;
      93             : 
      94             : /* REQ_HDR defines the common header of Repair request types. */
      95             : 
      96             : #define REQ_HDR                                                                     \
      97             :   fd_ed25519_sig_t sig;   /* ed25519 signature over all the subsequent fields */    \
      98             :   fd_pubkey_t      from;  /* pubkey of the validator that sent the request */       \
      99             :   fd_pubkey_t      to;    /* pubkey of the validator that is being requested */     \
     100             :   ulong            ts;    /* timestamp in milliseconds since unix epoch */          \
     101             :   uint             nonce; /* nonce to be echoed back by the responding validator */ \
     102             : 
     103             : /* fd_repair_shred requests the specific shred at slot and shred_idx
     104             :    from a peer validator. */
     105             : 
     106             : /* TODO: remove _req suffix from below */
     107             : struct __attribute__((packed)) fd_repair_shred_req {
     108             :   REQ_HDR
     109             :   ulong slot;
     110             :   ulong shred_idx;
     111             : };
     112             : typedef struct fd_repair_shred_req fd_repair_shred_req_t;
     113             : 
     114             : /* fd_repair_highest_shred requests the highest shred in slot greater
     115             :    than shred_idx that a peer validator has.  Note this is not
     116             :    necessarily the last shred in the slot, as it depends on what the
     117             :    peer has available. */
     118             : 
     119             : struct __attribute__((packed)) fd_repair_highest_shred_req {
     120             :   REQ_HDR
     121             :   ulong slot;
     122             :   ulong shred_idx;
     123             : };
     124             : typedef struct fd_repair_highest_shred_req fd_repair_highest_shred_req_t;
     125             : 
     126             : /* fd_repair_orphan requests the ancestors of slot (an "orphaned" slot)
     127             :    from a peer validator.  The peer can respond with shreds for up to 10
     128             :    ancestor slots, where every shred is the last shred for that slot. */
     129             : 
     130             : struct __attribute__((packed)) fd_repair_orphan_req {
     131             :   REQ_HDR
     132             :   ulong slot;
     133             : };
     134             : typedef struct fd_repair_orphan_req fd_repair_orphan_req_t;
     135             : 
     136             : /* fd_repair_msg_t defines the schema of all Repair message types. */
     137             : 
     138             : struct __attribute__((packed)) fd_repair_msg {
     139             :   uint kind; /* FD_REPAIR_KIND_{PONG,SHRED,HIGHEST_SHRED,ORPHAN} */
     140             :   union {
     141             :     fd_repair_pong_t              pong;
     142             :     fd_repair_shred_req_t         shred;
     143             :     fd_repair_highest_shred_req_t highest_shred;
     144             :     fd_repair_orphan_req_t        orphan;
     145             :   };
     146             : };
     147             : typedef struct fd_repair_msg fd_repair_msg_t;
     148             : 
     149             : struct __attribute__((packed)) fd_repair_ping {
     150             :   uint kind;
     151             :   fd_repair_pong_t ping;
     152             : };
     153             : typedef struct fd_repair_ping fd_repair_ping_t;
     154             : 
     155             : /* FD_REPAIR_PONG_PREIMAGE_PREFIX is used by Repair's Ping-Pong protocol.
     156             :    Both a Ping and Pong contain a hash token, that is generated from a
     157             :    preimage prefixed with the below.  */
     158             : 
     159           0 : #define FD_REPAIR_PONG_PREIMAGE_PREFIX "SOLANA_PING_PONG"
     160           0 : #define FD_REPAIR_PONG_PREIMAGE_SZ (48UL)
     161             : 
     162             : /* FD_REPAIR_MAX_PREIMAGE_SZ is the maximum size of a preimage for a
     163             :    repair request. This is the size of the largest repair request
     164             :    (highest_shred or shred) without the signature. */
     165             : 
     166           0 : #define FD_REPAIR_MAX_PREIMAGE_SZ (sizeof(fd_repair_msg_t) - sizeof(fd_ed25519_sig_t))
     167             : 
     168             : static const fd_pubkey_t null_pubkey = {{ 0 }};
     169             : 
     170             : /* fd_repair_sign_fn defines the function signature for a user-provided
     171             :    signing callback. */
     172             : 
     173             : typedef void (fd_repair_sign_fn)( void * ctx, fd_repair_msg_t * msg, uchar * sig );
     174             : 
     175             : struct fd_repair {
     176             :   fd_pubkey_t         identity_key; /* validator identity key */
     177             :   fd_repair_sign_fn * sign_fn;      /* user-provided signing callback */
     178             :   void *              sign_ctx;     /* user-provided context for signing callback */
     179             :   fd_repair_msg_t     msg;          /* buffer for outgoing repair requests */
     180             : };
     181             : typedef struct fd_repair fd_repair_t;
     182             : 
     183             : /* Constructors */
     184             : 
     185             : /* fd_repair_{align,footprint} return the required alignment and
     186             :    footprint of a memory region suitable for use as repair.  Declaration
     187             :    friendly (e.g. a memory region declared as "fd_repair_t repair[1];"
     188             :    will automatically have the needed alignment and footprint). */
     189             : 
     190             : FD_FN_CONST static inline ulong
     191           0 : fd_repair_align( void ) {
     192           0 :   return alignof(fd_repair_t);
     193           0 : }
     194             : 
     195             : FD_FN_CONST static inline ulong
     196           0 : fd_repair_footprint( void ) {
     197           0 :   return sizeof(fd_repair_t);
     198           0 : }
     199             : 
     200             : /* fd_repair_new formats an unused memory region for use as a repair.
     201             :    mem is a non-NULL pointer to this region in the local address space
     202             :    with the required footprint and alignment.
     203             :    Initializes repair with the public identity key. */
     204             : 
     205             : void *
     206             : fd_repair_new( void * shmem, fd_pubkey_t * identity_key );
     207             : 
     208             : /* fd_repair_join joins the caller to the repair.  repair points to the
     209             :    first byte of the memory region backing the repair in the caller's
     210             :    address space.  Returns a pointer in the local address space to
     211             :    repair on success. */
     212             : 
     213             : fd_repair_t *
     214             : fd_repair_join( void * repair );
     215             : 
     216             : /* fd_repair_leave leaves a current local join.  Returns a pointer to
     217             :    the underlying shared memory region on success and NULL on failure
     218             :    (logs details).  Reasons for failure include repair is NULL. */
     219             : 
     220             : void *
     221             : fd_repair_leave( fd_repair_t const * repair );
     222             : 
     223             : /* fd_repair_delete unformats a memory region used as a repair.  Assumes
     224             :    only the nobody is joined to the region.  Returns a pointer to the
     225             :    underlying shared memory region or NULL if used obviously in error
     226             :    (e.g. repair is obviously not a repair ... logs details).  The
     227             :    ownership of the memory region is transferred to the caller. */
     228             : 
     229             : void *
     230             : fd_repair_delete( void * repair );
     231             : 
     232             : /* fd_repair_ping_de and fd_repair_ping_ser deserialize and serialize a
     233             :    ping message. */
     234             : int
     235             : fd_repair_ping_de( fd_repair_ping_t * ping,
     236             :                    uchar const      * buf,
     237             :                    ulong              buf_sz );
     238             : 
     239             : int
     240             : fd_repair_ping_ser( fd_repair_ping_t const * ping,
     241             :                     uchar                    buf[static sizeof(fd_repair_ping_t)],
     242             :                     ulong                    buf_sz );
     243             : 
     244             : /* fd_repair_{pong,shred,highest_shred,orphan} creates and returns a
     245             :    pointer to a serialized {Pong,Shred,HighestShred,Orphan} message.
     246             :    Does not require the caller to provide memory, as Repair itself
     247             :    maintains a dedicated memory region (repair->msg) for buffering
     248             :    requests.  Assumes repair->msg is not already buffering an existing
     249             :    request and can be overwritten.  Returns a pointer to repair->msg on
     250             :    success, NULL on failure. */
     251             : 
     252             : fd_repair_msg_t * fd_repair_pong         ( fd_repair_t * repair, fd_hash_t * ping_token );
     253             : 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 );
     254             : 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 );
     255             : fd_repair_msg_t * fd_repair_orphan       ( fd_repair_t * repair, fd_pubkey_t const * to, ulong ts, uint nonce, ulong slot );
     256             : 
     257             : /* fd_repair_sz returns the bincode-serialized sz of msg. */
     258             : 
     259             : static inline ulong
     260           0 : fd_repair_sz( fd_repair_msg_t const * msg ) {
     261           0 :    switch( msg->kind ) {
     262           0 :      case FD_REPAIR_KIND_PONG:          return sizeof(uint) + sizeof(fd_repair_pong_t);
     263           0 :      case FD_REPAIR_KIND_SHRED:         return sizeof(uint) + sizeof(fd_repair_shred_req_t);
     264           0 :      case FD_REPAIR_KIND_HIGHEST_SHRED: return sizeof(uint) + sizeof(fd_repair_highest_shred_req_t);
     265           0 :      case FD_REPAIR_KIND_ORPHAN:        return sizeof(uint) + sizeof(fd_repair_orphan_req_t);
     266           0 :      default:                           FD_LOG_ERR(( "Unhandled repair kind %u", msg->kind ));
     267           0 :    }
     268           0 : }
     269             : 
     270             : /* preimage_pong takes a pong and takes the token in a ping and a
     271             :    FD_REPAIR_PONG_PREIMAGE_SZ-byte buffer and returns a pointer to a
     272             :    preimage that can be signed. */
     273             : 
     274             : static inline uchar *
     275           0 : preimage_pong( fd_hash_t const * ping_token, uchar * preimage_buf, ulong preimage_sz ) {
     276           0 : # if FD_REPAIR_USE_HANDHOLDING
     277           0 :   if( FD_UNLIKELY( preimage_sz != FD_REPAIR_PONG_PREIMAGE_SZ ) ) {
     278           0 :     FD_LOG_ERR(( "preimage_sz %lu must be %lu", preimage_sz, FD_REPAIR_PONG_PREIMAGE_SZ ));
     279           0 :   }
     280           0 : # endif
     281           0 :   ulong prefix_sz = sizeof(FD_REPAIR_PONG_PREIMAGE_PREFIX) - 1 /* subtract NUL */;
     282           0 :   memcpy( preimage_buf,             FD_REPAIR_PONG_PREIMAGE_PREFIX, prefix_sz         );
     283           0 :   memcpy( preimage_buf + prefix_sz, ping_token,                     sizeof(fd_hash_t) );
     284           0 :   return preimage_buf;
     285           0 : }
     286             : 
     287             : /* preimage_req takes a repair request populated with all fields except
     288             :    for the signature, and returns a pointer to a preimage that can be
     289             :    signed.  Modifies the msg in place.
     290             : 
     291             :    At the start of this function, the repair_msg_t should contain
     292             : 
     293             :      [ discriminant ] [ empty sig ] [ repair request fields ]
     294             :      ^                ^             ^
     295             :      0                4             68
     296             : 
     297             :      https://github.com/solana-labs/solana/blob/master/core/src/repair/serve_repair.rs#L1258
     298             : 
     299             :      We want to sign over
     300             :      [ discriminant ] [ payload ]
     301             :       ^                ^
     302             :       buffer           buffer+4
     303             : 
     304             :    We can do this without using any extra memory copying the discriminant
     305             :    to the last 4 bytes of the sig field, and returning a pointer to
     306             :    that copied location.  The sig field should be overwritten with the
     307             :    actual signature value later, else the fd_repair_msg_t will be
     308             :    incorrect. */
     309             : 
     310             : static inline uchar *
     311           0 : preimage_req( fd_repair_msg_t * msg, ulong * preimage_sz ) {
     312           0 :   uchar * preimage = (uchar *)fd_type_pun(msg);
     313           0 :   preimage += sizeof(fd_ed25519_sig_t);
     314           0 :   FD_STORE( uint, preimage, msg->kind ); /* copy discriminant over */
     315           0 :   *preimage_sz = fd_repair_sz( msg ) - sizeof(fd_ed25519_sig_t);
     316           0 :   return preimage;
     317           0 : }
     318             : #endif /* HEADER_fd_src_discof_repair_fd_repair_h */

Generated by: LCOV version 1.14