LCOV - code coverage report
Current view: top level - disco/repair - fd_repair.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 413 792 52.1 %
Date: 2026-11-11 12:40:43 Functions: 18 35 51.4 %

          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        8550 :                             fd_repair_blk_t * blk ) {
      10        8550 :   fd_repair_slot_meta_t const * meta  = fd_repair_slot_meta( self, blk->slot );
      11        8550 :   ulong                         idx   = fd_repair_blk_pool_idx( self->blk_pool, blk );
      12        8550 :   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        8550 :   if( FD_UNLIKELY( fd_repair_treap_ele_query_const( treap, blk, self->blk_pool )==blk ) ) return 0;
      14        2009 :   if( FD_UNLIKELY( meta->final_blk_idx!=idx && meta->notar_blk_idx!=idx && meta->eager_blk_idx==idx && !blk->eager ) ) return 0;
      15        2009 :   fd_repair_treap_ele_insert( treap, blk, self->blk_pool );
      16        2009 :   return 1;
      17        2009 : }
      18             : 
      19             : static int
      20             : blk_treap_remove( fd_repair_t *     self,
      21        1008 :                   fd_repair_blk_t * blk ) {
      22        1008 :   fd_repair_slot_meta_t const * meta  = fd_repair_slot_meta( self, blk->slot );
      23        1008 :   ulong                         idx   = fd_repair_blk_pool_idx( self->blk_pool, blk );
      24        1008 :   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        1008 :   if( FD_LIKELY( fd_repair_treap_ele_query_const( treap, blk, self->blk_pool )!=blk ) ) return 0;
      26        1008 :   fd_repair_treap_ele_remove( treap, blk, self->blk_pool );
      27        1008 :   return 1;
      28        1008 : }
      29             : 
      30             : static fd_repair_blk_t *
      31             : blk_insert( fd_repair_t *     self,
      32             :             ulong             slot,
      33        2009 :             fd_mr32_t const * dmr ) {
      34        2009 :   FD_TEST( fd_repair_blk_pool_free( self->blk_pool ) );
      35        2009 :   fd_repair_slot_meta_t *       meta = fd_repair_slot_meta( self, slot );
      36        2009 :   fd_repair_blk_t *             blk  = fd_repair_blk_pool_ele_acquire( self->blk_pool );
      37        2009 :   blk->slot             = slot;
      38        2009 :   blk->dmr              = *dmr;
      39        2009 :   blk->parent           = fd_repair_blk_pool_idx_null( self->blk_pool );
      40        2009 :   blk->child            = fd_repair_blk_pool_idx_null( self->blk_pool );
      41        2009 :   blk->sibling          = fd_repair_blk_pool_idx_null( self->blk_pool );
      42        2009 :   blk->cmpl_fec_cnt     = 0U;
      43        2009 :   blk->rcvd_fec_cnt     = 0U;
      44        2009 :   blk->buff_fec_cnt     = 0U;
      45        2009 :   blk->cons_fec_cnt     = 0U;
      46        2009 :   blk->wait_fec_cnt     = 0U;
      47        2009 :   blk->parent_slot      = ULONG_MAX;
      48        2009 :   blk->parent_dmr       = (fd_mr32_t){ { 0 } };
      49        2009 :   blk->init_fec_cnt     = 0U;
      50        2009 :   blk->init_parent_slot = ULONG_MAX;
      51             : 
      52        2009 :   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        2009 :   blk->meta_req      = 0;
      54        2009 :   blk->highest_req   = 0;
      55        2009 :   blk->orphan_req    = 0;
      56        2009 :   memset( &blk->telemetry, 0, sizeof(blk->telemetry) );
      57        2009 :   blk->telemetry.last_cmpl_fec_idx = UINT_MAX;
      58        2009 :   blk->eager_ts      = 0L;
      59     2056276 :   for( ulong k=0UL; k<self->fec_blk_max; k++ ) blk->fecs[ k ] = fd_repair_fec_pool_idx_null( self->fec_pool );
      60        2009 :   fd_repair_blk_map_ele_insert( self->blk_map, blk, self->blk_pool );
      61        2009 :   fd_repair_blk_orphan_map_ele_insert( self->orphan_map, blk, self->blk_pool );
      62        2009 :   if     ( !memcmp( dmr, &hash_null, sizeof(fd_mr32_t) ) )               meta->eager_blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk );
      63        1001 :   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        2009 :   fd_repair_blk_treap_insert( self, blk );
      65        2009 :   return blk;
      66        2009 : }
      67             : 
      68             : static void
      69             : compute_dmr( fd_repair_t *     self,
      70         992 :              fd_repair_blk_t * blk ) {
      71         992 :   uint fec_set_cnt = blk->cmpl_fec_cnt;
      72             : 
      73         992 :   uchar tree_mem[ FD_BMTREE_COMMIT_FOOTPRINT( 0UL ) ] __attribute__((aligned(FD_BMTREE_COMMIT_ALIGN)));
      74         992 :   fd_bmtree_commit_t * tree = fd_bmtree_commit_init( tree_mem, FD_SHRED_MERKLE_NODE_SZ, FD_BMTREE_LONG_PREFIX_SZ, 0UL );
      75        5537 :   for( uint k=0U; k<fec_set_cnt; k++ ) {
      76        4545 :     fd_bmtree_node_t leaf[1];
      77        4545 :     memcpy( leaf->hash, fd_repair_fec_pool_ele( self->fec_pool, blk->fecs[ k ] )->mr.mr32.uc, sizeof(fd_mr32_t) );
      78        4545 :     fd_bmtree_commit_append( tree, leaf, 1UL );
      79        4545 :   }
      80             : 
      81         992 :   fd_bmtree_node_t parent_info[1];
      82         992 :   fd_sha256_t      sha[1];
      83         992 :   fd_sha256_init  ( sha );
      84         992 :   fd_sha256_append( sha, &blk->parent_slot,      sizeof(ulong)     );
      85         992 :   fd_sha256_append( sha, blk->parent_dmr.uc,     sizeof(fd_mr32_t) );
      86         992 :   fd_sha256_append( sha, &fec_set_cnt,           sizeof(uint)      );
      87         992 :   fd_sha256_fini  ( sha, parent_info->hash );
      88         992 :   fd_bmtree_commit_append( tree, parent_info, 1UL );
      89             : 
      90         992 :   memcpy( blk->dmr.uc, fd_bmtree_commit_fini( tree ), sizeof(fd_mr32_t) );
      91         992 : }
      92             : 
      93             : static void
      94             : consume( fd_repair_t *     self,
      95        4557 :          fd_repair_blk_t * blk ) {
      96        4557 :   fd_repair_slot_meta_t const * meta = fd_repair_slot_meta( self, blk->slot );
      97        4557 :   if( FD_UNLIKELY( meta->invalidated && !memcmp( &blk->dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) return;
      98        4557 :   if( FD_UNLIKELY( blk->parent==fd_repair_blk_pool_idx_null( self->blk_pool ) ) ) return; /* orphaned, even if partly consumed */
      99        4557 :   if( FD_UNLIKELY( blk->cons_fec_cnt<=blk->init_fec_cnt ) ) {
     100        1001 :     fd_repair_blk_t const * parent = fd_repair_blk_pool_ele_const( self->blk_pool, blk->parent );
     101        1001 :     if( FD_LIKELY( parent!=self->root_blk && ( !parent->cmpl_fec_cnt || parent->cons_fec_cnt!=parent->cmpl_fec_cnt ) ) ) return;
     102        1001 :   }
     103        4557 :   ulong             idx   = fd_repair_blk_pool_idx( self->blk_pool, blk );
     104        4557 :   fd_repair_blk_t * eager = fd_repair_blk_pool_ele( self->blk_pool, meta->eager_blk_idx );
     105        4557 :   if( FD_UNLIKELY(  eager && meta->eager_blk_idx!=idx && !meta->invalidated && !memcmp( &eager->dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     106           0 :     int dup = eager->parent==blk->parent;
     107           0 :     for( uint k=blk->cons_fec_cnt; dup && k<blk->buff_fec_cnt; k++ ) dup = eager->fecs[ k ]==blk->fecs[ k ];
     108           0 :     if( FD_LIKELY( dup ) ) {
     109           0 :       blk = eager;
     110           0 :       idx = meta->eager_blk_idx;
     111           0 :     }
     112           0 :   }
     113             : 
     114        4557 :   if( FD_UNLIKELY( self->reconsume && blk->cons_fec_cnt<blk->buff_fec_cnt ) ) { /* replay evicted, need to specially redeliver from the root */
     115           0 :     self->reconsume = 0;
     116           0 :     for( fd_repair_blk_t * anc = blk; anc && anc!=self->root_blk; anc = fd_repair_blk_pool_ele( self->blk_pool, anc->parent ) ) {
     117           0 :       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           0 :     }
     119           0 :   }
     120        4557 :   if( FD_UNLIKELY( meta->leader && meta->eager_blk_idx==idx ) ) { /* our leader blk goes first, in FEC order */
     121           0 :     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           0 :     blk->cons_fec_cnt = blk->buff_fec_cnt;
     123           0 :     return;
     124           0 :   }
     125        9114 :   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        4557 : }
     127             : 
     128             : static void
     129             : cascade( fd_repair_t *     self,
     130         992 :          fd_repair_blk_t * root ) {
     131         992 :   fd_repair_blk_t * pool = self->blk_pool;
     132         992 :   ulong             null = fd_repair_blk_pool_idx_null( pool );
     133         992 :   ulong             tidx = fd_repair_blk_pool_idx( pool, root );
     134         992 :   ulong             idx  = root->child;
     135         992 :   while( FD_LIKELY( idx!=null ) ) {
     136           0 :     fd_repair_blk_t * blk = fd_repair_blk_pool_ele( pool, idx );
     137           0 :     consume( self, blk );
     138           0 :     if( FD_LIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt && blk->child!=null ) ) { idx = blk->child; continue; }
     139           0 :     while( FD_LIKELY( idx!=tidx && fd_repair_blk_pool_ele( pool, idx )->sibling==null ) ) idx = fd_repair_blk_pool_ele( pool, idx )->parent;
     140           0 :     if( FD_UNLIKELY( idx==tidx ) ) break;
     141           0 :     idx = fd_repair_blk_pool_ele( pool, idx )->sibling;
     142           0 :   }
     143         992 : }
     144             : 
     145             : static void
     146             : promote( fd_repair_t *     self,
     147             :          fd_repair_blk_t * blk,
     148           0 :          uint *            role ) {
     149           0 :   if( FD_LIKELY( *role==fd_repair_blk_pool_idx( self->blk_pool, blk ) ) ) return;
     150           0 :   int queued = blk_treap_remove( self, blk );
     151           0 :   *role      = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk );
     152           0 :   blk->eager = 0;
     153           0 :   if( FD_LIKELY( queued ) ) fd_repair_blk_treap_insert( self, blk );
     154           0 : }
     155             : 
     156             : static fd_repair_blk_t *
     157             : link_parent( fd_repair_t *     self,
     158        1001 :              fd_repair_blk_t * blk ) {
     159        1001 :   fd_repair_blk_t * pool = self->blk_pool;
     160        1001 :   if( FD_UNLIKELY( blk->parent_slot==ULONG_MAX ) ) return NULL;
     161        1001 :   if( FD_UNLIKELY( blk->parent_slot!=self->root && !memcmp( &blk->parent_dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) return NULL;
     162             : 
     163        1001 :   fd_repair_blk_t * parent = fd_repair_blk_map_ele_query( self->blk_map, &blk->parent_slot, NULL, pool );
     164        1001 :   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        1001 :   if( FD_LIKELY( !parent ) ) return NULL;
     166             : 
     167        1001 :   if( FD_LIKELY( blk!=self->root_blk ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, pool );
     168             : 
     169        1001 :   blk->parent   = fd_repair_blk_pool_idx( pool, parent );
     170        1001 :   blk->sibling  = parent->child;
     171        1001 :   parent->child = fd_repair_blk_pool_idx( pool, blk );
     172        1001 :   return parent;
     173        1001 : }
     174             : 
     175             : static void
     176             : link_ancestors( fd_repair_t *     self,
     177           0 :                 fd_repair_blk_t * blk ) {
     178           0 :   fd_repair_blk_t * pool = self->blk_pool;
     179           0 :   ulong             null = fd_repair_blk_pool_idx_null( pool );
     180           0 :   fd_repair_blk_t * top  = blk;
     181           0 :   for(;;) {
     182           0 :     if( FD_UNLIKELY( blk->parent_slot==ULONG_MAX || blk->parent_slot<self->root ) ) break;
     183           0 :     if( FD_UNLIKELY( blk->parent_slot!=self->root && !memcmp( &blk->parent_dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) break;
     184           0 :     fd_repair_slot_meta_t * meta     = fd_repair_slot_meta( self, blk->slot );
     185           0 :     fd_repair_slot_meta_t * pmeta    = fd_repair_slot_meta( self, blk->parent_slot );
     186           0 :     int                     final    = meta->final_blk_idx==fd_repair_blk_pool_idx( pool, blk );
     187           0 :     int                     unlinked = blk->parent==null;
     188           0 :     fd_repair_blk_t *       parent   = unlinked ? link_parent( self, blk ) : fd_repair_blk_pool_ele( pool, blk->parent );
     189           0 :     if( FD_UNLIKELY( parent && unlinked ) ) top = blk;
     190           0 :     if( FD_UNLIKELY( !parent ) ) {
     191           0 :       if( FD_UNLIKELY( blk->parent_slot==self->root ) ) break; /* names a root other than ours */
     192           0 :       parent = fd_repair_blk_notarized( self, blk->parent_slot, &blk->parent_dmr ); /* link_orphans links blk under it */
     193           0 :       if( FD_UNLIKELY( !parent ) ) break; /* the parent slot is settled otherwise */
     194           0 :       fd_repair_blk_treap_insert( self, blk ); /* its tile asks ORPHAN for eager ancestry, which may merge with the named parents */
     195           0 :       top = blk;
     196           0 :     }
     197           0 :     if( FD_UNLIKELY( final && pmeta->final_blk_idx==null ) ) promote( self, parent, &pmeta->final_blk_idx );
     198           0 :     blk = parent;
     199           0 :   }
     200           0 :   consume( self, top );
     201           0 :   if( FD_UNLIKELY( top->cmpl_fec_cnt && top->cons_fec_cnt==top->cmpl_fec_cnt ) ) cascade( self, top );
     202           0 : }
     203             : 
     204             : static void
     205             : link_orphans( fd_repair_t *     self,
     206         992 :               fd_repair_blk_t * parent ) {
     207         992 :   fd_repair_blk_t *       pool  = self->blk_pool;
     208         992 :   ulong                   null  = fd_repair_blk_pool_idx_null( pool );
     209         992 :   fd_repair_slot_meta_t * pmeta = fd_repair_slot_meta( self, parent->slot );
     210         992 :   fd_repair_blk_t *       next;
     211         992 :   for( fd_repair_blk_t * blk = fd_repair_blk_orphan_map_ele_query( self->orphan_map, &parent->dmr, NULL, pool );
     212         992 :                          blk;
     213         992 :                          blk = next ) {
     214           0 :     next = (fd_repair_blk_t *)fd_repair_blk_orphan_map_ele_next_const( blk, NULL, pool );
     215           0 :     if( FD_UNLIKELY( blk->parent_slot!=parent->slot ) ) continue;
     216           0 :     fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, pool );
     217           0 :     blk->parent   = fd_repair_blk_pool_idx( pool, parent );
     218           0 :     blk->sibling  = parent->child;
     219           0 :     parent->child = fd_repair_blk_pool_idx( pool, blk );
     220           0 :     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           0 :   }
     222         992 : }
     223             : 
     224             : static void
     225             : unlink( fd_repair_t *     self,
     226           0 :         fd_repair_blk_t * blk ) {
     227           0 :   fd_repair_blk_t * pool = self->blk_pool;
     228           0 :   ulong             null = fd_repair_blk_pool_idx_null( pool );
     229           0 :   ulong             idx  = fd_repair_blk_pool_idx( pool, blk );
     230           0 :   if( FD_LIKELY( blk->parent!=null ) ) {
     231           0 :     ulong * link = &fd_repair_blk_pool_ele( pool, blk->parent )->child;
     232           0 :     while( *link!=idx ) link = &fd_repair_blk_pool_ele( pool, *link )->sibling;
     233           0 :     *link = blk->sibling;
     234           0 :   }
     235           0 :   ulong next;
     236           0 :   for( ulong c=blk->child; c!=null; c=next ) {
     237           0 :     fd_repair_blk_t * child = fd_repair_blk_pool_ele( pool, c );
     238           0 :     next            = child->sibling;
     239           0 :     child->parent   = null;
     240           0 :     child->sibling  = null;
     241           0 :     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           0 :   }
     243           0 : }
     244             : 
     245             : static void
     246             : prune( fd_repair_t *     self,
     247           0 :        fd_repair_blk_t * blk ) {
     248           0 :   fd_repair_blk_t * pool = self->blk_pool;
     249           0 :   if( FD_UNLIKELY( !blk->telemetry.reported && self->telemetry.report ) ) self->telemetry.report( self->telemetry.ctx, blk );
     250           0 :   for( ulong k=0UL; k<self->fec_blk_max; k++ ) {
     251           0 :     if( FD_LIKELY( blk->fecs[ k ]==fd_repair_fec_pool_idx_null( self->fec_pool ) ) ) continue;
     252           0 :     int held = 0;
     253           0 :     for( fd_repair_blk_t const * other = fd_repair_blk_map_ele_query_const( self->blk_map, &blk->slot, NULL, pool );
     254           0 :                                  other && !held;
     255           0 :                                  other = fd_repair_blk_map_ele_next_const( other, NULL, pool ) ) {
     256           0 :       held = other!=blk && other->fecs[ k ]==blk->fecs[ k ];
     257           0 :     }
     258           0 :     if( FD_UNLIKELY( held ) ) continue;
     259           0 :     fd_repair_fec_t * fec = fd_repair_fec_pool_ele( self->fec_pool, blk->fecs[ k ] );
     260           0 :     fd_repair_fec_map_ele_remove_fast( self->fec_map, fec, self->fec_pool );
     261           0 :     fd_repair_fec_pool_ele_release   ( self->fec_pool, fec );
     262           0 :   }
     263           0 :   unlink( self, blk );
     264           0 :   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           0 :   blk_treap_remove( self, blk );
     266           0 :   fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, blk->slot );
     267           0 :   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           0 :   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           0 :   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           0 :   fd_repair_blk_map_ele_remove_fast( self->blk_map, blk, pool );
     271           0 :   fd_repair_blk_pool_ele_release   ( pool, blk );
     272           0 : }
     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         992 :        long              ts ) {
     290         992 :   fd_repair_blk_t * pool = self->blk_pool;
     291         992 :   ulong             null = fd_repair_blk_pool_idx_null( pool );
     292         992 :   ulong             idx  = fd_repair_blk_pool_idx( pool, eager );
     293             : 
     294         992 :   fd_repair_blk_t * notar = fd_repair_blk_map_ele_query( self->blk_map, &eager->slot, NULL, pool );
     295        1984 :   while( notar && ( notar==eager || memcmp( &notar->dmr, &eager->dmr, sizeof(fd_mr32_t) ) ) ) {
     296         992 :     notar = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( notar, NULL, pool );
     297         992 :   }
     298         992 :   fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, eager->slot );
     299         992 :   if( FD_UNLIKELY( !notar && meta->final_blk_idx!=null ) ) {
     300           0 :     eager->telemetry.cancelled_ts     = fd_long_if( !eager->telemetry.cancelled_reason, ts, eager->telemetry.cancelled_ts );
     301           0 :     eager->telemetry.cancelled_reason = (uchar)FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_NOTARIZED_VERSION;
     302           0 :     prune( self, eager );
     303           0 :     return;
     304           0 :   } /* turbine built a blk the cluster did not finalize */
     305         992 :   if( FD_LIKELY( !notar ) ) return;
     306             : 
     307           1 :   ulong next;
     308           1 :   for( ulong c=notar->child; c!=null; c=next ) {
     309           0 :     next                                       = fd_repair_blk_pool_ele( pool, c )->sibling;
     310           0 :     fd_repair_blk_pool_ele( pool, c )->parent  = idx;
     311           0 :     fd_repair_blk_pool_ele( pool, c )->sibling = eager->child;
     312           0 :     eager->child                               = c;
     313           0 :   }
     314           1 :   notar->child = null;
     315           1 :   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           1 :   blk_treap_remove( self, notar );
     322           1 :   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           1 :   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           1 :   notar->telemetry.reported = 1; /* the same block lives on as eager, we won't report two different lines for this */
     325             : 
     326           1 :   eager->telemetry.req_window_cnt    += notar->telemetry.req_window_cnt;
     327           1 :   eager->telemetry.req_highest_cnt   += notar->telemetry.req_highest_cnt;
     328           1 :   eager->telemetry.req_orphan_cnt    += notar->telemetry.req_orphan_cnt;
     329           1 :   eager->telemetry.req_shred_bid_cnt += notar->telemetry.req_shred_bid_cnt;
     330           1 :   eager->telemetry.req_parent_cnt    += notar->telemetry.req_parent_cnt;
     331           1 :   eager->telemetry.req_fec_root_cnt  += notar->telemetry.req_fec_root_cnt;
     332           1 :   eager->telemetry.shred_res_cnt     += notar->telemetry.shred_res_cnt;
     333           1 :   eager->telemetry.parent_res_cnt    += notar->telemetry.parent_res_cnt;
     334           1 :   eager->telemetry.fec_root_res_cnt  += notar->telemetry.fec_root_res_cnt;
     335             :   /* first timestamps */
     336           1 :   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           1 :   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           1 :   eager->telemetry.last_shred_res_ts  = fd_long_max( eager->telemetry.last_shred_res_ts, notar->telemetry.last_shred_res_ts );
     339           1 :   prune( self, notar );
     340           1 :   link_ancestors( self, eager );
     341           1 : }
     342             : 
     343             : static void
     344             : update_parent( fd_repair_t * self,
     345             :                ulong         slot,
     346             :                uint          fec_idx,
     347       18223 :                long          ts ) {
     348       18223 :   if( FD_UNLIKELY( !fec_idx || fec_idx>=self->fec_blk_max ) ) return;
     349       18223 :   fd_repair_slot_meta_t * meta  = fd_repair_slot_meta( self, slot );
     350       18223 :   fd_repair_blk_t *       pool  = self->blk_pool;
     351       18223 :   ulong                   null  = fd_repair_blk_pool_idx_null( pool );
     352       18223 :   fd_repair_blk_t * next;
     353       36448 :   for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, pool ); blk; blk = next ) {
     354       18225 :     next = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, pool );
     355             : 
     356       18225 :     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           1 :     fd_repair_fec_t const * prev_fec = fd_repair_fec_pool_ele_const( self->fec_pool, blk->fecs[ fec_idx-1U ] );
     358           1 :     fd_repair_fec_t const * next_fec = fd_repair_fec_pool_ele_const( self->fec_pool, blk->fecs[ fec_idx    ] );
     359             : 
     360           1 :     if( FD_LIKELY( !prev_fec->data_complete || next_fec->update_parent.new_parent_slot==ULONG_MAX ) ) continue;
     361           1 :     if( FD_UNLIKELY( blk->init_fec_cnt==fec_idx ) ) continue;
     362             : 
     363           1 :     fd_update_parent_t const * update = &next_fec->update_parent;
     364           1 :     int                        eager  = meta->eager_blk_idx==fd_repair_blk_pool_idx( pool, blk );
     365           1 :     if( FD_UNLIKELY( eager && meta->invalidated ) ) continue;
     366             : 
     367           1 :     int bad = !fd_is_start_of_window( slot )                                            /* not in the first slot of its leader window */
     368           1 :               || blk->init_fec_cnt                                                      /* a second UpdateParent */
     369           1 :               || update->new_parent_slot<self->root                                     /* a parent below the root */
     370           1 :               || update->new_parent_slot>=slot                                          /* a parent newer than the header's */
     371           1 :               || ( eager && update->new_parent_slot>blk->init_parent_slot )             /* a parent newer than the header's */
     372           1 :               || !memcmp( &update->new_parent_block_id, &hash_null, sizeof(fd_mr32_t) ) /* a null parent block id */
     373           1 :               || ( 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           1 :     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           1 :     if( FD_UNLIKELY( bad &&  eager ) ) { fd_repair_slot_invalidated( self, slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_INVALID_UPDATE_PARENT, ts ); continue; }
     378             : 
     379           1 :     if( FD_UNLIKELY( !eager ) ) {
     380           0 :       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           0 :       blk->init_fec_cnt = fec_idx;
     385           0 :       blk->cons_fec_cnt = fd_uint_max( blk->cons_fec_cnt, fec_idx ); /* stream starts at the UpdateParent, prefix is skipped */
     386           0 :       continue;
     387           0 :     }
     388             : 
     389           1 :     if( FD_LIKELY( blk->parent!=null ) ) {
     390           0 :       ulong   idx  = fd_repair_blk_pool_idx( pool, blk );
     391           0 :       ulong * link = &fd_repair_blk_pool_ele( pool, blk->parent )->child;
     392           0 :       while( *link!=idx ) link = &fd_repair_blk_pool_ele( pool, *link )->sibling;
     393           0 :       *link        = blk->sibling;
     394           0 :       blk->parent  = null;
     395           0 :       blk->sibling = null;
     396           1 :     } else if( FD_LIKELY( blk!=self->root_blk ) ) {
     397           0 :       fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, pool );
     398           0 :     }
     399             : 
     400           1 :     blk->parent_slot   = update->new_parent_slot;
     401           1 :     blk->parent_dmr = FD_LOAD( fd_mr32_t, update->new_parent_block_id.uc );
     402           1 :     blk->init_fec_cnt  = fec_idx;
     403           1 :     blk->cons_fec_cnt  = fd_uint_max( blk->cons_fec_cnt, fec_idx );
     404           1 :     blk->orphan_req    = 0; /* an ORPHAN req for the header's parent is moot */
     405             : 
     406           1 :     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           1 :     if( FD_UNLIKELY( link_parent( self, blk ) ) ) continue;
     408           1 :     fd_repair_slot_meta_t * pmeta = fd_repair_slot_meta( self, blk->parent_slot );
     409           1 :     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           1 :     fd_repair_blk_treap_insert( self, blk ); /* its tile asks ORPHAN for the new parent */
     411           1 :   }
     412       18223 : }
     413             : 
     414             : FD_FN_CONST ulong
     415       12090 : fd_repair_align( void ) {
     416       12090 :   return fd_ulong_max( alignof(fd_repair_t), 128UL );
     417       12090 : }
     418             : 
     419             : FD_FN_CONST ulong
     420             : fd_repair_footprint( ulong slot_max,
     421             :                      ulong fec_max,
     422        2015 :                      ulong fec_blk_max ) {
     423        2015 :   ulong blk_max = ( 5UL*slot_max+1UL )/2UL;
     424        2015 :   return FD_LAYOUT_FINI(
     425        2015 :     FD_LAYOUT_APPEND(
     426        2015 :     FD_LAYOUT_APPEND(
     427        2015 :     FD_LAYOUT_APPEND(
     428        2015 :     FD_LAYOUT_APPEND(
     429        2015 :     FD_LAYOUT_APPEND(
     430        2015 :     FD_LAYOUT_APPEND(
     431        2015 :     FD_LAYOUT_APPEND(
     432        2015 :     FD_LAYOUT_APPEND(
     433        2015 :     FD_LAYOUT_APPEND(
     434        2015 :     FD_LAYOUT_APPEND(
     435        2015 :     FD_LAYOUT_APPEND(
     436        2015 :     FD_LAYOUT_APPEND(
     437        2015 :     FD_LAYOUT_INIT,
     438        2015 :       fd_repair_align(),                sizeof(fd_repair_t)                                                                     ),
     439        2015 :       alignof(fd_repair_slot_meta_t),   sizeof(fd_repair_slot_meta_t)*slot_max                                                  ),
     440        2015 :       fd_repair_blk_pool_align(),       fd_repair_blk_pool_footprint      ( blk_max                                           ) ),
     441        2015 :       alignof(ulong),                   sizeof(ulong)*blk_max*fec_blk_max                                                       ),
     442        2015 :       fd_repair_blk_map_align(),        fd_repair_blk_map_footprint       ( fd_repair_blk_map_chain_cnt_est( blk_max )        ) ),
     443        2015 :       fd_repair_blk_orphan_map_align(), fd_repair_blk_orphan_map_footprint( fd_repair_blk_orphan_map_chain_cnt_est( blk_max ) ) ),
     444        2015 :       fd_repair_fec_pool_align(),       fd_repair_fec_pool_footprint      ( fec_max                                           ) ),
     445        2015 :       fd_repair_fec_map_align(),        fd_repair_fec_map_footprint       ( fd_repair_fec_map_chain_cnt_est( fec_max )        ) ),
     446        2015 :       fd_repair_deque_align(),          fd_repair_deque_footprint         ( fec_max                                           ) ),
     447        2015 :       fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) ),
     448        2015 :       fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) ),
     449        2015 :       fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) ),
     450        2015 :     fd_repair_align() );
     451        2015 : }
     452             : 
     453             : void *
     454             : fd_repair_new( void * shmem,
     455             :                ulong  slot_max,
     456             :                ulong  fec_max,
     457             :                ulong  fec_blk_max,
     458        1008 :                ulong  seed ) {
     459        1008 :   ulong footprint = fd_repair_footprint( slot_max, fec_max, fec_blk_max );
     460        1008 :   if( FD_UNLIKELY( !footprint || fec_blk_max<FD_FEC_BLK_MAX || fec_blk_max>UINT_MAX ) ) {
     461           0 :     FD_LOG_WARNING(( "bad footprint: %lu %lu %lu", slot_max, fec_max, fec_blk_max ));
     462           0 :     return NULL;
     463           0 :   }
     464             : 
     465        1008 :   fd_memset( shmem, 0, footprint );
     466        1008 :   fd_repair_t * repair;
     467             : 
     468        1008 :   ulong blk_max = ( 5UL*slot_max+1UL )/2UL;
     469             : 
     470        1008 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     471        1008 :   repair             = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_align(),                sizeof(fd_repair_t)                                                                     );
     472        1008 :   void * slot_meta   = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_repair_slot_meta_t),   sizeof(fd_repair_slot_meta_t)*slot_max                                                  );
     473        1008 :   void * blk_pool    = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_blk_pool_align(),       fd_repair_blk_pool_footprint      ( blk_max                                           ) );
     474        1008 :   ulong * blk_fecs   = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong),                   sizeof(ulong)*blk_max*fec_blk_max                                                       );
     475        1008 :   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        1008 :   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        1008 :   void * fec_pool    = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_fec_pool_align(),       fd_repair_fec_pool_footprint      ( fec_max                                           ) );
     478        1008 :   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        1008 :   void * reasm_deque = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_deque_align(),          fd_repair_deque_footprint         ( fec_max                                           ) );
     480        1008 :   void * eager_treap = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) );
     481        1008 :   void * notar_treap = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) );
     482        1008 :   void * final_treap = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_treap_align(),          fd_repair_treap_footprint         ( blk_max                                           ) );
     483        1008 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_repair_align() ) == (ulong)shmem + footprint );
     484             : 
     485        1008 :   repair->root        = ULONG_MAX;
     486        1008 :   repair->slot_max    = slot_max;
     487        1008 :   repair->fec_blk_max = fec_blk_max;
     488        1008 :   repair->slot_meta   = (fd_repair_slot_meta_t *)slot_meta;
     489        3039 :   for( ulong i=0UL; i<slot_max; i++ ) repair->slot_meta[ i ].slot = ULONG_MAX;
     490        1008 :   repair->blk_pool    = fd_repair_blk_pool_join      ( fd_repair_blk_pool_new      ( blk_pool,    blk_max                                                 ) );
     491        6083 :   for( ulong i=0UL; i<blk_max; i++ ) repair->blk_pool[ i ].fecs = blk_fecs + i*fec_blk_max;
     492        1008 :   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        1008 :   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        1008 :   repair->fec_pool    = fd_repair_fec_pool_join      ( fd_repair_fec_pool_new      ( fec_pool,    fec_max                                                 ) );
     495        1008 :   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        1008 :   repair->reasm_deque = fd_repair_deque_join         ( fd_repair_deque_new         ( reasm_deque, fec_max                                                 ) );
     497        1008 :   fd_repair_treap_seed( repair->blk_pool, blk_max, seed ); /* the treaps share each blk's prio */
     498        1008 :   repair->eager_treap = fd_repair_treap_join( fd_repair_treap_new( eager_treap, blk_max ) );
     499        1008 :   repair->notar_treap = fd_repair_treap_join( fd_repair_treap_new( notar_treap, blk_max ) );
     500        1008 :   repair->final_treap = fd_repair_treap_join( fd_repair_treap_new( final_treap, blk_max ) );
     501             : 
     502        1008 :   return shmem;
     503        1008 : }
     504             : 
     505             : fd_repair_t *
     506        1008 : fd_repair_join( void * shrepair ) {
     507        1008 :   return (fd_repair_t *)shrepair;
     508        1008 : }
     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        1008 :                 void *                report_ctx ) {
     526        1008 :   self->telemetry.report = report;
     527        1008 :   self->telemetry.ctx    = report_ctx;
     528        1008 :   fd_repair_blk_t * blk = blk_insert( self, root, root_blk_mr );
     529        1008 :   blk->telemetry.reported = 1; /* the root is not a received block */
     530        1008 :   blk_treap_remove( self, blk ); /* the root is never repaired */
     531        1008 :   fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, self->blk_pool ); /* the root waits for nothing */
     532        1008 :   fd_repair_slot_meta( self, root )->final_blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk );
     533        1008 :   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        1008 :   blk->eager           = 0;
     535        1008 :   self->root           = root;
     536        1008 :   self->root_blk       = blk;
     537        1008 :   self->catchup_slot   = root;
     538        1008 : }
     539             : 
     540             : void
     541           0 : fd_repair_fini( fd_repair_t * self ) {
     542           0 :   for( ulong i=0UL; i<self->slot_max; i++ ) self->slot_meta[ i ].slot = ULONG_MAX;
     543           0 :   fd_repair_blk_map_reset( self->blk_map );
     544           0 :   fd_repair_blk_orphan_map_reset( self->orphan_map );
     545           0 :   fd_repair_blk_pool_reset( self->blk_pool );
     546           0 :   fd_repair_fec_map_reset( self->fec_map );
     547           0 :   fd_repair_fec_pool_reset( self->fec_pool );
     548           0 :   fd_repair_deque_remove_all( self->reasm_deque );
     549           0 :   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           0 :   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           0 :   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           0 :   self->root         = ULONG_MAX;
     553           0 :   self->root_blk     = NULL;
     554           0 :   self->catchup_slot = 0UL;
     555           0 :   self->reconsume    = 0;
     556           0 : }
     557             : 
     558             : void
     559             : fd_repair_blk_dead( fd_repair_t *     self,
     560             :                     ulong             slot,
     561             :                     fd_mr32_t const * blk_mr,
     562           0 :                     uint              latest_fec_idx ) {
     563           0 :   fd_repair_blk_t * dead = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     564           0 :   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           0 :   if( FD_UNLIKELY( !dead ) ) return;
     566             : 
     567           0 :   fd_repair_slot_meta_t * meta   = fd_repair_slot_meta( self, slot );
     568           0 :   int                     eager  = meta->eager_blk_idx==fd_repair_blk_pool_idx( self->blk_pool, dead );
     569           0 :   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           0 :   int                     prefix = eager && fd_is_start_of_window( slot ) && latest_fec_idx<bound;
     571           0 :   if( FD_UNLIKELY( prefix ) ) return; /* the optimistic prefix died, the block may still replay from an UpdateParent */
     572             : 
     573           0 :   blk_treap_remove( self, dead ); /* replay rejected it, no point repairing the rest */
     574           0 :   if( FD_LIKELY( !eager || meta->invalidated ) ) return;
     575             : 
     576           0 :   meta->invalidated = 1; /* turbine must not rebuild it */
     577           0 :   dead->eager       = 0; /* never repaired by position */
     578           0 :   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           0 : }
     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           0 :                          long              ts ) {
     586           0 :   fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, slot );
     587           0 :   fd_repair_blk_t const * existing_final = fd_repair_blk_pool_ele_const( self->blk_pool, meta->final_blk_idx );
     588           0 :   FD_CHECK_CRIT( !existing_final || !memcmp( &existing_final->dmr, blk_mr, sizeof(fd_mr32_t) ), "consensus safety violation" );
     589             : 
     590           0 :   fd_repair_blk_t * final = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     591           0 :   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           0 :   if( FD_UNLIKELY( !final ) ) final = fd_repair_blk_notarized( self, slot, blk_mr );
     593           0 :   if( FD_UNLIKELY( !final ) ) return NULL;
     594             : 
     595           0 :   fd_repair_blk_t * pool = self->blk_pool;
     596           0 :   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           0 :   fd_repair_blk_t * next;
     604           0 :   for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, pool );
     605           0 :                          blk;
     606           0 :                          blk = next ) {
     607           0 :     next = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, pool );
     608           0 :     if( FD_LIKELY( blk==final ) ) continue;
     609           0 :     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           0 :     blk->telemetry.cancelled_ts     = fd_long_if( !blk->telemetry.cancelled_reason, ts, blk->telemetry.cancelled_ts );
     611           0 :     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           0 :     prune( self, blk );
     613           0 :   }
     614           0 :   link_ancestors( self, final );
     615           0 :   return final;
     616           0 : }
     617             : 
     618             : fd_repair_blk_t *
     619             : fd_repair_blk_notarized( fd_repair_t *     self,
     620             :                          ulong             slot,
     621           0 :                          fd_mr32_t const * blk_mr ) {
     622           0 :   fd_repair_slot_meta_t *       meta = fd_repair_slot_meta( self, slot );
     623           0 :   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           0 :   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           0 :   for( fd_repair_blk_t * dup = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     626           0 :                          dup;
     627           0 :                          dup = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( dup, NULL, self->blk_pool ) ) {
     628           0 :     if( FD_UNLIKELY( memcmp( &dup->dmr, blk_mr, sizeof(fd_mr32_t) ) ) ) continue;
     629           0 :     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           0 :     link_ancestors( self, dup );
     631           0 :     return NULL;
     632           0 :   }
     633           0 :   fd_repair_blk_t * blk = blk_insert( self, slot, blk_mr );
     634           0 :   link_orphans( self, blk );
     635           0 :   return blk;
     636           0 : }
     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           0 :                         long              ts ) {
     646           0 :   fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     647           0 :   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           0 :   if( FD_UNLIKELY( !blk                         ) ) return NULL; /* blk was pruned while the response was in flight */
     649           0 :   blk->telemetry.parent_res_cnt++;
     650           0 :   blk->telemetry.first_meta_res_ts = fd_long_if( !blk->telemetry.first_meta_res_ts, ts, blk->telemetry.first_meta_res_ts );
     651             : 
     652           0 :   if( FD_UNLIKELY( blk->parent_slot!=ULONG_MAX ) ) return NULL; /* duplicate response, eg. a retried request */
     653           0 :   if( FD_UNLIKELY( parent_slot>=slot           ) ) return NULL; /* a blk's parent precedes it */
     654             : 
     655           0 :   int orphan = blk!=self->root_blk && blk->parent==fd_repair_blk_pool_idx_null( self->blk_pool );
     656           0 :   if( FD_LIKELY( orphan ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, self->blk_pool );
     657           0 :   blk->parent_slot  = parent_slot;
     658           0 :   blk->parent_dmr   = *parent_dmr;
     659           0 :   blk->cmpl_fec_cnt = fec_set_cnt;
     660           0 :   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           0 :   fd_repair_blk_treap_insert( self, blk ); /* its FEC sets can be discovered now */
     662             : 
     663           0 :   fd_repair_blk_t * parent  = link_parent( self, blk );
     664           0 :   fd_repair_blk_t * created = NULL;
     665           0 :   if( FD_LIKELY( !parent )  ) parent = created = fd_repair_blk_notarized( self, parent_slot, parent_dmr ); /* links the child blks naming it */
     666           0 :   if( FD_LIKELY( parent ) ) {
     667           0 :     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           0 :     consume( self, blk );
     669           0 :     if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt ) ) cascade( self, blk );
     670           0 :     if( FD_LIKELY( !created ) ) link_ancestors( self, parent ); /* a held parent may itself be orphaned */
     671           0 :   }
     672           0 :   return created;
     673           0 : }
     674             : 
     675             : void
     676             : fd_repair_slot_catchup( fd_repair_t * self,
     677           0 :                         ulong         slot ) {
     678           0 :   for( ulong s=fd_ulong_max( self->root+1UL, self->catchup_slot ); s<slot; s++ ) {
     679           0 :     fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, s );
     680           0 :     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           0 :     meta->eager_blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk_insert( self, s, fd_type_pun_const( &hash_null ) ) );
     682           0 :   }
     683           0 :   self->catchup_slot = fd_ulong_max( self->catchup_slot, slot );
     684           0 : }
     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        4557 :                         long               ts ) {
     698        4557 :   fd_repair_shred_insert( self, last_shred, fec_mr, ts ); /* the last shred's header */
     699             : 
     700        4557 :   fd_mr_t key;
     701        4557 :   memcpy( key.mr32.uc, fec_mr->uc, sizeof(fd_mr32_t) );
     702        4557 :   fd_repair_fec_t * fec = fd_repair_fec_map_ele_query( self->fec_map, &key, NULL, self->fec_pool );
     703        4557 :   if( FD_UNLIKELY( !fec ) ) return NULL; /* fd_repair_shred_insert dropped it as conflicting with the eager blk */
     704             : 
     705        4557 :   int first_complete = !fec->fec_complete;
     706        4557 :   fec->cmpl_ts       = fd_long_if( first_complete, ts, fec->cmpl_ts );
     707        4557 :   fec->data_rcvd     = fd_uint_if( first_complete, data_rcvd,   fec->data_rcvd   );
     708        4557 :   fec->parity_rcvd   = fd_uint_if( first_complete, parity_rcvd, fec->parity_rcvd );
     709        4557 :   fec->repair_rcvd   = fd_uint_if( first_complete, repair_rcvd, fec->repair_rcvd );
     710        4557 :   fec->mr            = key;
     711        4557 :   fec->fec_complete  = 1;
     712        4557 :   fec->data_complete = !!( last_shred->data.flags & FD_SHRED_DATA_FLAG_DATA_COMPLETE );
     713        4557 :   fec->is_leader    |= !!is_leader; /* a completion again from the network does not clear our own */
     714             : 
     715        4557 :   ulong                   slot  = last_shred->slot; /* fd_shred_t is packed, the map query needs an aligned key */
     716        4557 :   fd_repair_slot_meta_t * meta  = fd_repair_slot_meta( self, slot );
     717        4557 :   fd_repair_blk_t *       pool  = self->blk_pool;
     718        4557 :   fd_repair_blk_t *       eager = fd_repair_blk_pool_ele( pool, meta->eager_blk_idx );
     719        4557 :   int                     cmpl  = eager && !meta->invalidated && eager->cmpl_fec_cnt && !memcmp( &eager->dmr, &hash_null, sizeof(fd_mr32_t) );
     720        9100 :   for( uint k=0U; cmpl && k<eager->cmpl_fec_cnt; k++ ) {
     721        4543 :     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        4543 :   }
     723        4557 :   meta->leader |= (uint)!!is_leader;
     724        4557 :   if( FD_UNLIKELY( is_leader && eager ) ) eager->eager = 0; /* our own, never repaired */
     725        4557 :   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        4557 :   if( FD_UNLIKELY( cmpl ) ) {
     727         992 :     compute_dmr( self, eager );
     728         992 :     link_orphans( self, eager ); /* the blks naming its dmr waited for it */
     729         992 :     dedup( self, eager, ts );
     730         992 :     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         992 :   }
     732             : 
     733        4557 :   for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, pool );
     734        9113 :                          blk;
     735        4557 :                          blk = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, pool ) ) {
     736        4556 :     if( FD_LIKELY( blk->fecs[ fec->fec_idx ]!=fd_repair_fec_pool_idx( self->fec_pool, fec ) ) ) continue;
     737        4556 :     uint end = fd_uint_if( !!blk->cmpl_fec_cnt, blk->cmpl_fec_cnt, (uint)self->fec_blk_max );
     738        9112 :     while(    blk->buff_fec_cnt<end
     739        9112 :            && blk->fecs[ blk->buff_fec_cnt ]!=fd_repair_fec_pool_idx_null( self->fec_pool )
     740        9112 :            && fd_repair_fec_pool_ele( self->fec_pool, blk->fecs[ blk->buff_fec_cnt ] )->fec_complete ) {
     741        4556 :       blk->buff_fec_cnt++;
     742        4556 :     }
     743        4556 :     if( FD_LIKELY( first_complete ) ) {
     744        4556 :       blk->telemetry.turbine_shred_cnt   += turbine_cnt;
     745        4556 :       blk->telemetry.repair_shred_cnt    += repair_cnt;
     746        4556 :       blk->telemetry.recovered_shred_cnt += recovered_cnt;
     747        4556 :       blk->telemetry.last_cmpl_fec_idx    = fec->fec_idx*FD_FEC_SHRED_CNT;
     748        4556 :     }
     749        4556 :     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        4556 :     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        4556 :     consume( self, blk );
     752        4556 :     if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt ) ) cascade( self, blk );
     753        4556 :   }
     754        4557 :   return fec;
     755        4557 : }
     756             : 
     757             : void
     758             : fd_repair_fec_evicted( fd_repair_t *     self,
     759             :                        ulong             slot,
     760             :                        uint              fec_set_idx,
     761           0 :                        fd_mr_t const *   fec_mr ) {
     762           0 :   uint              k   = fec_set_idx/FD_FEC_SHRED_CNT;
     763           0 :   fd_repair_fec_t * fec = fd_repair_fec_map_ele_query( self->fec_map, fec_mr, NULL, self->fec_pool );
     764           0 :   if( FD_UNLIKELY(  !fec || fec->fec_complete || fec->slot!=slot || fec->fec_idx!=k ) ) return;
     765             : 
     766           0 :   fec->data_rcvd = 0U; /* its req, still waiting since the set is incomplete, asks for every shred again */
     767           0 : }
     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           0 :                          long              ts ) {
     776             : 
     777           0 :   uint fec_idx = fec_set_idx / FD_FEC_SHRED_CNT;
     778             : 
     779           0 :   fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
     780           0 :   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           0 :   if( FD_UNLIKELY( !blk ) ) return NULL; /* blk was pruned while the response was in flight */
     782           0 :   blk->telemetry.fec_root_res_cnt++;
     783           0 :   blk->telemetry.first_meta_res_ts = fd_long_if( !blk->telemetry.first_meta_res_ts, ts, blk->telemetry.first_meta_res_ts );
     784             : 
     785           0 :   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           0 :   fd_repair_slot_meta_t * meta  = fd_repair_slot_meta( self, slot );
     788           0 :   fd_repair_blk_t *       eager = fd_repair_blk_pool_ele( self->blk_pool, meta->eager_blk_idx );
     789           0 :   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           0 :   if( FD_UNLIKELY( eqvoc  ) ) fd_repair_slot_invalidated( self, slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_MERKLE_ROOT_MISMATCH, ts );
     791             : 
     792           0 :   fd_repair_fec_t * fec = fd_repair_fec_map_ele_query( self->fec_map, fec_mr, NULL, self->fec_pool );
     793           0 :   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           0 :   if( FD_UNLIKELY( !fec ) ) {
     795           0 :     FD_TEST( fd_repair_fec_pool_free( self->fec_pool ) );
     796           0 :     fec                = fd_repair_fec_pool_ele_acquire( self->fec_pool );
     797           0 :     fec->mr            = *fec_mr;
     798           0 :     fec->fec_idx       = fec_idx;
     799           0 :     fec->slot          = slot;
     800           0 :     fec->fec_complete  = 0;
     801           0 :     fec->data_complete = 0;
     802           0 :     fec->first_ts      = 0L;
     803           0 :     fec->cmpl_ts       = 0L;
     804           0 :     fec->is_leader     = 0;
     805           0 :     fec->data_rcvd     = 0U;
     806           0 :     fec->parity_rcvd   = 0U;
     807           0 :     fec->repair_rcvd   = 0U;
     808           0 :     fec->update_parent.new_parent_slot = ULONG_MAX;
     809           0 :     fd_repair_fec_map_ele_insert( self->fec_map, fec, self->fec_pool );
     810           0 :   }
     811           0 :   fec->is_notar        = 1;
     812           0 :   blk->fecs[ fec_idx ] = fd_repair_fec_pool_idx( self->fec_pool, fec );
     813             : 
     814           0 :   if( FD_UNLIKELY( fec->update_parent.new_parent_slot!=ULONG_MAX ) ) update_parent( self, slot, fec_idx,    ts );
     815           0 :   if( FD_UNLIKELY( fec->data_complete                            ) ) update_parent( self, slot, fec_idx+1U, ts );
     816             : 
     817           0 :   if( FD_LIKELY( !fec->fec_complete ) ) return fec;
     818             : 
     819           0 :   uint end = fd_uint_if( !!blk->cmpl_fec_cnt, blk->cmpl_fec_cnt, (uint)self->fec_blk_max );
     820           0 :   while(    blk->buff_fec_cnt<end
     821           0 :          && blk->fecs[ blk->buff_fec_cnt ]!=fd_repair_fec_pool_idx_null( self->fec_pool )
     822           0 :          && fd_repair_fec_pool_ele( self->fec_pool, blk->fecs[ blk->buff_fec_cnt ] )->fec_complete ) {
     823           0 :     blk->buff_fec_cnt++;
     824           0 :   }
     825           0 :   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           0 :   consume( self, blk );
     827           0 :   if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt ) ) cascade( self, blk );
     828           0 :   return fec;
     829           0 : }
     830             : 
     831             : void
     832             : fd_repair_root_advanced( fd_repair_t *     self,
     833             :                          ulong             slot,
     834           0 :                          fd_mr32_t const * blk_mr ) {
     835           0 :   fd_repair_slot_meta_t const * meta           = &self->slot_meta[ slot % self->slot_max ];
     836           0 :   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           0 :   FD_CHECK_CRIT( !existing_final || !memcmp( &existing_final->dmr, blk_mr, sizeof(fd_mr32_t) ), "consensus safety violation" );
     838             : 
     839           0 :   ulong             root     = self->root;
     840           0 :   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           0 :   for( ulong s=root; s<=slot; s++ ) {
     842           0 :     fd_repair_blk_t * next;
     843           0 :     for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &s, NULL, self->blk_pool );
     844           0 :                            blk;
     845           0 :                            blk = next ) {
     846           0 :       next = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, self->blk_pool );
     847           0 :       if( FD_UNLIKELY( !blk->telemetry.reported && self->telemetry.report ) ) { blk->telemetry.reported = 1; self->telemetry.report( self->telemetry.ctx, blk ); }
     848           0 :       for( ulong k=0UL; k<self->fec_blk_max; k++ ) {
     849           0 :         ulong idx = blk->fecs[ k ];
     850           0 :         blk->fecs[ k ] = fd_repair_fec_pool_idx_null( self->fec_pool );
     851           0 :         if( FD_LIKELY( idx==fd_repair_fec_pool_idx_null( self->fec_pool ) ) ) continue;
     852           0 :         fd_repair_fec_t * fec = fd_repair_fec_pool_ele( self->fec_pool, idx );
     853           0 :         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           0 :         fd_repair_fec_map_ele_remove_fast( self->fec_map, fec, self->fec_pool );
     855           0 :         fd_repair_fec_pool_ele_release   ( self->fec_pool, fec );
     856           0 :       }
     857           0 :       if( FD_UNLIKELY( s==slot && !memcmp( &blk->dmr, blk_mr, sizeof(fd_mr32_t) ) ) ) { /* the new root */
     858           0 :         blk_treap_remove( self, blk ); /* the root is never repaired */
     859           0 :         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           0 :         fd_repair_slot_meta( self, slot )->final_blk_idx = (uint)fd_repair_blk_pool_idx( self->blk_pool, blk );
     861           0 :         blk->eager = 0;
     862           0 :         root_blk   = blk;
     863           0 :         continue;
     864           0 :       }
     865             : 
     866           0 :       unlink( self, blk );
     867           0 :       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           0 :       blk_treap_remove( self, blk );
     869           0 :       fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, s );
     870           0 :       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           0 :       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           0 :       fd_repair_blk_map_ele_remove_fast( self->blk_map, blk, self->blk_pool );
     873           0 :       fd_repair_blk_pool_ele_release   ( self->blk_pool, blk );
     874           0 :     }
     875           0 :   }
     876           0 :   self->root_blk = root_blk;
     877           0 :   self->root     = slot;
     878           0 : }
     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      150351 :                         long               ts ) {
     885             : 
     886      150351 :   fd_mr_t key;
     887      150351 :   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      150351 :   fd_repair_fec_t * fec     = fd_repair_fec_map_ele_query( self->fec_map, &key, NULL, self->fec_pool );
     894      150351 :   uint              fec_idx = shred->idx / FD_FEC_SHRED_CNT;
     895      150351 :   if( FD_UNLIKELY(  fec && ( fec->slot!=shred->slot || fec->fec_idx!=fec_idx ) ) ) return; /* a 20-byte root collision */
     896      150351 :   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      150351 :   if( FD_UNLIKELY(  fec && !fec->first_ts ) ) fec->first_ts = ts;
     898             : 
     899      150351 :   fd_block_marker_t update[1];
     900      150351 :   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      150351 :   int data_complete = !!( shred->data.flags & FD_SHRED_DATA_FLAG_DATA_COMPLETE );
     902             : 
     903      150351 :   if( FD_UNLIKELY( parent_update && fec ) ) { fec->update_parent = update->update_parent; update_parent( self, shred->slot, fec_idx,    ts ); }
     904      150351 :   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      150351 :   ulong                   parent_slot = shred->slot - shred->data.parent_off;
     909      150351 :   fd_repair_blk_t *       pool        = self->blk_pool;
     910      150351 :   ulong                   null        = fd_repair_blk_pool_idx_null( pool );
     911      150351 :   fd_repair_slot_meta_t * meta        = fd_repair_slot_meta( self, shred->slot );
     912      150351 :   fd_repair_blk_t const * fin         = fd_repair_blk_pool_ele_const( pool, meta->final_blk_idx );
     913      150351 :   int                     fin_done    = fin && fin->cmpl_fec_cnt && fin->buff_fec_cnt==fin->cmpl_fec_cnt;
     914             : 
     915      150351 :   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      150351 :   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      150351 :   fd_repair_blk_t * blk = fd_repair_blk_pool_ele( pool, meta->eager_blk_idx );
     918      150351 :   if( FD_UNLIKELY( blk->parent_slot==ULONG_MAX      ) ) blk->parent_slot      = parent_slot; /* created empty by a child or catchup */
     919      150351 :   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      150351 :   fd_block_marker_t marker[1];
     924      150351 :   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      150351 :   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      150351 :   int slot_complete     = !!( shred->data.flags & FD_SHRED_DATA_FLAG_SLOT_COMPLETE );
     928      150351 :   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      150351 :   int parent_conflict   = bad_header || blk->init_parent_slot!=parent_slot;
     930      150351 :   int complete_conflict = ( blk->cmpl_fec_cnt && fec_idx>=blk->cmpl_fec_cnt ) || ( slot_complete && fec_idx+1U<blk->rcvd_fec_cnt );
     931      150351 :   if( FD_UNLIKELY( eqvoc ) )             { fd_repair_slot_invalidated( self, shred->slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_MERKLE_ROOT_MISMATCH, ts ); return; }
     932      150351 :   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      150351 :   if( FD_UNLIKELY( complete_conflict ) ) { fd_repair_slot_invalidated( self, shred->slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_SLOT_COMPLETE_MISMATCH, ts ); return; }
     934      150351 :   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           0 :     fd_repair_slot_invalidated( self, shred->slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_INVALID_UPDATE_PARENT, ts );
     937           0 :     return;
     938           0 :   }
     939             : 
     940      150351 :   if( FD_UNLIKELY( parent_dmr ) ) { /* shred 0 again names the same parent, the key must not change while in orphan_map */
     941        1001 :     if( FD_UNLIKELY( blk!=self->root_blk && blk->parent==null ) ) fd_repair_blk_orphan_map_ele_remove_fast( self->orphan_map, blk, pool );
     942        1001 :     blk->parent_dmr = *parent_dmr;
     943        1001 :     if( FD_UNLIKELY( blk!=self->root_blk && blk->parent==null ) ) fd_repair_blk_orphan_map_ele_insert     ( self->orphan_map, blk, pool );
     944        1001 :   }
     945      150351 :   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           0 :     fd_repair_slot_meta_t * pmeta = fd_repair_slot_meta( self, parent_slot );
     947           0 :     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           0 :   }
     949      150351 :   if( FD_UNLIKELY( slot_complete ) ) blk->cmpl_fec_cnt = fec_idx+1U;
     950      150351 :   if( FD_UNLIKELY( slot_complete || fec_idx+1U>blk->rcvd_fec_cnt ) ) { /* its repair limit moved */
     951        6541 :     blk->eager_ts = fd_long_if( fec_idx+1U>blk->rcvd_fec_cnt, ts, blk->eager_ts );
     952        6541 :     fd_repair_blk_treap_insert( self, blk );
     953        6541 :   }
     954             : 
     955      150351 :   if( FD_UNLIKELY( !fec ) ) {
     956        4557 :     FD_TEST( fd_repair_fec_pool_free( self->fec_pool ) );
     957        4557 :     fec                = fd_repair_fec_pool_ele_acquire( self->fec_pool );
     958        4557 :     fec->mr            = key;
     959        4557 :     fec->fec_idx       = fec_idx;
     960        4557 :     fec->slot          = shred->slot;
     961        4557 :     fec->fec_complete  = 0;
     962        4557 :     fec->data_complete = 0;
     963        4557 :     fec->first_ts      = ts;
     964        4557 :     fec->cmpl_ts       = 0L;
     965        4557 :     fec->is_leader     = 0;
     966        4557 :     fec->is_notar      = 0;
     967        4557 :     fec->data_rcvd     = 0U;
     968        4557 :     fec->parity_rcvd   = 0U;
     969        4557 :     fec->repair_rcvd   = 0U;
     970        4557 :     fec->update_parent.new_parent_slot = ULONG_MAX;
     971        4557 :     fd_repair_fec_map_ele_insert( self->fec_map, fec, self->fec_pool );
     972        4557 :   }
     973      150351 :   blk->fecs[ fec_idx ] = fd_repair_fec_pool_idx( self->fec_pool, fec );
     974      150351 :   blk->rcvd_fec_cnt    = fd_uint_max( blk->rcvd_fec_cnt, fec_idx+1U );
     975      150351 :   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      150351 :   if( FD_UNLIKELY( parent_update ) ) { fec->update_parent = update->update_parent; update_parent( self, shred->slot, fec_idx,    ts ); }
     978      150351 :   if( FD_UNLIKELY( data_complete ) ) { fec->data_complete = 1;                     update_parent( self, shred->slot, fec_idx+1U, ts ); }
     979      150351 : }
     980             : 
     981             : void
     982             : fd_repair_slot_invalidated( fd_repair_t * self,
     983             :                             ulong         slot,
     984             :                             int           reason,
     985           0 :                             long          ts ) {
     986           0 :   fd_repair_slot_meta_t * meta  = fd_repair_slot_meta( self, slot );
     987           0 :   fd_repair_blk_t *       eager = fd_repair_blk_pool_ele( self->blk_pool, meta->eager_blk_idx );
     988           0 :   int                     first = !meta->invalidated;
     989           0 :   meta->invalidated = 1;
     990           0 :   if( FD_LIKELY( eager ) ) {
     991           0 :     eager->telemetry.cancelled_ts     = fd_long_if( !eager->telemetry.cancelled_reason, ts, eager->telemetry.cancelled_ts );
     992           0 :     eager->telemetry.cancelled_reason = (uchar)reason;
     993           0 :     eager->eager = 0;
     994           0 :   }
     995           0 :   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           0 :   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           0 : }
     998             : 
     999             : fd_repair_slot_meta_t *
    1000             : fd_repair_slot_meta( fd_repair_t * self,
    1001      197766 :                      ulong         slot ) {
    1002      197766 :   fd_repair_slot_meta_t * meta = &self->slot_meta[ slot % self->slot_max ];
    1003      197766 :   if( FD_UNLIKELY( meta->slot!=slot ) ) {
    1004        2009 :     meta->slot          = slot;
    1005        2009 :     meta->eager_blk_idx = (uint)fd_repair_blk_pool_idx_null( self->blk_pool );
    1006        2009 :     meta->notar_blk_idx = (uint)fd_repair_blk_pool_idx_null( self->blk_pool );
    1007        2009 :     meta->final_blk_idx = (uint)fd_repair_blk_pool_idx_null( self->blk_pool );
    1008        2009 :     meta->invalidated   = 0;
    1009        2009 :     meta->skipped       = 0;
    1010        2009 :     meta->leader        = 0;
    1011        2009 :   }
    1012      197766 :   return meta;
    1013      197766 : }
    1014             : 
    1015             : void
    1016             : fd_repair_slot_skipped( fd_repair_t * self,
    1017           0 :                         ulong         slot ) {
    1018           0 :   fd_repair_slot_meta_t * meta = fd_repair_slot_meta( self, slot );
    1019           0 :   meta->skipped = 1; /* late repair requests or shreds for the slot must not recreate blks in it */
    1020             : 
    1021           0 :   fd_repair_blk_t * next;
    1022           0 :   for( fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( self->blk_map, &slot, NULL, self->blk_pool );
    1023           0 :                          blk;
    1024           0 :                          blk = next ) {
    1025           0 :     next = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, self->blk_pool );
    1026           0 :     prune( self, blk );
    1027           0 :   }
    1028           0 : }

Generated by: LCOV version 1.14