LCOV - code coverage report
Current view: top level - disco/shred - fd_rnonce_ss.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 42 42 100.0 %
Date: 2026-09-17 04:28:31 Functions: 5 36 13.9 %

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

Generated by: LCOV version 1.14