LCOV - code coverage report
Current view: top level - disco/shred - fd_rnonce_ss.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 43 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 12 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_disco_shred_fd_rnonce_ss_h
       2             : #define HEADER_fd_src_disco_shred_fd_rnonce_ss_h
       3             : #include "../../util/fd_util.h"
       4             : 
       5             : /* fd_rnonce_ss_t is a strongly typed version of the 64 byte shared
       6             :    secret used to generate and verify nonces for repair requests and
       7             :    responses. */
       8             : union fd_rnonce_ss {
       9             :   uchar bytes[64];
      10             :   struct {
      11             :     ulong ss0[3];
      12             :     ulong slot;
      13             :     ulong ss1[1];
      14             :     uint  shred_idx;
      15             :     uint  ss2[2];
      16             :     uint  time;
      17             :     ulong ss3[1];
      18             :   } private;
      19             : };
      20             : typedef union fd_rnonce_ss fd_rnonce_ss_t;
      21             : FD_STATIC_ASSERT( sizeof(fd_rnonce_ss_t)==64, rnonce_ss );
      22             : 
      23             : /* FD_RNONCE_SS_DELTA_MAX is how many 2^24 ns ticks after it is computed
      24             :    a nonce still verifies, about 1 s. */
      25             : 
      26           0 : #define FD_RNONCE_SS_DELTA_MAX ((uint)((1000000000UL + (1UL<<24) - 1UL)/(1UL<<24)))  /* == 60 */
      27             : 
      28             : FD_PROTOTYPES_BEGIN
      29             : 
      30             : FD_FN_CONST static inline int
      31           0 : fd_rnonce_ss_normal_repair( uint nonce ) { return (int)(nonce>>31); }
      32             : 
      33             : /* fd_rnonce_ss_{compute,verify} compute and verify, respectively, the
      34             :    nonce for the specified repair request issued or received at time_ns.
      35             :    slot and shred_idx specify the slot and shred index of the
      36             :    requested/received shred.  slot_complete must be non-zero if the
      37             :    received shred is the last in the slot.  normal_repair must be
      38             :    non-zero if the request is a "normal" repair request, i.e., one for a
      39             :    specific shred index.  If normal_repair is zero, shred_idx is
      40             :    ignored, slot is adjusted (see below), and slot_complete must be
      41             :    non-zero in the verify function. ss is a pointer to the shared secret
      42             :    value.  ss_compute returns the value of the nonce.  ss_verify takes
      43             :    the supposed value of the nonce in the nonce parameter.  ss_verify
      44             :    returns 1 if the nonce is correct and 0 otherwise.
      45             : 
      46             :    These satisfy:
      47             :    1==ss_verify( ss_v, ss_compute( ss_c, 1, slot_c, shred_idx_c, rq_time ), slot_v, shred_idx_v, slot_complete, rs_time )
      48             :    when
      49             :       ss_v        == ss_c,
      50             :       slot_v      == slot_c,
      51             :       shred_idx_v == shred_idx_c, AND
      52             :       rq_time <= rs_time < rq_time + 1.02 seconds.
      53             :    When any of these of these conditions is false, it should return 0
      54             :    with high probability, approx (1 - 2^25).
      55             : 
      56             :    And
      57             :    1==ss_verify( ss_v, ss_compute( ss_c, 0, slot_c, shred_idx_c, rq_time ), slot_v, shred_idx_v, slot_complete, rs_time )
      58             :    when
      59             :       ss_v          == ss_c,
      60             :       slot_complete != 0,
      61             :       -1 <= floor(slot_v/128) - floor(slot_c/128) <= 0,  which is looser than slot_c - 128 <= slot_v <= slot_c, AND
      62             :       rq_time <= rs_time < rq_time + 1.02 seconds.
      63             :    When any of these of these conditions is false, it should return 0
      64             :    with high probability, approx (1 - 2^25).
      65             : */
      66             : static inline uint
      67             : fd_rnonce_ss_compute( fd_rnonce_ss_t const * ss,
      68             :                       int                    normal_repair,
      69             :                       ulong                  slot,
      70             :                       uint                   shred_idx,
      71           0 :                       long                   time_ns ) {
      72           0 :   fd_rnonce_ss_t temp[1] = { *ss };
      73             :   /* truncate time down to intervals of 2^32 ns, which is ~4 seconds. */
      74           0 :   temp->private.time      = (uint)(time_ns>>32);
      75           0 :   temp->private.slot      = fd_ulong_if( normal_repair, slot,      slot/128UL );
      76           0 :   temp->private.shred_idx = fd_uint_if ( normal_repair, shred_idx, 0U         );
      77             :   /* seed is fractional part of sqrt(17) */
      78             :   /* Then we add back in time_ns>>24 (truncated to 16ms intervals).
      79             :      This is kind of surprising, but it means that we can generate a new
      80             :      nonce when we re-request a specific shred, but we don't need to
      81             :      compute a ton of hashes. */
      82           0 :   return (uint)(
      83           0 :     fd_ulong_if( normal_repair, 0x80000000UL, 0UL ) |
      84           0 :     (0x7FFFFFFFUL & (fd_hash( 2270897969802886507UL, temp, sizeof(temp) ) + (((ulong)time_ns)>>24) ) ) );
      85           0 : }
      86             : 
      87             : static inline int
      88             : fd_rnonce_ss_verify( fd_rnonce_ss_t const * ss,
      89             :                      uint                   nonce,
      90             :                      ulong                  slot,
      91             :                      uint                   shred_idx,
      92             :                      int                    slot_complete,
      93           0 :                      long                   time_ns ) {
      94           0 :   fd_rnonce_ss_t temp[1] = { *ss };
      95           0 :   int normal_repair      = fd_rnonce_ss_normal_repair( nonce );
      96             : 
      97             :   /* If it's not "normal" repair, then the shred must have slot
      98             :      complete.  Technically this is not required by the repair protocol,
      99             :      so we may occasionally reject an honest response here when the
     100             :      responder doesn't have the last shred in the slot, but this is good
     101             :      defense in depth.  If none of the repair peers have the last shred
     102             :      in the slot, then it's unlikely the cluster has confirmed it. */
     103           0 :   if( FD_UNLIKELY( (!normal_repair) & (!slot_complete) ) ) return 0;
     104             : 
     105           0 :   temp->private.time      = (uint)(time_ns>>32);
     106           0 :   temp->private.slot      = fd_ulong_if( normal_repair, slot,      slot/128UL );
     107           0 :   temp->private.shred_idx = fd_uint_if ( normal_repair, shred_idx, 0U         );
     108           0 : #define ALLOWED_TIME_DELTA FD_RNONCE_SS_DELTA_MAX
     109             : 
     110           0 : #define CHECKN( temp ) do{ if( FD_LIKELY(                                                                                   \
     111           0 :                          ( (fd_hash( 2270897969802886507UL, temp, sizeof(temp) ) + (((ulong)time_ns)>>24) - (ulong)nonce) & \
     112           0 :                            0x7FFFFFFFUL ) <= ALLOWED_TIME_DELTA ) )                                                         \
     113           0 :                              return 1;                                                                                      \
     114           0 :                        } while( 0 )
     115             : 
     116             : 
     117           0 :   CHECKN( temp );
     118             : 
     119           0 :   int try_prev_time = ((time_ns-1000000000L)>>32) != (time_ns>>32);
     120           0 :   if( try_prev_time ) {
     121             :     /* If that doesn't match, check the previous time bucket. */
     122           0 :     temp->private.time--;
     123           0 :     CHECKN( temp );
     124           0 :     temp->private.time++;
     125           0 :   }
     126             : 
     127           0 :   if( FD_UNLIKELY( !normal_repair ) ) {
     128             :     /* Check the next slot bucket */
     129           0 :     temp->private.slot++;
     130           0 :     CHECKN( temp );
     131           0 :     if( try_prev_time ) {
     132             :       /* And check it with the prev time bucket */
     133           0 :       temp->private.time--;
     134           0 :       CHECKN( temp );
     135           0 :     }
     136           0 :   }
     137           0 : #undef CHECKN
     138           0 : #undef ALLOWED_TIME_DELTA
     139           0 :   return 0;
     140           0 : }
     141             : 
     142             : FD_PROTOTYPES_END
     143             : 
     144             : #endif /* HEADER_fd_src_disco_shred_fd_rnonce_ss_h */

Generated by: LCOV version 1.14