LCOV - code coverage report
Current view: top level - disco/repair - fd_repair.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 777 791 98.2 %
Date: 2026-11-12 04:25:03 Functions: 33 35 94.3 %

          Line data    Source code
       1             : #include "fd_repair.h"
       2             : #include "../../ballet/bmtree/fd_bmtree.h"
       3             : #include "../../ballet/sha256/fd_sha256.h"
       4             : #include "../../flamenco/alpenglow/fd_block_marker_serde.h"
       5             : #include "../../disco/events/generated/fd_event_gen.h"
       6             : 
       7             : int
       8             : fd_repair_blk_treap_insert( fd_repair_t *     self,
       9      115929 :                             fd_repair_blk_t * blk ) {
      10      115929 :   fd_repair_slot_meta_t const * meta  = fd_repair_slot_meta( self, blk->slot );
      11      115929 :   ulong                         idx   = fd_repair_blk_pool_idx( self->blk_pool, blk );
      12      115929 :   fd_repair_treap_t *           treap = meta->final_blk_idx==idx ? self->final_treap : meta->notar_blk_idx==idx ? self->notar_treap : self->eager_treap;
      13      115929 :   if( FD_UNLIKELY( fd_repair_treap_ele_query_const( treap, blk, self->blk_pool )==blk ) ) return 0;
      14       62493 :   if( FD_UNLIKELY( meta->final_blk_idx!=idx && meta->notar_blk_idx!=idx && meta->eager_blk_idx==idx && !blk->eager ) ) return 0;
      15       62478 :   fd_repair_treap_ele_insert( treap, blk, self->blk_pool );
      16       62478 :   return 1;
      17       62493 : }
      18             : 
      19             : static int
      20             : blk_treap_remove( fd_repair_t *     self,
      21       16179 :                   fd_repair_blk_t * blk ) {
      22       16179 :   fd_repair_slot_meta_t const * meta  = fd_repair_slot_meta( self, blk->slot );
      23       16179 :   ulong                         idx   = fd_repair_blk_pool_idx( self->blk_pool, blk );
      24       16179 :   fd_repair_treap_t *           treap = meta->final_blk_idx==idx ? self->final_treap : meta->notar_blk_idx==idx ? self->notar_treap : self->eager_treap;
      25       16179 :   if( FD_LIKELY( fd_repair_treap_ele_query_const( treap, blk, self->blk_pool )!=blk ) ) return 0;
      26       16053 :   fd_repair_treap_ele_remove( treap, blk, self->blk_pool );
      27       16053 :   return 1;
      28       16179 : }
      29             : 
      30             : static fd_repair_blk_t *
      31             : blk_insert( fd_repair_t *     self,
      32             :             ulong             slot,
      33       62337 :             fd_mr32_t const * dmr ) {
      34       62337 :   FD_TEST( fd_repair_blk_pool_free( self->blk_pool ) );
      35       62337 :   fd_repair_slot_meta_t *       meta = fd_repair_slot_meta( self, slot );
      36       62337 :   fd_repair_blk_t *             blk  = fd_repair_blk_pool_ele_acquire( self->blk_pool );
      37       62337 :   blk->slot             = slot;
      38       62337 :   blk->dmr              = *dmr;
      39       62337 :   blk->parent           = fd_repair_blk_pool_idx_null( self->blk_pool );
      40       62337 :   blk->child            = fd_repair_blk_pool_idx_null( self->blk_pool );
      41       62337 :   blk->sibling          = fd_repair_blk_pool_idx_null( self->blk_pool );
      42       62337 :   blk->cmpl_fec_cnt     = 0U;
      43       62337 :   blk->rcvd_fec_cnt     = 0U;
      44       62337 :   blk->buff_fec_cnt     = 0U;
      45       62337 :   blk->cons_fec_cnt     = 0U;
      46       62337 :   blk->wait_fec_cnt     = 0U;
      47       62337 :   blk->parent_slot      = ULONG_MAX;
      48       62337 :   blk->parent_dmr       = (fd_mr32_t){ { 0 } };
      49       62337 :   blk->init_fec_cnt     = 0U;
      50       62337 :   blk->init_parent_slot = ULONG_MAX;
      51             : 
      52       62337 :   blk->eager         = (!memcmp( dmr, &hash_null, sizeof(fd_mr32_t)) && !meta->invalidated && meta->final_blk_idx==fd_repair_blk_pool_idx_null( self->blk_pool ) && !meta->leader );
      53       62337 :   blk->meta_req      = 0;
      54       62337 :   blk->highest_req   = 0;
      55       62337 :   blk->orphan_req    = 0;
      56       62337 :   memset( &blk->telemetry, 0, sizeof(blk->telemetry) );
      57       62337 :   blk->telemetry.last_cmpl_fec_idx = UINT_MAX;
      58       62337 :   blk->eager_ts      = 0L;
      59    31988097 :   for( ulong k=0UL; k<self->fec_blk_max; k++ ) blk->fecs[ k ] = fd_repair_fec_pool_idx_null( self->fec_pool );
      60       62337 :   fd_repair_blk_map_ele_insert( self->blk_map, blk, self->blk_pool );
      61       62337 :   fd_repair_blk_orphan_map_ele_insert( self->orphan_map, blk, self->blk_pool );
      62       62337 :   if     ( !memcmp( dmr, &hash_null, sizeof(fd_mr32_t) ) )               meta->eager_blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk );
      63       61332 :   else if( meta->notar_blk_idx==fd_repair_blk_pool_idx_null( self->blk_pool ) ) meta->notar_blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk );
      64       62337 :   fd_repair_blk_treap_insert( self, blk );
      65       62337 :   return blk;
      66       62337 : }
      67             : 
      68             : static void
      69             : compute_dmr( fd_repair_t *     self,
      70         684 :              fd_repair_blk_t * blk ) {
      71         684 :   uint fec_set_cnt = blk->cmpl_fec_cnt;
      72             : 
      73         684 :   uchar tree_mem[ FD_BMTREE_COMMIT_FOOTPRINT( 0UL ) ] __attribute__((aligned(FD_BMTREE_COMMIT_ALIGN)));
      74         684 :   fd_bmtree_commit_t * tree = fd_bmtree_commit_init( tree_mem, FD_SHRED_MERKLE_NODE_SZ, FD_BMTREE_LONG_PREFIX_SZ, 0UL );
      75        7812 :   for( uint k=0U; k<fec_set_cnt; k++ ) {
      76        7128 :     fd_bmtree_node_t leaf[1];
      77        7128 :     memcpy( leaf->hash, fd_repair_fec_pool_ele( self->fec_pool, blk->fecs[ k ] )->mr.mr32.uc, sizeof(fd_mr32_t) );
      78        7128 :     fd_bmtree_commit_append( tree, leaf, 1UL );
      79        7128 :   }
      80             : 
      81         684 :   fd_bmtree_node_t parent_info[1];
      82         684 :   fd_sha256_t      sha[1];
      83         684 :   fd_sha256_init  ( sha );
      84         684 :   fd_sha256_append( sha, &blk->parent_slot,      sizeof(ulong)     );
      85         684 :   fd_sha256_append( sha, blk->parent_dmr.uc,     sizeof(fd_mr32_t) );
      86         684 :   fd_sha256_append( sha, &fec_set_cnt,           sizeof(uint)      );
      87         684 :   fd_sha256_fini  ( sha, parent_info->hash );
      88         684 :   fd_bmtree_commit_append( tree, parent_info, 1UL );
      89             : 
      90         684 :   memcpy( blk->dmr.uc, fd_bmtree_commit_fini( tree ), sizeof(fd_mr32_t) );
      91         684 : }
      92             : 
      93             : static void
      94             : consume( fd_repair_t *     self,
      95      192204 :          fd_repair_blk_t * blk ) {
      96      192204 :   fd_repair_slot_meta_t const * meta = fd_repair_slot_meta( self, blk->slot );
      97      192204 :   if( FD_UNLIKELY( meta->invalidated && !memcmp( &blk->dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) return;
      98      192147 :   if( FD_UNLIKELY( blk->parent==fd_repair_blk_pool_idx_null( self->blk_pool ) ) ) return; /* orphaned, even if partly consumed */
      99      131202 :   if( FD_UNLIKELY( blk->cons_fec_cnt<=blk->init_fec_cnt ) ) {
     100      104781 :     fd_repair_blk_t const * parent = fd_repair_blk_pool_ele_const( self->blk_pool, blk->parent );
     101      104781 :     if( FD_LIKELY( parent!=self->root_blk && ( !parent->cmpl_fec_cnt || parent->cons_fec_cnt!=parent->cmpl_fec_cnt ) ) ) return;
     102      104781 :   }
     103       92646 :   ulong             idx   = fd_repair_blk_pool_idx( self->blk_pool, blk );
     104       92646 :   fd_repair_blk_t * eager = fd_repair_blk_pool_ele( self->blk_pool, meta->eager_blk_idx );
     105       92646 :   if( FD_UNLIKELY(  eager && meta->eager_blk_idx!=idx && !meta->invalidated && !memcmp( &eager->dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     106          51 :     int dup = eager->parent==blk->parent;
     107          69 :     for( uint k=blk->cons_fec_cnt; dup && k<blk->buff_fec_cnt; k++ ) dup = eager->fecs[ k ]==blk->fecs[ k ];
     108          51 :     if( FD_LIKELY( dup ) ) {
     109          36 :       blk = eager;
     110          36 :       idx = meta->eager_blk_idx;
     111          36 :     }
     112          51 :   }
     113             : 
     114       92646 :   if( FD_UNLIKELY( self->reconsume && blk->cons_fec_cnt<blk->buff_fec_cnt ) ) { /* replay evicted, need to specially redeliver from the root */
     115          27 :     self->reconsume = 0;
     116          87 :     for( fd_repair_blk_t * anc = blk; anc && anc!=self->root_blk; anc = fd_repair_blk_pool_ele( self->blk_pool, anc->parent ) ) {
     117         114 :       for( uint k=anc->cons_fec_cnt; k>anc->init_fec_cnt; k-- ) fd_repair_deque_push_head( self->reasm_deque, (fd_repair_deque_t){ .blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, anc ), .fec_idx = k-1U } );
     118          60 :     }
     119          27 :   }
     120       92646 :   if( FD_UNLIKELY( meta->leader && meta->eager_blk_idx==idx ) ) { /* our leader blk goes first, in FEC order */
     121          33 :     for( uint k=blk->buff_fec_cnt; k>blk->cons_fec_cnt; k-- ) fd_repair_deque_push_head( self->reasm_deque, (fd_repair_deque_t){ .blk_idx = (uint)idx, .fec_idx = k-1U } );
     122          18 :     blk->cons_fec_cnt = blk->buff_fec_cnt;
     123          18 :     return;
     124          18 :   }
     125      160266 :   for( ; blk->cons_fec_cnt<blk->buff_fec_cnt; blk->cons_fec_cnt++ ) fd_repair_deque_push_tail( self->reasm_deque, (fd_repair_deque_t){ .blk_idx = (uint)idx, .fec_idx = blk->cons_fec_cnt } );
     126       92628 : }
     127             : 
     128             : static void
     129             : cascade( fd_repair_t *     self,
     130       32478 :          fd_repair_blk_t * root ) {
     131       32478 :   fd_repair_blk_t * pool = self->blk_pool;
     132       32478 :   ulong             null = fd_repair_blk_pool_idx_null( pool );
     133       32478 :   ulong             tidx = fd_repair_blk_pool_idx( pool, root );
     134       32478 :   ulong             idx  = root->child;
     135       42615 :   while( FD_LIKELY( idx!=null ) ) {
     136       33087 :     fd_repair_blk_t * blk = fd_repair_blk_pool_ele( pool, idx );
     137       33087 :     consume( self, blk );
     138       33087 :     if( FD_LIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt && blk->child!=null ) ) { idx = blk->child; continue; }
     139       56037 :     while( FD_LIKELY( idx!=tidx && fd_repair_blk_pool_ele( pool, idx )->sibling==null ) ) idx = fd_repair_blk_pool_ele( pool, idx )->parent;
     140       22989 :     if( FD_UNLIKELY( idx==tidx ) ) break;
     141          39 :     idx = fd_repair_blk_pool_ele( pool, idx )->sibling;
     142          39 :   }
     143       32478 : }
     144             : 
     145             : static void
     146             : promote( fd_repair_t *     self,
     147             :          fd_repair_blk_t * blk,
     148         168 :          uint *            role ) {
     149         168 :   if( FD_LIKELY( *role==fd_repair_blk_pool_idx( self->blk_pool, blk ) ) ) return;
     150         153 :   int queued = blk_treap_remove( self, blk );
     151         153 :   *role      = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk );
     152         153 :   blk->eager = 0;
     153         153 :   if( FD_LIKELY( queued ) ) fd_repair_blk_treap_insert( self, blk );
     154         153 : }
     155             : 
     156             : static fd_repair_blk_t *
     157             : link_parent( fd_repair_t *     self,
     158       46473 :              fd_repair_blk_t * blk ) {
     159       46473 :   fd_repair_blk_t * pool = self->blk_pool;
     160       46473 :   if( FD_UNLIKELY( blk->parent_slot==ULONG_MAX ) ) return NULL;
     161       46473 :   if( FD_UNLIKELY( blk->parent_slot!=self->root && !memcmp( &blk->parent_dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) return NULL;
     162             : 
     163       46467 :   fd_repair_blk_t * parent = fd_repair_blk_map_ele_query( self->blk_map, &blk->parent_slot, NULL, pool );
     164       46611 :   while( parent && memcmp( &parent->dmr, &blk->parent_dmr, sizeof(fd_mr32_t) ) ) parent = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( parent, NULL, pool );
     165       46467 :   if( FD_LIKELY( !parent ) ) return NULL;
     166             : 
     167       46164 :   if( FD_LIKELY( blk!=self->root_blk ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, pool );
     168             : 
     169       46164 :   blk->parent   = fd_repair_blk_pool_idx( pool, parent );
     170       46164 :   blk->sibling  = parent->child;
     171       46164 :   parent->child = fd_repair_blk_pool_idx( pool, blk );
     172       46164 :   return parent;
     173       46467 : }
     174             : 
     175             : static void
     176             : link_ancestors( fd_repair_t *     self,
     177       45699 :                 fd_repair_blk_t * blk ) {
     178       45699 :   fd_repair_blk_t * pool = self->blk_pool;
     179       45699 :   ulong             null = fd_repair_blk_pool_idx_null( pool );
     180       45699 :   fd_repair_blk_t * top  = blk;
     181       66042 :   for(;;) {
     182       66042 :     if( FD_UNLIKELY( blk->parent_slot==ULONG_MAX || blk->parent_slot<self->root ) ) break;
     183       20343 :     if( FD_UNLIKELY( blk->parent_slot!=self->root && !memcmp( &blk->parent_dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) break;
     184       20343 :     fd_repair_slot_meta_t * meta     = fd_repair_slot_meta( self, blk->slot );
     185       20343 :     fd_repair_slot_meta_t * pmeta    = fd_repair_slot_meta( self, blk->parent_slot );
     186       20343 :     int                     final    = meta->final_blk_idx==fd_repair_blk_pool_idx( pool, blk );
     187       20343 :     int                     unlinked = blk->parent==null;
     188       20343 :     fd_repair_blk_t *       parent   = unlinked ? link_parent( self, blk ) : fd_repair_blk_pool_ele( pool, blk->parent );
     189       20343 :     if( FD_UNLIKELY( parent && unlinked ) ) top = blk;
     190       20343 :     if( FD_UNLIKELY( !parent ) ) {
     191          12 :       if( FD_UNLIKELY( blk->parent_slot==self->root ) ) break; /* names a root other than ours */
     192          12 :       parent = fd_repair_blk_notarized( self, blk->parent_slot, &blk->parent_dmr ); /* link_orphans links blk under it */
     193          12 :       if( FD_UNLIKELY( !parent ) ) break; /* the parent slot is settled otherwise */
     194          12 :       fd_repair_blk_treap_insert( self, blk ); /* its tile asks ORPHAN for eager ancestry, which may merge with the named parents */
     195          12 :       top = blk;
     196          12 :     }
     197       20343 :     if( FD_UNLIKELY( final && pmeta->final_blk_idx==null ) ) promote( self, parent, &pmeta->final_blk_idx );
     198       20343 :     blk = parent;
     199       20343 :   }
     200       45699 :   consume( self, top );
     201       45699 :   if( FD_UNLIKELY( top->cmpl_fec_cnt && top->cons_fec_cnt==top->cmpl_fec_cnt ) ) cascade( self, top );
     202       45699 : }
     203             : 
     204             : static void
     205             : link_orphans( fd_repair_t *     self,
     206       46374 :               fd_repair_blk_t * parent ) {
     207       46374 :   fd_repair_blk_t *       pool  = self->blk_pool;
     208       46374 :   ulong                   null  = fd_repair_blk_pool_idx_null( pool );
     209       46374 :   fd_repair_slot_meta_t * pmeta = fd_repair_slot_meta( self, parent->slot );
     210       46374 :   fd_repair_blk_t *       next;
     211       46374 :   for( fd_repair_blk_t * blk = fd_repair_blk_orphan_map_ele_query( self->orphan_map, &parent->dmr, NULL, pool );
     212       46554 :                          blk;
     213       46374 :                          blk = next ) {
     214         180 :     next = (fd_repair_blk_t *)fd_repair_blk_orphan_map_ele_next_const( blk, NULL, pool );
     215         180 :     if( FD_UNLIKELY( blk->parent_slot!=parent->slot ) ) continue;
     216         177 :     fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, pool );
     217         177 :     blk->parent   = fd_repair_blk_pool_idx( pool, parent );
     218         177 :     blk->sibling  = parent->child;
     219         177 :     parent->child = fd_repair_blk_pool_idx( pool, blk );
     220         177 :     if( FD_UNLIKELY( fd_repair_slot_meta( self, blk->slot )->final_blk_idx==fd_repair_blk_pool_idx( pool, blk ) && pmeta->final_blk_idx==null ) ) promote( self, parent, &pmeta->final_blk_idx );
     221         177 :   }
     222       46374 : }
     223             : 
     224             : static void
     225             : unlink( fd_repair_t *     self,
     226         159 :         fd_repair_blk_t * blk ) {
     227         159 :   fd_repair_blk_t * pool = self->blk_pool;
     228         159 :   ulong             null = fd_repair_blk_pool_idx_null( pool );
     229         159 :   ulong             idx  = fd_repair_blk_pool_idx( pool, blk );
     230         159 :   if( FD_LIKELY( blk->parent!=null ) ) {
     231          54 :     ulong * link = &fd_repair_blk_pool_ele( pool, blk->parent )->child;
     232          84 :     while( *link!=idx ) link = &fd_repair_blk_pool_ele( pool, *link )->sibling;
     233          54 :     *link = blk->sibling;
     234          54 :   }
     235         159 :   ulong next;
     236         201 :   for( ulong c=blk->child; c!=null; c=next ) {
     237          42 :     fd_repair_blk_t * child = fd_repair_blk_pool_ele( pool, c );
     238          42 :     next            = child->sibling;
     239          42 :     child->parent   = null;
     240          42 :     child->sibling  = null;
     241          42 :     if( FD_LIKELY( child!=self->root_blk ) ) fd_repair_blk_orphan_map_ele_insert( self->orphan_map, child, pool ); /* waits for blk's dmr again, should it be held again */
     242          42 :   }
     243         159 : }
     244             : 
     245             : static void
     246             : prune( fd_repair_t *     self,
     247          84 :        fd_repair_blk_t * blk ) {
     248          84 :   fd_repair_blk_t * pool = self->blk_pool;
     249          84 :   if( FD_UNLIKELY( !blk->telemetry.reported && self->telemetry.report ) ) self->telemetry.report( self->telemetry.ctx, blk );
     250       43092 :   for( ulong k=0UL; k<self->fec_blk_max; k++ ) {
     251       43008 :     if( FD_LIKELY( blk->fecs[ k ]==fd_repair_fec_pool_idx_null( self->fec_pool ) ) ) continue;
     252          57 :     int held = 0;
     253          57 :     for( fd_repair_blk_t const * other = fd_repair_blk_map_ele_query_const( self->blk_map, &blk->slot, NULL, pool );
     254         159 :                                  other && !held;
     255         102 :                                  other = fd_repair_blk_map_ele_next_const( other, NULL, pool ) ) {
     256         102 :       held = other!=blk && other->fecs[ k ]==blk->fecs[ k ];
     257         102 :     }
     258          57 :     if( FD_UNLIKELY( held ) ) continue;
     259          36 :     fd_repair_fec_t * fec = fd_repair_fec_pool_ele( self->fec_pool, blk->fecs[ k ] );
     260          36 :     fd_repair_fec_map_ele_remove_fast( self->fec_map, fec, self->fec_pool );
     261          36 :     fd_repair_fec_pool_ele_release   ( self->fec_pool, fec );
     262          36 :   }
     263          84 :   unlink( self, blk );
     264          84 :   if( FD_UNLIKELY( blk!=self->root_blk && blk->parent==fd_repair_blk_pool_idx_null( pool ) ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, pool );
     265          84 :   blk_treap_remove( self, blk );
     266          84 :   fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, blk->slot );
     267          84 :   if( FD_UNLIKELY( meta->eager_blk_idx==fd_repair_blk_pool_idx( pool, blk ) ) ) meta->eager_blk_idx = (uint)fd_repair_blk_pool_idx_null( pool );
     268          84 :   if( FD_UNLIKELY( meta->notar_blk_idx==fd_repair_blk_pool_idx( pool, blk ) ) ) meta->notar_blk_idx = (uint)fd_repair_blk_pool_idx_null( pool );
     269          84 :   if( FD_UNLIKELY( meta->final_blk_idx==fd_repair_blk_pool_idx( pool, blk ) ) ) meta->final_blk_idx = (uint)fd_repair_blk_pool_idx_null( pool );
     270          84 :   fd_repair_blk_map_ele_remove_fast( self->blk_map, blk, pool );
     271          84 :   fd_repair_blk_pool_ele_release   ( pool, blk );
     272          84 : }
     273             : 
     274             : /* dedup checks for a duplicate of eager among the notar blks.  Only the
     275             :    eager blk can be duplicated, since notar blk DMRs are known a priori.
     276             : 
     277             :    Merge strategy if a duplicate is found:
     278             :    - eager is kept, notar is freed
     279             :    - eager inherits notar's children
     280             :    - eager takes notar's parent if it has none, as same DMR implies same
     281             :      parent
     282             : 
     283             :    Replay builds one bank for the block until then: consume holds notar
     284             :    behind eager, which delivers the FEC sets they share. */
     285             : 
     286             : static void
     287             : dedup( fd_repair_t *     self,
     288             :        fd_repair_blk_t * eager,
     289         684 :        long              ts ) {
     290         684 :   fd_repair_blk_t * pool = self->blk_pool;
     291         684 :   ulong             null = fd_repair_blk_pool_idx_null( pool );
     292         684 :   ulong             idx  = fd_repair_blk_pool_idx( pool, eager );
     293             : 
     294         684 :   fd_repair_blk_t * notar = fd_repair_blk_map_ele_query( self->blk_map, &eager->slot, NULL, pool );
     295        1416 :   while( notar && ( notar==eager || memcmp( &notar->dmr, &eager->dmr, sizeof(fd_mr32_t) ) ) ) {
     296         732 :     notar = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( notar, NULL, pool );
     297         732 :   }
     298         684 :   fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, eager->slot );
     299         684 :   if( FD_UNLIKELY( !notar && meta->final_blk_idx!=null ) ) {
     300           3 :     eager->telemetry.cancelled_ts     = fd_long_if( !eager->telemetry.cancelled_reason, ts, eager->telemetry.cancelled_ts );
     301           3 :     eager->telemetry.cancelled_reason = (uchar)FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_NOTARIZED_VERSION;
     302           3 :     prune( self, eager );
     303           3 :     return;
     304           3 :   } /* turbine built a blk the cluster did not finalize */
     305         681 :   if( FD_LIKELY( !notar ) ) return;
     306             : 
     307          33 :   ulong next;
     308          54 :   for( ulong c=notar->child; c!=null; c=next ) {
     309          21 :     next                                       = fd_repair_blk_pool_ele( pool, c )->sibling;
     310          21 :     fd_repair_blk_pool_ele( pool, c )->parent  = idx;
     311          21 :     fd_repair_blk_pool_ele( pool, c )->sibling = eager->child;
     312          21 :     eager->child                               = c;
     313          21 :   }
     314          33 :   notar->child = null;
     315          33 :   if( FD_UNLIKELY( notar->parent!=null && eager->parent==null ) ) {
     316           0 :     if( FD_LIKELY( eager!=self->root_blk ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, eager, pool );
     317           0 :     eager->parent                                        = notar->parent;
     318           0 :     eager->sibling                                       = fd_repair_blk_pool_ele( pool, notar->parent )->child;
     319           0 :     fd_repair_blk_pool_ele( pool, notar->parent )->child = idx;
     320           0 :   }
     321          33 :   blk_treap_remove( self, notar );
     322          33 :   if( FD_UNLIKELY( meta->final_blk_idx==fd_repair_blk_pool_idx( pool, notar ) ) ) { meta->final_blk_idx = (uint)null; promote( self, eager, &meta->final_blk_idx ); }
     323          33 :   if( FD_UNLIKELY( meta->notar_blk_idx==fd_repair_blk_pool_idx( pool, notar ) ) ) { meta->notar_blk_idx = (uint)null; promote( self, eager, &meta->notar_blk_idx ); }
     324          33 :   notar->telemetry.reported = 1; /* the same block lives on as eager, we won't report two different lines for this */
     325             : 
     326          33 :   eager->telemetry.req_window_cnt    += notar->telemetry.req_window_cnt;
     327          33 :   eager->telemetry.req_highest_cnt   += notar->telemetry.req_highest_cnt;
     328          33 :   eager->telemetry.req_orphan_cnt    += notar->telemetry.req_orphan_cnt;
     329          33 :   eager->telemetry.req_shred_bid_cnt += notar->telemetry.req_shred_bid_cnt;
     330          33 :   eager->telemetry.req_parent_cnt    += notar->telemetry.req_parent_cnt;
     331          33 :   eager->telemetry.req_fec_root_cnt  += notar->telemetry.req_fec_root_cnt;
     332          33 :   eager->telemetry.shred_res_cnt     += notar->telemetry.shred_res_cnt;
     333          33 :   eager->telemetry.parent_res_cnt    += notar->telemetry.parent_res_cnt;
     334          33 :   eager->telemetry.fec_root_res_cnt  += notar->telemetry.fec_root_res_cnt;
     335             :   /* first timestamps */
     336          33 :   eager->telemetry.first_req_ts       = fd_long_if( !eager->telemetry.first_req_ts      || ( notar->telemetry.first_req_ts      && notar->telemetry.first_req_ts     <eager->telemetry.first_req_ts      ), notar->telemetry.first_req_ts,      eager->telemetry.first_req_ts      );
     337          33 :   eager->telemetry.first_meta_res_ts  = fd_long_if( !eager->telemetry.first_meta_res_ts || ( notar->telemetry.first_meta_res_ts && notar->telemetry.first_meta_res_ts<eager->telemetry.first_meta_res_ts ), notar->telemetry.first_meta_res_ts, eager->telemetry.first_meta_res_ts );
     338          33 :   eager->telemetry.last_shred_res_ts  = fd_long_max( eager->telemetry.last_shred_res_ts, notar->telemetry.last_shred_res_ts );
     339          33 :   prune( self, notar );
     340          33 :   link_ancestors( self, eager );
     341          33 : }
     342             : 
     343             : static void
     344             : update_parent( fd_repair_t * self,
     345             :                ulong         slot,
     346             :                uint          fec_idx,
     347         384 :                long          ts ) {
     348         384 :   if( FD_UNLIKELY( !fec_idx || fec_idx>=self->fec_blk_max ) ) return;
     349         384 :   fd_repair_slot_meta_t * meta  = fd_repair_slot_meta( self, slot );
     350         384 :   fd_repair_blk_t *       pool  = self->blk_pool;
     351         384 :   ulong                   null  = fd_repair_blk_pool_idx_null( pool );
     352         384 :   fd_repair_blk_t * next;
     353         792 :   for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, pool ); blk; blk = next ) {
     354         408 :     next = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, pool );
     355             : 
     356         408 :     if( FD_LIKELY( blk->fecs[ fec_idx-1U ]==fd_repair_fec_pool_idx_null( self->fec_pool ) || blk->fecs[ fec_idx ]==fd_repair_fec_pool_idx_null( self->fec_pool ) ) ) continue;
     357         198 :     fd_repair_fec_t const * prev_fec = fd_repair_fec_pool_ele_const( self->fec_pool, blk->fecs[ fec_idx-1U ] );
     358         198 :     fd_repair_fec_t const * next_fec = fd_repair_fec_pool_ele_const( self->fec_pool, blk->fecs[ fec_idx    ] );
     359             : 
     360         198 :     if( FD_LIKELY( !prev_fec->data_complete || next_fec->update_parent.new_parent_slot==ULONG_MAX ) ) continue;
     361         186 :     if( FD_UNLIKELY( blk->init_fec_cnt==fec_idx ) ) continue;
     362             : 
     363         129 :     fd_update_parent_t const * update = &next_fec->update_parent;
     364         129 :     int                        eager  = meta->eager_blk_idx==fd_repair_blk_pool_idx( pool, blk );
     365         129 :     if( FD_UNLIKELY( eager && meta->invalidated ) ) continue;
     366             : 
     367         129 :     int bad = !fd_is_start_of_window( slot )                                            /* not in the first slot of its leader window */
     368         129 :               || blk->init_fec_cnt                                                      /* a second UpdateParent */
     369         129 :               || update->new_parent_slot<self->root                                     /* a parent below the root */
     370         129 :               || update->new_parent_slot>=slot                                          /* a parent newer than the header's */
     371         129 :               || ( eager && update->new_parent_slot>blk->init_parent_slot )             /* a parent newer than the header's */
     372         129 :               || !memcmp( &update->new_parent_block_id, &hash_null, sizeof(fd_mr32_t) ) /* a null parent block id */
     373         129 :               || ( eager && update->new_parent_slot==blk->parent_slot && !memcmp( &update->new_parent_block_id, &blk->parent_dmr, sizeof(fd_mr32_t) ) ); /* updating to the same thing */
     374             : 
     375             :     /* TODO: mark notarize blks invalid? */
     376         129 :     if( FD_UNLIKELY( bad && !eager ) ) { FD_LOG_WARNING(( "notarized blk at slot %lu has an invalid UpdateParent at fec set idx %u, ignoring it", slot, fec_idx*FD_FEC_SHRED_CNT )); continue; }
     377         129 :     if( FD_UNLIKELY( bad &&  eager ) ) { fd_repair_slot_invalidated( self, slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_INVALID_UPDATE_PARENT, ts ); continue; }
     378             : 
     379         108 :     if( FD_UNLIKELY( !eager ) ) {
     380          12 :       if( FD_UNLIKELY( blk->parent_slot!=ULONG_MAX && ( blk->parent_slot!=update->new_parent_slot || memcmp( &blk->parent_dmr, &update->new_parent_block_id, sizeof(fd_mr32_t) ) ) ) ) {
     381           0 :         FD_LOG_WARNING(( "notarized blk at slot %lu has an UpdateParent naming slot %lu, not its proven parent slot %lu, ignoring it", slot, update->new_parent_slot, blk->parent_slot ));
     382           0 :         continue;
     383           0 :       }
     384          12 :       blk->init_fec_cnt = fec_idx;
     385          12 :       blk->cons_fec_cnt = fd_uint_max( blk->cons_fec_cnt, fec_idx ); /* stream starts at the UpdateParent, prefix is skipped */
     386          12 :       continue;
     387          12 :     }
     388             : 
     389          96 :     if( FD_LIKELY( blk->parent!=null ) ) {
     390          39 :       ulong   idx  = fd_repair_blk_pool_idx( pool, blk );
     391          39 :       ulong * link = &fd_repair_blk_pool_ele( pool, blk->parent )->child;
     392          39 :       while( *link!=idx ) link = &fd_repair_blk_pool_ele( pool, *link )->sibling;
     393          39 :       *link        = blk->sibling;
     394          39 :       blk->parent  = null;
     395          39 :       blk->sibling = null;
     396          57 :     } else if( FD_LIKELY( blk!=self->root_blk ) ) {
     397          57 :       fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, pool );
     398          57 :     }
     399             : 
     400          96 :     blk->parent_slot   = update->new_parent_slot;
     401          96 :     blk->parent_dmr = FD_LOAD( fd_mr32_t, update->new_parent_block_id.uc );
     402          96 :     blk->init_fec_cnt  = fec_idx;
     403          96 :     blk->cons_fec_cnt  = fd_uint_max( blk->cons_fec_cnt, fec_idx );
     404          96 :     blk->orphan_req    = 0; /* an ORPHAN req for the header's parent is moot */
     405             : 
     406          96 :     fd_repair_blk_orphan_map_ele_insert( self->orphan_map, blk, pool ); /* under the new key, link_parent takes it out if the new parent is held */
     407          96 :     if( FD_UNLIKELY( link_parent( self, blk ) ) ) continue;
     408          24 :     fd_repair_slot_meta_t * pmeta = fd_repair_slot_meta( self, blk->parent_slot );
     409          24 :     if( FD_UNLIKELY( pmeta->eager_blk_idx==null && pmeta->final_blk_idx==null && !pmeta->skipped && !pmeta->invalidated ) ) blk_insert( self, blk->parent_slot, fd_type_pun_const( &hash_null ) ); /* seed the new parent slot's eager blk */
     410          24 :     fd_repair_blk_treap_insert( self, blk ); /* its tile asks ORPHAN for the new parent */
     411          24 :   }
     412         384 : }
     413             : 
     414             : FD_FN_CONST ulong
     415        4827 : fd_repair_align( void ) {
     416        4827 :   return fd_ulong_max( alignof(fd_repair_t), 128UL );
     417        4827 : }
     418             : 
     419             : FD_FN_CONST ulong
     420             : fd_repair_footprint( ulong slot_max,
     421             :                      ulong fec_max,
     422         876 :                      ulong fec_blk_max ) {
     423         876 :   ulong blk_max = ( 5UL*slot_max+1UL )/2UL;
     424         876 :   return FD_LAYOUT_FINI(
     425         876 :     FD_LAYOUT_APPEND(
     426         876 :     FD_LAYOUT_APPEND(
     427         876 :     FD_LAYOUT_APPEND(
     428         876 :     FD_LAYOUT_APPEND(
     429         876 :     FD_LAYOUT_APPEND(
     430         876 :     FD_LAYOUT_APPEND(
     431         876 :     FD_LAYOUT_APPEND(
     432         876 :     FD_LAYOUT_APPEND(
     433         876 :     FD_LAYOUT_APPEND(
     434         876 :     FD_LAYOUT_APPEND(
     435         876 :     FD_LAYOUT_APPEND(
     436         876 :     FD_LAYOUT_APPEND(
     437         876 :     FD_LAYOUT_INIT,
     438         876 :       fd_repair_align(),                sizeof(fd_repair_t)                                                                     ),
     439         876 :       alignof(fd_repair_slot_meta_t),   sizeof(fd_repair_slot_meta_t)*slot_max                                                  ),
     440         876 :       fd_repair_blk_pool_align(),       fd_repair_blk_pool_footprint      ( blk_max                                           ) ),
     441         876 :       alignof(ulong),                   sizeof(ulong)*blk_max*fec_blk_max                                                       ),
     442         876 :       fd_repair_blk_map_align(),        fd_repair_blk_map_footprint       ( fd_repair_blk_map_chain_cnt_est( blk_max )        ) ),
     443         876 :       fd_repair_blk_orphan_map_align(), fd_repair_blk_orphan_map_footprint( fd_repair_blk_orphan_map_chain_cnt_est( blk_max ) ) ),
     444         876 :       fd_repair_fec_pool_align(),       fd_repair_fec_pool_footprint      ( fec_max                                           ) ),
     445         876 :       fd_repair_fec_map_align(),        fd_repair_fec_map_footprint       ( fd_repair_fec_map_chain_cnt_est( fec_max )        ) ),
     446         876 :       fd_repair_deque_align(),          fd_repair_deque_footprint         ( fec_max                                           ) ),
     447         876 :       fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) ),
     448         876 :       fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) ),
     449         876 :       fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) ),
     450         876 :     fd_repair_align() );
     451         876 : }
     452             : 
     453             : void *
     454             : fd_repair_new( void * shmem,
     455             :                ulong  slot_max,
     456             :                ulong  fec_max,
     457             :                ulong  fec_blk_max,
     458         444 :                ulong  seed ) {
     459         444 :   ulong footprint = fd_repair_footprint( slot_max, fec_max, fec_blk_max );
     460         444 :   if( FD_UNLIKELY( !footprint || fec_blk_max<FD_FEC_BLK_MAX || fec_blk_max>UINT_MAX ) ) {
     461           3 :     FD_LOG_WARNING(( "bad footprint: %lu %lu %lu", slot_max, fec_max, fec_blk_max ));
     462           3 :     return NULL;
     463           3 :   }
     464             : 
     465         441 :   fd_memset( shmem, 0, footprint );
     466         441 :   fd_repair_t * repair;
     467             : 
     468         441 :   ulong blk_max = ( 5UL*slot_max+1UL )/2UL;
     469             : 
     470         441 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     471         441 :   repair             = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_align(),                sizeof(fd_repair_t)                                                                     );
     472         441 :   void * slot_meta   = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_repair_slot_meta_t),   sizeof(fd_repair_slot_meta_t)*slot_max                                                  );
     473         441 :   void * blk_pool    = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_blk_pool_align(),       fd_repair_blk_pool_footprint      ( blk_max                                           ) );
     474         441 :   ulong * blk_fecs   = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong),                   sizeof(ulong)*blk_max*fec_blk_max                                                       );
     475         441 :   void * blk_map     = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_blk_map_align(),        fd_repair_blk_map_footprint       ( fd_repair_blk_map_chain_cnt_est( blk_max )        ) );
     476         441 :   void * orphan_map  = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_blk_orphan_map_align(), fd_repair_blk_orphan_map_footprint( fd_repair_blk_orphan_map_chain_cnt_est( blk_max ) ) );
     477         441 :   void * fec_pool    = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_fec_pool_align(),       fd_repair_fec_pool_footprint      ( fec_max                                           ) );
     478         441 :   void * fec_map     = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_fec_map_align(),        fd_repair_fec_map_footprint       ( fd_repair_fec_map_chain_cnt_est( fec_max )        ) );
     479         441 :   void * reasm_deque = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_deque_align(),          fd_repair_deque_footprint         ( fec_max                                           ) );
     480         441 :   void * eager_treap = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) );
     481         441 :   void * notar_treap = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) );
     482         441 :   void * final_treap = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) );
     483         441 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_repair_align() ) == (ulong)shmem + footprint );
     484             : 
     485         441 :   repair->root        = ULONG_MAX;
     486         441 :   repair->slot_max    = slot_max;
     487         441 :   repair->fec_blk_max = fec_blk_max;
     488         441 :   repair->slot_meta   = (fd_repair_slot_meta_t *)slot_meta;
     489        7497 :   for( ulong i=0UL; i<slot_max; i++ ) repair->slot_meta[ i ].slot = ULONG_MAX;
     490         441 :   repair->blk_pool    = fd_repair_blk_pool_join      ( fd_repair_blk_pool_new      ( blk_pool,    blk_max                                                 ) );
     491       18081 :   for( ulong i=0UL; i<blk_max; i++ ) repair->blk_pool[ i ].fecs = blk_fecs + i*fec_blk_max;
     492         441 :   repair->blk_map     = fd_repair_blk_map_join       ( fd_repair_blk_map_new       ( blk_map,     fd_repair_blk_map_chain_cnt_est( blk_max ), seed        ) );
     493         441 :   repair->orphan_map  = fd_repair_blk_orphan_map_join( fd_repair_blk_orphan_map_new( orphan_map,  fd_repair_blk_orphan_map_chain_cnt_est( blk_max ), seed ) );
     494         441 :   repair->fec_pool    = fd_repair_fec_pool_join      ( fd_repair_fec_pool_new      ( fec_pool,    fec_max                                                 ) );
     495         441 :   repair->fec_map     = fd_repair_fec_map_join       ( fd_repair_fec_map_new       ( fec_map,     fd_repair_fec_map_chain_cnt_est( fec_max ), seed        ) );
     496         441 :   repair->reasm_deque = fd_repair_deque_join         ( fd_repair_deque_new         ( reasm_deque, fec_max                                                 ) );
     497         441 :   fd_repair_treap_seed( repair->blk_pool, blk_max, seed ); /* the treaps share each blk's prio */
     498         441 :   repair->eager_treap = fd_repair_treap_join( fd_repair_treap_new( eager_treap, blk_max ) );
     499         441 :   repair->notar_treap = fd_repair_treap_join( fd_repair_treap_new( notar_treap, blk_max ) );
     500         441 :   repair->final_treap = fd_repair_treap_join( fd_repair_treap_new( final_treap, blk_max ) );
     501             : 
     502         441 :   return shmem;
     503         441 : }
     504             : 
     505             : fd_repair_t *
     506         441 : fd_repair_join( void * shrepair ) {
     507         441 :   return (fd_repair_t *)shrepair;
     508         441 : }
     509             : 
     510             : void *
     511           0 : fd_repair_leave( fd_repair_t const * self ) {
     512           0 :   return (void *)self;
     513           0 : }
     514             : 
     515             : void *
     516           0 : fd_repair_delete( void * shrepair ) {
     517           0 :   return shrepair;
     518           0 : }
     519             : 
     520             : void
     521             : fd_repair_init( fd_repair_t *         self,
     522             :                 ulong                 root,
     523             :                 fd_mr32_t const *     root_blk_mr,
     524             :                 fd_repair_report_fn_t report,
     525       15651 :                 void *                report_ctx ) {
     526       15651 :   self->telemetry.report = report;
     527       15651 :   self->telemetry.ctx    = report_ctx;
     528       15651 :   fd_repair_blk_t * blk = blk_insert( self, root, root_blk_mr );
     529       15651 :   blk->telemetry.reported = 1; /* the root is not a received block */
     530       15651 :   blk_treap_remove( self, blk ); /* the root is never repaired */
     531       15651 :   fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, self->blk_pool ); /* the root waits for nothing */
     532       15651 :   fd_repair_slot_meta( self, root )->final_blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk );
     533       15651 :   fd_repair_slot_meta( self, root )->eager_blk_idx = (uint)fd_repair_blk_pool_idx_null( self->blk_pool ); /* the genesis root has a null dmr, but is not turbine's */
     534       15651 :   blk->eager           = 0;
     535       15651 :   self->root           = root;
     536       15651 :   self->root_blk       = blk;
     537       15651 :   self->catchup_slot   = root;
     538       15651 : }
     539             : 
     540             : void
     541       15330 : fd_repair_fini( fd_repair_t * self ) {
     542      260610 :   for( ulong i=0UL; i<self->slot_max; i++ ) self->slot_meta[ i ].slot = ULONG_MAX;
     543       15330 :   fd_repair_blk_map_reset( self->blk_map );
     544       15330 :   fd_repair_blk_orphan_map_reset( self->orphan_map );
     545       15330 :   fd_repair_blk_pool_reset( self->blk_pool );
     546       15330 :   fd_repair_fec_map_reset( self->fec_map );
     547       15330 :   fd_repair_fec_pool_reset( self->fec_pool );
     548       15330 :   fd_repair_deque_remove_all( self->reasm_deque );
     549       15330 :   self->eager_treap = fd_repair_treap_join( fd_repair_treap_new( fd_repair_treap_leave( self->eager_treap ), fd_repair_blk_pool_max( self->blk_pool ) ) );
     550       15330 :   self->notar_treap = fd_repair_treap_join( fd_repair_treap_new( fd_repair_treap_leave( self->notar_treap ), fd_repair_blk_pool_max( self->blk_pool ) ) );
     551       15330 :   self->final_treap = fd_repair_treap_join( fd_repair_treap_new( fd_repair_treap_leave( self->final_treap ), fd_repair_blk_pool_max( self->blk_pool ) ) );
     552       15330 :   self->root         = ULONG_MAX;
     553       15330 :   self->root_blk     = NULL;
     554       15330 :   self->catchup_slot = 0UL;
     555       15330 :   self->reconsume    = 0;
     556       15330 : }
     557             : 
     558             : void
     559             : fd_repair_blk_dead( fd_repair_t *     self,
     560             :                     ulong             slot,
     561             :                     fd_mr32_t const * blk_mr,
     562          51 :                     uint              latest_fec_idx ) {
     563          51 :   fd_repair_blk_t * dead = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     564          57 :   while( dead && memcmp( &dead->dmr, blk_mr, sizeof(fd_mr32_t) ) ) dead = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( dead, NULL, self->blk_pool );
     565          51 :   if( FD_UNLIKELY( !dead ) ) return;
     566             : 
     567          51 :   fd_repair_slot_meta_t * meta   = fd_repair_slot_meta( self, slot );
     568          51 :   int                     eager  = meta->eager_blk_idx==fd_repair_blk_pool_idx( self->blk_pool, dead );
     569          51 :   uint                    bound  = dead->init_fec_cnt ? dead->init_fec_cnt*FD_FEC_SHRED_CNT : UINT_MAX; /* UINT_MAX latest_fec_idx (no FLH) is never a prefix */
     570          51 :   int                     prefix = eager && fd_is_start_of_window( slot ) && latest_fec_idx<bound;
     571          51 :   if( FD_UNLIKELY( prefix ) ) return; /* the optimistic prefix died, the block may still replay from an UpdateParent */
     572             : 
     573          42 :   blk_treap_remove( self, dead ); /* replay rejected it, no point repairing the rest */
     574          42 :   if( FD_LIKELY( !eager || meta->invalidated ) ) return;
     575             : 
     576          15 :   meta->invalidated = 1; /* turbine must not rebuild it */
     577          15 :   dead->eager       = 0; /* never repaired by position */
     578          15 :   if( FD_LIKELY( dead->parent!=fd_repair_blk_pool_idx_null( self->blk_pool ) ) ) cascade( self, fd_repair_blk_pool_ele( self->blk_pool, dead->parent ) ); /* delivers the notar blks held behind it */
     579          15 : }
     580             : 
     581             : fd_repair_blk_t *
     582             : fd_repair_blk_finalized( fd_repair_t *     self,
     583             :                          ulong             slot,
     584             :                          fd_mr32_t const * blk_mr,
     585          78 :                          long              ts ) {
     586          78 :   fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, slot );
     587          78 :   fd_repair_blk_t const * existing_final = fd_repair_blk_pool_ele_const( self->blk_pool, meta->final_blk_idx );
     588          78 :   FD_CHECK_CRIT( !existing_final || !memcmp( &existing_final->dmr, blk_mr, sizeof(fd_mr32_t) ), "consensus safety violation" );
     589             : 
     590          78 :   fd_repair_blk_t * final = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     591          96 :   while( final && memcmp( &final->dmr, blk_mr, sizeof(fd_mr32_t) ) ) final = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( final, NULL, self->blk_pool );
     592          78 :   if( FD_UNLIKELY( !final ) ) final = fd_repair_blk_notarized( self, slot, blk_mr );
     593          78 :   if( FD_UNLIKELY( !final ) ) return NULL;
     594             : 
     595          75 :   fd_repair_blk_t * pool = self->blk_pool;
     596          75 :   promote( self, final, &meta->final_blk_idx );
     597             :   /* The eager blk keeps its positional repair: peers without the whole
     598             :      block still serve its shreds, which by-id repair cannot ask them for. */
     599             : 
     600             :   /* Remove all equivocating blocks except the finalized one.  Their
     601             :      children name them, not the finalized blk, and are unlinked. */
     602             : 
     603          75 :   fd_repair_blk_t * next;
     604          75 :   for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, pool );
     605         183 :                          blk;
     606         108 :                          blk = next ) {
     607         108 :     next = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, pool );
     608         108 :     if( FD_LIKELY( blk==final ) ) continue;
     609          33 :     if( FD_UNLIKELY( meta->eager_blk_idx==fd_repair_blk_pool_idx( pool, blk ) && !meta->invalidated && !memcmp( &blk->dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) continue; /* turbine may still complete as the finalized blk, see dedup */
     610          24 :     blk->telemetry.cancelled_ts     = fd_long_if( !blk->telemetry.cancelled_reason, ts, blk->telemetry.cancelled_ts );
     611          24 :     blk->telemetry.cancelled_reason = (uchar)fd_int_if( !blk->telemetry.cancelled_reason, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_NOTARIZED_VERSION, blk->telemetry.cancelled_reason ); /* an invalidated eager blk keeps its reason */
     612          24 :     prune( self, blk );
     613          24 :   }
     614          75 :   link_ancestors( self, final );
     615          75 :   return final;
     616          78 : }
     617             : 
     618             : fd_repair_blk_t *
     619             : fd_repair_blk_notarized( fd_repair_t *     self,
     620             :                          ulong             slot,
     621       45759 :                          fd_mr32_t const * blk_mr ) {
     622       45759 :   fd_repair_slot_meta_t *       meta = fd_repair_slot_meta( self, slot );
     623       45759 :   if( FD_UNLIKELY( !memcmp( blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) return NULL; /* never a real blk id, only turbine makes an eager blk */
     624       45759 :   if( FD_UNLIKELY( meta->final_blk_idx!=fd_repair_blk_pool_idx_null( self->blk_pool ) || meta->skipped ) ) return NULL; /* the slot is settled */
     625       45738 :   for( fd_repair_blk_t * dup = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     626       45849 :                          dup;
     627       45738 :                          dup = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( dup, NULL, self->blk_pool ) ) {
     628         159 :     if( FD_UNLIKELY( memcmp( &dup->dmr, blk_mr, sizeof(fd_mr32_t) ) ) ) continue;
     629          48 :     if( FD_LIKELY( meta->notar_blk_idx==fd_repair_blk_pool_idx_null( self->blk_pool ) ) ) promote( self, dup, &meta->notar_blk_idx ); /* already have it, now named */
     630          48 :     link_ancestors( self, dup );
     631          48 :     return NULL;
     632         159 :   }
     633       45690 :   fd_repair_blk_t * blk = blk_insert( self, slot, blk_mr );
     634       45690 :   link_orphans( self, blk );
     635       45690 :   return blk;
     636       45738 : }
     637             : 
     638             : fd_repair_blk_t *
     639             : fd_repair_blk_parented( fd_repair_t *     self,
     640             :                         ulong             slot,
     641             :                         fd_mr32_t const * blk_mr,
     642             :                         ulong             parent_slot,
     643             :                         fd_mr32_t const * parent_dmr,
     644             :                         uint              fec_set_cnt,
     645       45594 :                         long              ts ) {
     646       45594 :   fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     647       45603 :   while( FD_LIKELY( blk && memcmp( &blk->dmr, blk_mr, sizeof(fd_mr32_t) ) ) ) blk = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, self->blk_pool );
     648       45594 :   if( FD_UNLIKELY( !blk                         ) ) return NULL; /* blk was pruned while the response was in flight */
     649       45591 :   blk->telemetry.parent_res_cnt++;
     650       45591 :   blk->telemetry.first_meta_res_ts = fd_long_if( !blk->telemetry.first_meta_res_ts, ts, blk->telemetry.first_meta_res_ts );
     651             : 
     652       45591 :   if( FD_UNLIKELY( blk->parent_slot!=ULONG_MAX ) ) return NULL; /* duplicate response, eg. a retried request */
     653       45582 :   if( FD_UNLIKELY( parent_slot>=slot           ) ) return NULL; /* a blk's parent precedes it */
     654             : 
     655       45576 :   int orphan = blk!=self->root_blk && blk->parent==fd_repair_blk_pool_idx_null( self->blk_pool );
     656       45576 :   if( FD_LIKELY( orphan ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, self->blk_pool );
     657       45576 :   blk->parent_slot  = parent_slot;
     658       45576 :   blk->parent_dmr   = *parent_dmr;
     659       45576 :   blk->cmpl_fec_cnt = fec_set_cnt;
     660       45576 :   if( FD_LIKELY( orphan ) ) fd_repair_blk_orphan_map_ele_insert( self->orphan_map, blk, self->blk_pool ); /* under the named parent, link_parent takes it out if the parent is held */
     661       45576 :   fd_repair_blk_treap_insert( self, blk ); /* its FEC sets can be discovered now */
     662             : 
     663       45576 :   fd_repair_blk_t * parent  = link_parent( self, blk );
     664       45576 :   fd_repair_blk_t * created = NULL;
     665       45576 :   if( FD_LIKELY( !parent )  ) parent = created = fd_repair_blk_notarized( self, parent_slot, parent_dmr ); /* links the child blks naming it */
     666       45576 :   if( FD_LIKELY( parent ) ) {
     667       45564 :     if( FD_UNLIKELY( fd_repair_slot_meta( self, slot )->final_blk_idx==fd_repair_blk_pool_idx( self->blk_pool, blk ) ) ) promote( self, parent, &fd_repair_slot_meta( self, parent_slot )->final_blk_idx ); /* finalized ancestry */
     668       45564 :     consume( self, blk );
     669       45564 :     if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt ) ) cascade( self, blk );
     670       45564 :     if( FD_LIKELY( !created ) ) link_ancestors( self, parent ); /* a held parent may itself be orphaned */
     671       45564 :   }
     672       45576 :   return created;
     673       45582 : }
     674             : 
     675             : void
     676             : fd_repair_slot_catchup( fd_repair_t * self,
     677          21 :                         ulong         slot ) {
     678          81 :   for( ulong s=fd_ulong_max( self->root+1UL, self->catchup_slot ); s<slot; s++ ) {
     679          60 :     fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, s );
     680          60 :     if( FD_UNLIKELY( meta->skipped || meta->invalidated || meta->final_blk_idx!=fd_repair_blk_pool_idx_null( self->blk_pool ) || meta->eager_blk_idx!=fd_repair_blk_pool_idx_null( self->blk_pool ) ) ) continue;
     681          48 :     meta->eager_blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk_insert( self, s, fd_type_pun_const( &hash_null ) ) );
     682          48 :   }
     683          21 :   self->catchup_slot = fd_ulong_max( self->catchup_slot, slot );
     684          21 : }
     685             : 
     686             : fd_repair_fec_t *
     687             : fd_repair_fec_complete( fd_repair_t *      self,
     688             :                         fd_shred_t const * last_shred,
     689             :                         fd_mr32_t const *  fec_mr,
     690             :                         int                is_leader,
     691             :                         uint               turbine_cnt,
     692             :                         uint               repair_cnt,
     693             :                         uint               recovered_cnt,
     694             :                         uint               data_rcvd,
     695             :                         uint               parity_rcvd,
     696             :                         uint               repair_rcvd,
     697       67797 :                         long               ts ) {
     698       67797 :   fd_repair_shred_insert( self, last_shred, fec_mr, ts ); /* the last shred's header */
     699             : 
     700       67797 :   fd_mr_t key;
     701       67797 :   memcpy( key.mr32.uc, fec_mr->uc, sizeof(fd_mr32_t) );
     702       67797 :   fd_repair_fec_t * fec = fd_repair_fec_map_ele_query( self->fec_map, &key, NULL, self->fec_pool );
     703       67797 :   if( FD_UNLIKELY( !fec ) ) return NULL; /* fd_repair_shred_insert dropped it as conflicting with the eager blk */
     704             : 
     705       67773 :   int first_complete = !fec->fec_complete;
     706       67773 :   fec->cmpl_ts       = fd_long_if( first_complete, ts, fec->cmpl_ts );
     707       67773 :   fec->data_rcvd     = fd_uint_if( first_complete, data_rcvd,   fec->data_rcvd   );
     708       67773 :   fec->parity_rcvd   = fd_uint_if( first_complete, parity_rcvd, fec->parity_rcvd );
     709       67773 :   fec->repair_rcvd   = fd_uint_if( first_complete, repair_rcvd, fec->repair_rcvd );
     710       67773 :   fec->mr            = key;
     711       67773 :   fec->fec_complete  = 1;
     712       67773 :   fec->data_complete = !!( last_shred->data.flags & FD_SHRED_DATA_FLAG_DATA_COMPLETE );
     713       67773 :   fec->is_leader    |= !!is_leader; /* a completion again from the network does not clear our own */
     714             : 
     715       67773 :   ulong                   slot  = last_shred->slot; /* fd_shred_t is packed, the map query needs an aligned key */
     716       67773 :   fd_repair_slot_meta_t * meta  = fd_repair_slot_meta( self, slot );
     717       67773 :   fd_repair_blk_t *       pool  = self->blk_pool;
     718       67773 :   fd_repair_blk_t *       eager = fd_repair_blk_pool_ele( pool, meta->eager_blk_idx );
     719       67773 :   int                     cmpl  = eager && !meta->invalidated && eager->cmpl_fec_cnt && !memcmp( &eager->dmr, &hash_null, sizeof(fd_mr32_t) );
     720       75015 :   for( uint k=0U; cmpl && k<eager->cmpl_fec_cnt; k++ ) {
     721        7242 :     cmpl = eager->fecs[ k ]!=fd_repair_fec_pool_idx_null( self->fec_pool ) && fd_repair_fec_pool_ele( self->fec_pool, eager->fecs[ k ] )->fec_complete;
     722        7242 :   }
     723       67773 :   meta->leader |= (uint)!!is_leader;
     724       67773 :   if( FD_UNLIKELY( is_leader && eager ) ) eager->eager = 0; /* our own, never repaired */
     725       67773 :   if( FD_UNLIKELY( is_leader && eager && meta->final_blk_idx!=meta->eager_blk_idx && meta->notar_blk_idx!=meta->eager_blk_idx ) ) blk_treap_remove( self, eager );
     726       67773 :   if( FD_UNLIKELY( cmpl ) ) {
     727         684 :     compute_dmr( self, eager );
     728         684 :     link_orphans( self, eager ); /* the blks naming its dmr waited for it */
     729         684 :     dedup( self, eager, ts );
     730         684 :     if( FD_UNLIKELY( fd_repair_fec_map_ele_query( self->fec_map, &key, NULL, self->fec_pool )!=fec ) ) return NULL; /* dedup pruned eager and with it the FEC set */
     731         684 :   }
     732             : 
     733       67770 :   for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, pool );
     734      135669 :                          blk;
     735       67899 :                          blk = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, pool ) ) {
     736       67899 :     if( FD_LIKELY( blk->fecs[ fec->fec_idx ]!=fd_repair_fec_pool_idx( self->fec_pool, fec ) ) ) continue;
     737       67845 :     uint end = fd_uint_if( !!blk->cmpl_fec_cnt, blk->cmpl_fec_cnt, (uint)self->fec_blk_max );
     738      135678 :     while(    blk->buff_fec_cnt<end
     739      135678 :            && blk->fecs[ blk->buff_fec_cnt ]!=fd_repair_fec_pool_idx_null( self->fec_pool )
     740      135678 :            && fd_repair_fec_pool_ele( self->fec_pool, blk->fecs[ blk->buff_fec_cnt ] )->fec_complete ) {
     741       67833 :       blk->buff_fec_cnt++;
     742       67833 :     }
     743       67845 :     if( FD_LIKELY( first_complete ) ) {
     744       67839 :       blk->telemetry.turbine_shred_cnt   += turbine_cnt;
     745       67839 :       blk->telemetry.repair_shred_cnt    += repair_cnt;
     746       67839 :       blk->telemetry.recovered_shred_cnt += recovered_cnt;
     747       67839 :       blk->telemetry.last_cmpl_fec_idx    = fec->fec_idx*FD_FEC_SHRED_CNT;
     748       67839 :     }
     749       67845 :     blk->telemetry.last_shred_ts = fd_long_if( blk->cmpl_fec_cnt && blk->buff_fec_cnt==blk->cmpl_fec_cnt && !blk->telemetry.last_shred_ts, ts, blk->telemetry.last_shred_ts );
     750       67845 :     if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->buff_fec_cnt==blk->cmpl_fec_cnt && !blk->telemetry.reported && self->telemetry.report ) ) { blk->telemetry.reported = 1; self->telemetry.report( self->telemetry.ctx, blk ); }
     751       67845 :     consume( self, blk );
     752       67845 :     if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt ) ) cascade( self, blk );
     753       67845 :   }
     754       67770 :   return fec;
     755       67773 : }
     756             : 
     757             : void
     758             : fd_repair_fec_evicted( fd_repair_t *     self,
     759             :                        ulong             slot,
     760             :                        uint              fec_set_idx,
     761           3 :                        fd_mr_t const *   fec_mr ) {
     762           3 :   uint              k   = fec_set_idx/FD_FEC_SHRED_CNT;
     763           3 :   fd_repair_fec_t * fec = fd_repair_fec_map_ele_query( self->fec_map, fec_mr, NULL, self->fec_pool );
     764           3 :   if( FD_UNLIKELY(  !fec || fec->fec_complete || fec->slot!=slot || fec->fec_idx!=k ) ) return;
     765             : 
     766           3 :   fec->data_rcvd = 0U; /* its req, still waiting since the set is incomplete, asks for every shred again */
     767           3 : }
     768             : 
     769             : fd_repair_fec_t *
     770             : fd_repair_fec_notarized( fd_repair_t *     self,
     771             :                          ulong             slot,
     772             :                          fd_mr32_t const * blk_mr,
     773             :                          uint              fec_set_idx,
     774             :                          fd_mr_t const *   fec_mr,
     775       60729 :                          long              ts ) {
     776             : 
     777       60729 :   uint fec_idx = fec_set_idx / FD_FEC_SHRED_CNT;
     778             : 
     779       60729 :   fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     780       60753 :   while( blk && memcmp( &blk->dmr, blk_mr, sizeof(fd_mr32_t) ) ) blk = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, self->blk_pool );
     781       60729 :   if( FD_UNLIKELY( !blk ) ) return NULL; /* blk was pruned while the response was in flight */
     782       60726 :   blk->telemetry.fec_root_res_cnt++;
     783       60726 :   blk->telemetry.first_meta_res_ts = fd_long_if( !blk->telemetry.first_meta_res_ts, ts, blk->telemetry.first_meta_res_ts );
     784             : 
     785       60726 :   if( FD_UNLIKELY( blk->fecs[ fec_idx ]!=fd_repair_fec_pool_idx_null( self->fec_pool ) ) ) return NULL; /* duplicate response, eg. a retried request */
     786             : 
     787       60723 :   fd_repair_slot_meta_t * meta  = fd_repair_slot_meta( self, slot );
     788       60723 :   fd_repair_blk_t *       eager = fd_repair_blk_pool_ele( self->blk_pool, meta->eager_blk_idx );
     789       60723 :   int                     eqvoc = eager && eager->fecs[ fec_idx ]!=fd_repair_fec_pool_idx_null( self->fec_pool ) && memcmp( &fd_repair_fec_pool_ele( self->fec_pool, eager->fecs[ fec_idx ] )->mr.mr20, &fec_mr->mr20, sizeof(fd_mr20_t) );
     790       60723 :   if( FD_UNLIKELY( eqvoc  ) ) fd_repair_slot_invalidated( self, slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_MERKLE_ROOT_MISMATCH, ts );
     791             : 
     792       60723 :   fd_repair_fec_t * fec = fd_repair_fec_map_ele_query( self->fec_map, fec_mr, NULL, self->fec_pool );
     793       60723 :   if( FD_UNLIKELY( fec && ( fec->slot!=slot || fec->fec_idx!=fec_idx ) ) ) return NULL; /* a FEC set root commits to its slot and index, so this leaf names no FEC set a shred can fill */
     794       60714 :   if( FD_UNLIKELY( !fec ) ) {
     795       60684 :     FD_TEST( fd_repair_fec_pool_free( self->fec_pool ) );
     796       60684 :     fec                = fd_repair_fec_pool_ele_acquire( self->fec_pool );
     797       60684 :     fec->mr            = *fec_mr;
     798       60684 :     fec->fec_idx       = fec_idx;
     799       60684 :     fec->slot          = slot;
     800       60684 :     fec->fec_complete  = 0;
     801       60684 :     fec->data_complete = 0;
     802       60684 :     fec->first_ts      = 0L;
     803       60684 :     fec->cmpl_ts       = 0L;
     804       60684 :     fec->is_leader     = 0;
     805       60684 :     fec->data_rcvd     = 0U;
     806       60684 :     fec->parity_rcvd   = 0U;
     807       60684 :     fec->repair_rcvd   = 0U;
     808       60684 :     fec->update_parent.new_parent_slot = ULONG_MAX;
     809       60684 :     fd_repair_fec_map_ele_insert( self->fec_map, fec, self->fec_pool );
     810       60684 :   }
     811       60714 :   fec->is_notar        = 1;
     812       60714 :   blk->fecs[ fec_idx ] = fd_repair_fec_pool_idx( self->fec_pool, fec );
     813             : 
     814       60714 :   if( FD_UNLIKELY( fec->update_parent.new_parent_slot!=ULONG_MAX ) ) update_parent( self, slot, fec_idx,    ts );
     815       60714 :   if( FD_UNLIKELY( fec->data_complete                            ) ) update_parent( self, slot, fec_idx+1U, ts );
     816             : 
     817       60714 :   if( FD_LIKELY( !fec->fec_complete ) ) return fec;
     818             : 
     819           9 :   uint end = fd_uint_if( !!blk->cmpl_fec_cnt, blk->cmpl_fec_cnt, (uint)self->fec_blk_max );
     820          18 :   while(    blk->buff_fec_cnt<end
     821          18 :          && blk->fecs[ blk->buff_fec_cnt ]!=fd_repair_fec_pool_idx_null( self->fec_pool )
     822          18 :          && fd_repair_fec_pool_ele( self->fec_pool, blk->fecs[ blk->buff_fec_cnt ] )->fec_complete ) {
     823           9 :     blk->buff_fec_cnt++;
     824           9 :   }
     825           9 :   if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->buff_fec_cnt==blk->cmpl_fec_cnt && !blk->telemetry.reported && self->telemetry.report ) ) { blk->telemetry.reported = 1; self->telemetry.report( self->telemetry.ctx, blk ); }
     826           9 :   consume( self, blk );
     827           9 :   if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt ) ) cascade( self, blk );
     828           9 :   return fec;
     829       60714 : }
     830             : 
     831             : void
     832             : fd_repair_root_advanced( fd_repair_t *     self,
     833             :                          ulong             slot,
     834          33 :                          fd_mr32_t const * blk_mr ) {
     835          33 :   fd_repair_slot_meta_t const * meta           = &self->slot_meta[ slot % self->slot_max ];
     836          33 :   fd_repair_blk_t const *       existing_final = meta->slot==slot ? fd_repair_blk_pool_ele_const( self->blk_pool, meta->final_blk_idx ) : NULL;
     837          33 :   FD_CHECK_CRIT( !existing_final || !memcmp( &existing_final->dmr, blk_mr, sizeof(fd_mr32_t) ), "consensus safety violation" );
     838             : 
     839          33 :   ulong             root     = self->root;
     840          33 :   fd_repair_blk_t * root_blk = NULL; /* the new root, if held. self->root_blk is the old root until freed below: it left orphan_map on becoming root and must not be removed again */
     841         135 :   for( ulong s=root; s<=slot; s++ ) {
     842         102 :     fd_repair_blk_t * next;
     843         102 :     for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &s, NULL, self->blk_pool );
     844         207 :                            blk;
     845         105 :                            blk = next ) {
     846         105 :       next = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, self->blk_pool );
     847         105 :       if( FD_UNLIKELY( !blk->telemetry.reported && self->telemetry.report ) ) { blk->telemetry.reported = 1; self->telemetry.report( self->telemetry.ctx, blk ); }
     848       53865 :       for( ulong k=0UL; k<self->fec_blk_max; k++ ) {
     849       53760 :         ulong idx = blk->fecs[ k ];
     850       53760 :         blk->fecs[ k ] = fd_repair_fec_pool_idx_null( self->fec_pool );
     851       53760 :         if( FD_LIKELY( idx==fd_repair_fec_pool_idx_null( self->fec_pool ) ) ) continue;
     852          30 :         fd_repair_fec_t * fec = fd_repair_fec_pool_ele( self->fec_pool, idx );
     853          30 :         if( FD_LIKELY( fd_repair_fec_map_ele_query( self->fec_map, &fec->mr, NULL, self->fec_pool )!=fec ) ) continue; /* freed through a sibling */
     854          30 :         fd_repair_fec_map_ele_remove_fast( self->fec_map, fec, self->fec_pool );
     855          30 :         fd_repair_fec_pool_ele_release   ( self->fec_pool, fec );
     856          30 :       }
     857         105 :       if( FD_UNLIKELY( s==slot && !memcmp( &blk->dmr, blk_mr, sizeof(fd_mr32_t) ) ) ) { /* the new root */
     858          30 :         blk_treap_remove( self, blk ); /* the root is never repaired */
     859          30 :         if( FD_LIKELY( blk!=self->root_blk && blk->parent==fd_repair_blk_pool_idx_null( self->blk_pool ) ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, self->blk_pool ); /* its parent was just freed, the root waits for nothing */
     860          30 :         fd_repair_slot_meta( self, slot )->final_blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk );
     861          30 :         blk->eager = 0;
     862          30 :         root_blk   = blk;
     863          30 :         continue;
     864          30 :       }
     865             : 
     866          75 :       unlink( self, blk );
     867          75 :       if( FD_UNLIKELY( blk!=self->root_blk && blk->parent==fd_repair_blk_pool_idx_null( self->blk_pool ) ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, self->blk_pool );
     868          75 :       blk_treap_remove( self, blk );
     869          75 :       fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, s );
     870          75 :       if( FD_UNLIKELY( meta->eager_blk_idx==fd_repair_blk_pool_idx( self->blk_pool, blk ) ) ) meta->eager_blk_idx = (uint)fd_repair_blk_pool_idx_null( self->blk_pool ); /* the root slot's meta stays live, its roles must not name freed blks */
     871          75 :       if( FD_UNLIKELY( meta->notar_blk_idx==fd_repair_blk_pool_idx( self->blk_pool, blk ) ) ) meta->notar_blk_idx = (uint)fd_repair_blk_pool_idx_null( self->blk_pool );
     872          75 :       fd_repair_blk_map_ele_remove_fast( self->blk_map, blk, self->blk_pool );
     873          75 :       fd_repair_blk_pool_ele_release   ( self->blk_pool, blk );
     874          75 :     }
     875         102 :   }
     876          33 :   self->root_blk = root_blk;
     877          33 :   self->root     = slot;
     878          33 : }
     879             : 
     880             : void
     881             : fd_repair_shred_insert( fd_repair_t *      self,
     882             :                         fd_shred_t const * shred,
     883             :                         fd_mr32_t const *  fec_mr,
     884       68955 :                         long               ts ) {
     885             : 
     886       68955 :   fd_mr_t key;
     887       68955 :   memcpy( key.mr32.uc, fec_mr->uc, sizeof(fd_mr32_t) );
     888             : 
     889             :   /* First, handle a Notar shred (shred for a FEC set that has already
     890             :      reached a notarized status ie. SafeToNotar or stronger).  It also
     891             :      belongs to the eager blk, so fall through. */
     892             : 
     893       68955 :   fd_repair_fec_t * fec     = fd_repair_fec_map_ele_query( self->fec_map, &key, NULL, self->fec_pool );
     894       68955 :   uint              fec_idx = shred->idx / FD_FEC_SHRED_CNT;
     895       68955 :   if( FD_UNLIKELY(  fec && ( fec->slot!=shred->slot || fec->fec_idx!=fec_idx ) ) ) return; /* a 20-byte root collision */
     896       68955 :   if( FD_UNLIKELY(  fec && fec->is_notar && !fec->fec_complete ) ) fec->data_rcvd = fd_uint_set_bit( fec->data_rcvd, (int)( shred->idx - fec_idx * FD_FEC_SHRED_CNT ) );
     897       68955 :   if( FD_UNLIKELY(  fec && !fec->first_ts ) ) fec->first_ts = ts;
     898             : 
     899       68955 :   fd_block_marker_t update[1];
     900       68955 :   int parent_update = shred->idx%FD_FEC_SHRED_CNT==0U && shred->idx && !fd_block_marker_de( update, fd_shred_data_payload( shred ), fd_shred_payload_sz( shred ) ) && update->kind==FD_BLOCK_MARKER_KIND_UPDATE_PARENT;
     901       68955 :   int data_complete = !!( shred->data.flags & FD_SHRED_DATA_FLAG_DATA_COMPLETE );
     902             : 
     903       68955 :   if( FD_UNLIKELY( parent_update && fec ) ) { fec->update_parent = update->update_parent; update_parent( self, shred->slot, fec_idx,    ts ); }
     904       68955 :   if( FD_UNLIKELY( data_complete && fec ) ) { fec->data_complete = 1;                     update_parent( self, shred->slot, fec_idx+1U, ts ); }
     905             : 
     906             :   /* Second, handle an Eager shred (from Turbine or Eager Repair). */
     907             : 
     908       68955 :   ulong                   parent_slot = shred->slot - shred->data.parent_off;
     909       68955 :   fd_repair_blk_t *       pool        = self->blk_pool;
     910       68955 :   ulong                   null        = fd_repair_blk_pool_idx_null( pool );
     911       68955 :   fd_repair_slot_meta_t * meta        = fd_repair_slot_meta( self, shred->slot );
     912       68955 :   fd_repair_blk_t const * fin         = fd_repair_blk_pool_ele_const( pool, meta->final_blk_idx );
     913       68955 :   int                     fin_done    = fin && fin->cmpl_fec_cnt && fin->buff_fec_cnt==fin->cmpl_fec_cnt;
     914             : 
     915       68955 :   if( FD_UNLIKELY( meta->skipped || meta->invalidated || ( fin_done && meta->eager_blk_idx==null ) ) ) return; /* a final cert ahead of turbine still lets turbine build the blk, dedup merges it */
     916        8322 :   if( FD_UNLIKELY( meta->eager_blk_idx==null ) ) meta->eager_blk_idx = (uint)fd_repair_blk_pool_idx( pool, blk_insert( self, shred->slot, fd_type_pun_const( &hash_null ) ) );
     917        8322 :   fd_repair_blk_t * blk = fd_repair_blk_pool_ele( pool, meta->eager_blk_idx );
     918        8322 :   if( FD_UNLIKELY( blk->parent_slot==ULONG_MAX      ) ) blk->parent_slot      = parent_slot; /* created empty by a child or catchup */
     919        8322 :   if( FD_UNLIKELY( blk->init_parent_slot==ULONG_MAX ) ) blk->init_parent_slot = parent_slot;
     920             : 
     921             :   /* Check for misbehavior that can invalidate the eager block. */
     922             : 
     923        8322 :   fd_block_marker_t marker[1];
     924        8322 :   int               bad_header    = shred->idx==0U && ( fd_block_marker_de( marker, fd_shred_data_payload( shred ), fd_shred_payload_sz( shred ) ) || marker->kind!=FD_BLOCK_MARKER_KIND_HEADER || marker->header.parent_slot!=parent_slot );
     925        8322 :   fd_mr32_t const * parent_dmr = ( shred->idx==0U && !blk->init_fec_cnt ) ? fd_type_pun_const( &marker->header.parent_block_id ) : NULL;
     926             : 
     927        8322 :   int slot_complete     = !!( shred->data.flags & FD_SHRED_DATA_FLAG_SLOT_COMPLETE );
     928        8322 :   int eqvoc             = blk->fecs[ fec_idx ]!=fd_repair_fec_pool_idx_null( self->fec_pool ) && blk->fecs[ fec_idx ]!=fd_repair_fec_pool_idx( self->fec_pool, fec ); /* the leader signed two FEC sets at this index, eg. two shred 0s */
     929        8322 :   int parent_conflict   = bad_header || blk->init_parent_slot!=parent_slot;
     930        8322 :   int complete_conflict = ( blk->cmpl_fec_cnt && fec_idx>=blk->cmpl_fec_cnt ) || ( slot_complete && fec_idx+1U<blk->rcvd_fec_cnt );
     931        8322 :   if( FD_UNLIKELY( eqvoc ) )             { fd_repair_slot_invalidated( self, shred->slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_MERKLE_ROOT_MISMATCH, ts ); return; }
     932        8316 :   if( FD_UNLIKELY( parent_conflict ) )   { fd_repair_slot_invalidated( self, shred->slot, bad_header ? FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_INVALID_BLOCK_HEADER : FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_PARENT_OFF_MISMATCH, ts ); return; }
     933        8313 :   if( FD_UNLIKELY( complete_conflict ) ) { fd_repair_slot_invalidated( self, shred->slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_SLOT_COMPLETE_MISMATCH, ts ); return; }
     934        8304 :   if( FD_UNLIKELY( shred->idx==0U && blk->init_fec_cnt && marker->header.parent_slot==blk->parent_slot && !memcmp( &marker->header.parent_block_id, &blk->parent_dmr, sizeof(fd_mr32_t) ) ) ) {
     935             :     /* a late shred 0 header == UpdateParent's parent */
     936           3 :     fd_repair_slot_invalidated( self, shred->slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_INVALID_UPDATE_PARENT, ts );
     937           3 :     return;
     938           3 :   }
     939             : 
     940        8301 :   if( FD_UNLIKELY( parent_dmr ) ) { /* shred 0 again names the same parent, the key must not change while in orphan_map */
     941         789 :     if( FD_UNLIKELY( blk!=self->root_blk && blk->parent==null ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, pool );
     942         789 :     blk->parent_dmr = *parent_dmr;
     943         789 :     if( FD_UNLIKELY( blk!=self->root_blk && blk->parent==null ) ) fd_repair_blk_orphan_map_ele_insert     ( self->orphan_map, blk, pool );
     944         789 :   }
     945        8301 :   if( FD_UNLIKELY( parent_dmr && blk->parent==null && !link_parent( self, blk ) ) ) { /* waits unlinked for the named parent to be held, eg. to complete on turbine */
     946         240 :     fd_repair_slot_meta_t * pmeta = fd_repair_slot_meta( self, parent_slot );
     947         240 :     if( FD_UNLIKELY( pmeta->eager_blk_idx==null && pmeta->final_blk_idx==null && !pmeta->skipped && !pmeta->invalidated ) ) blk_insert( self, parent_slot, fd_type_pun_const( &hash_null ) ); /* seed the parent slot's eager blk, turbine may have lost it */
     948         240 :   }
     949        8301 :   if( FD_UNLIKELY( slot_complete ) ) blk->cmpl_fec_cnt = fec_idx+1U;
     950        8301 :   if( FD_UNLIKELY( slot_complete || fec_idx+1U>blk->rcvd_fec_cnt ) ) { /* its repair limit moved */
     951        7842 :     blk->eager_ts = fd_long_if( fec_idx+1U>blk->rcvd_fec_cnt, ts, blk->eager_ts );
     952        7842 :     fd_repair_blk_treap_insert( self, blk );
     953        7842 :   }
     954             : 
     955        8301 :   if( FD_UNLIKELY( !fec ) ) {
     956        7398 :     FD_TEST( fd_repair_fec_pool_free( self->fec_pool ) );
     957        7398 :     fec                = fd_repair_fec_pool_ele_acquire( self->fec_pool );
     958        7398 :     fec->mr            = key;
     959        7398 :     fec->fec_idx       = fec_idx;
     960        7398 :     fec->slot          = shred->slot;
     961        7398 :     fec->fec_complete  = 0;
     962        7398 :     fec->data_complete = 0;
     963        7398 :     fec->first_ts      = ts;
     964        7398 :     fec->cmpl_ts       = 0L;
     965        7398 :     fec->is_leader     = 0;
     966        7398 :     fec->is_notar      = 0;
     967        7398 :     fec->data_rcvd     = 0U;
     968        7398 :     fec->parity_rcvd   = 0U;
     969        7398 :     fec->repair_rcvd   = 0U;
     970        7398 :     fec->update_parent.new_parent_slot = ULONG_MAX;
     971        7398 :     fd_repair_fec_map_ele_insert( self->fec_map, fec, self->fec_pool );
     972        7398 :   }
     973        8301 :   blk->fecs[ fec_idx ] = fd_repair_fec_pool_idx( self->fec_pool, fec );
     974        8301 :   blk->rcvd_fec_cnt    = fd_uint_max( blk->rcvd_fec_cnt, fec_idx+1U );
     975        8301 :   fec->data_rcvd       = fd_uint_if( fec->fec_complete, fec->data_rcvd, fd_uint_set_bit( fec->data_rcvd, (int)( shred->idx - fec_idx*FD_FEC_SHRED_CNT ) ) );
     976             : 
     977        8301 :   if( FD_UNLIKELY( parent_update ) ) { fec->update_parent = update->update_parent; update_parent( self, shred->slot, fec_idx,    ts ); }
     978        8301 :   if( FD_UNLIKELY( data_complete ) ) { fec->data_complete = 1;                     update_parent( self, shred->slot, fec_idx+1U, ts ); }
     979        8301 : }
     980             : 
     981             : void
     982             : fd_repair_slot_invalidated( fd_repair_t * self,
     983             :                             ulong         slot,
     984             :                             int           reason,
     985       45501 :                             long          ts ) {
     986       45501 :   fd_repair_slot_meta_t * meta  = fd_repair_slot_meta( self, slot );
     987       45501 :   fd_repair_blk_t *       eager = fd_repair_blk_pool_ele( self->blk_pool, meta->eager_blk_idx );
     988       45501 :   int                     first = !meta->invalidated;
     989       45501 :   meta->invalidated = 1;
     990       45501 :   if( FD_LIKELY( eager ) ) {
     991          93 :     eager->telemetry.cancelled_ts     = fd_long_if( !eager->telemetry.cancelled_reason, ts, eager->telemetry.cancelled_ts );
     992          93 :     eager->telemetry.cancelled_reason = (uchar)reason;
     993          93 :     eager->eager = 0;
     994          93 :   }
     995       45501 :   if( FD_LIKELY( eager && meta->final_blk_idx!=meta->eager_blk_idx && meta->notar_blk_idx!=meta->eager_blk_idx ) ) blk_treap_remove( self, eager ); /* still the eager blk, never repaired */
     996       45501 :   if( FD_LIKELY( first && eager && eager->parent!=fd_repair_blk_pool_idx_null( self->blk_pool ) ) ) cascade( self, fd_repair_blk_pool_ele( self->blk_pool, eager->parent ) ); /* delivers the notar blks held behind it */
     997       45501 : }
     998             : 
     999             : fd_repair_slot_meta_t *
    1000             : fd_repair_slot_meta( fd_repair_t * self,
    1001      841230 :                      ulong         slot ) {
    1002      841230 :   fd_repair_slot_meta_t * meta = &self->slot_meta[ slot % self->slot_max ];
    1003      841230 :   if( FD_UNLIKELY( meta->slot!=slot ) ) {
    1004       62166 :     meta->slot          = slot;
    1005       62166 :     meta->eager_blk_idx = (uint)fd_repair_blk_pool_idx_null( self->blk_pool );
    1006       62166 :     meta->notar_blk_idx = (uint)fd_repair_blk_pool_idx_null( self->blk_pool );
    1007       62166 :     meta->final_blk_idx = (uint)fd_repair_blk_pool_idx_null( self->blk_pool );
    1008       62166 :     meta->invalidated   = 0;
    1009       62166 :     meta->skipped       = 0;
    1010       62166 :     meta->leader        = 0;
    1011       62166 :   }
    1012      841230 :   return meta;
    1013      841230 : }
    1014             : 
    1015             : void
    1016             : fd_repair_slot_skipped( fd_repair_t * self,
    1017          36 :                         ulong         slot ) {
    1018          36 :   fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, slot );
    1019          36 :   meta->skipped = 1; /* late repair requests or shreds for the slot must not recreate blks in it */
    1020             : 
    1021          36 :   fd_repair_blk_t * next;
    1022          36 :   for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
    1023          60 :                          blk;
    1024          36 :                          blk = next ) {
    1025             :     next = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, self->blk_pool );
    1026          24 :     prune( self, blk );
    1027          24 :   }
    1028          36 : }

Generated by: LCOV version 1.14