LCOV - code coverage report
Current view: top level - choreo/rotor - fd_rotor.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 744 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 34 0.0 %

          Line data    Source code
       1             : #include "fd_rotor.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             : static void
       8             : blk_treap_insert( fd_rotor_t *     rotor,
       9           0 :                   fd_rotor_blk_t * blk ) {
      10           0 :   fd_rotor_slot_meta_t const * meta = fd_rotor_slot_meta( rotor, blk->slot );
      11           0 :   ulong                        idx  = fd_rotor_blk_pool_idx( rotor->blk_pool, blk );
      12           0 :   if( FD_UNLIKELY( blk->in_blk_treap ) ) return;
      13           0 :   if( FD_UNLIKELY( meta->final!=idx && meta->notar!=idx && meta->eager==idx && !blk->eager ) ) return;
      14           0 :   fd_rotor_treap_ele_insert( meta->final==idx ? rotor->final_treap : meta->notar==idx ? rotor->notar_treap : rotor->eager_treap, blk, rotor->blk_pool );
      15           0 :   blk->in_blk_treap = 1;
      16           0 : }
      17             : 
      18             : static void
      19             : blk_treap_remove( fd_rotor_t *     rotor,
      20           0 :                   fd_rotor_blk_t * blk ) {
      21           0 :   if( FD_LIKELY( !blk->in_blk_treap ) ) return;
      22           0 :   fd_rotor_slot_meta_t const * meta = fd_rotor_slot_meta( rotor, blk->slot );
      23           0 :   ulong                        idx  = fd_rotor_blk_pool_idx( rotor->blk_pool, blk );
      24           0 :   fd_rotor_treap_ele_remove( meta->final==idx ? rotor->final_treap : meta->notar==idx ? rotor->notar_treap : rotor->eager_treap, blk, rotor->blk_pool );
      25           0 :   blk->in_blk_treap = 0;
      26           0 : }
      27             : 
      28             : 
      29             : /* blk_insert creates a blk of slot named dmr (null for an eager blk)
      30             :    with nothing received yet.  An eager blk is the slot's eager blk. */
      31             : 
      32             : static fd_rotor_blk_t *
      33             : blk_insert( fd_rotor_t *      rotor,
      34             :             ulong             slot,
      35           0 :             fd_mr32_t const * dmr ) {
      36           0 :   FD_TEST( fd_rotor_blk_pool_free( rotor->blk_pool ) );
      37           0 :   fd_rotor_slot_meta_t *       meta = fd_rotor_slot_meta( rotor, slot );
      38           0 :   fd_rotor_blk_t *             blk  = fd_rotor_blk_pool_ele_acquire( rotor->blk_pool );
      39           0 :   blk->slot          = slot;
      40           0 :   blk->dmr           = *dmr;
      41           0 :   blk->parent        = fd_rotor_blk_pool_idx_null( rotor->blk_pool );
      42           0 :   blk->child         = fd_rotor_blk_pool_idx_null( rotor->blk_pool );
      43           0 :   blk->sibling       = fd_rotor_blk_pool_idx_null( rotor->blk_pool );
      44           0 :   blk->cmpl_fec_cnt  = 0U;
      45           0 :   blk->rcvd_fec_cnt  = 0U;
      46           0 :   blk->buff_fec_cnt  = 0U;
      47           0 :   blk->cons_fec_cnt  = 0U;
      48           0 :   blk->wait_fec_cnt  = 0U;
      49           0 :   blk->parent_slot   = ULONG_MAX;
      50           0 :   blk->parent_blk_mr = hash_null;
      51           0 :   blk->in_blk_treap  = 0;
      52           0 :   blk->eager         = (!memcmp( dmr, &hash_null, sizeof(fd_mr32_t)) && !meta->invalidated && meta->final==fd_rotor_blk_pool_idx_null( rotor->blk_pool ) && !meta->leader );
      53           0 :   blk->meta_req      = 0;
      54           0 :   blk->highest_req   = 0;
      55           0 :   blk->orphan_req    = 0;
      56           0 :   memset( &blk->telemetry, 0, sizeof(blk->telemetry) );
      57           0 :   blk->telemetry.last_cmpl_fec_idx = UINT_MAX;
      58           0 :   blk->rcvd_fec_ts   = 0L;
      59           0 :   for( ulong k=0UL; k<rotor->fec_blk_max; k++ ) blk->fecs[ k ] = fd_rotor_fec_pool_idx_null( rotor->fec_pool );
      60           0 :   fd_rotor_blk_map_ele_insert( rotor->blk_map, blk, rotor->blk_pool );
      61           0 :   if     ( !memcmp( dmr, &hash_null, sizeof(fd_mr32_t) )    ) meta->eager = fd_rotor_blk_pool_idx( rotor->blk_pool, blk );
      62           0 :   else if( meta->notar==fd_rotor_blk_pool_idx_null( rotor->blk_pool ) ) meta->notar = fd_rotor_blk_pool_idx( rotor->blk_pool, blk );
      63           0 :   blk_treap_insert( rotor, blk );
      64           0 :   return blk;
      65           0 : }
      66             : 
      67             : /* compute_dmr computes the double merkle root of a complete eager blk:
      68             :    the merkle tree over its FEC set roots, then a parent info leaf. */
      69             : 
      70             : static void
      71             : compute_dmr( fd_rotor_t *     rotor,
      72           0 :              fd_rotor_blk_t * blk ) {
      73           0 :   uint fec_set_cnt = blk->cmpl_fec_cnt;
      74             : 
      75           0 :   uchar tree_mem[ FD_BMTREE_COMMIT_FOOTPRINT( 0UL ) ] __attribute__((aligned(FD_BMTREE_COMMIT_ALIGN)));
      76           0 :   fd_bmtree_commit_t * tree = fd_bmtree_commit_init( tree_mem, FD_SHRED_MERKLE_NODE_SZ, FD_BMTREE_LONG_PREFIX_SZ, 0UL );
      77           0 :   for( uint k=0U; k<fec_set_cnt; k++ ) {
      78           0 :     fd_bmtree_node_t leaf[1];
      79           0 :     memcpy( leaf->hash, fd_rotor_fec_pool_ele( rotor->fec_pool, blk->fecs[ k ] )->mr32.uc, sizeof(fd_mr32_t) );
      80           0 :     fd_bmtree_commit_append( tree, leaf, 1UL );
      81           0 :   }
      82             : 
      83           0 :   fd_bmtree_node_t parent_info[1];
      84           0 :   fd_sha256_t      sha[1];
      85           0 :   fd_sha256_init  ( sha );
      86           0 :   fd_sha256_append( sha, &blk->parent_slot,      sizeof(ulong)     );
      87           0 :   fd_sha256_append( sha, blk->parent_blk_mr.uc,  sizeof(fd_mr32_t) );
      88           0 :   fd_sha256_append( sha, &fec_set_cnt,           sizeof(uint)      );
      89           0 :   fd_sha256_fini  ( sha, parent_info->hash );
      90           0 :   fd_bmtree_commit_append( tree, parent_info, 1UL );
      91             : 
      92           0 :   memcpy( blk->dmr.uc, fd_bmtree_commit_fini( tree ), sizeof(fd_mr32_t) );
      93           0 : }
      94             : 
      95             : /* consume pushes buffered FECs onto the reasm queue.  The first push
      96             :    after a reconsume first pushes blk's consumed FECs and its ancestors'
      97             :    onto the head, so its chain goes out again from the root. */
      98             : 
      99             : static void
     100             : consume( fd_rotor_t *     rotor,
     101           0 :          fd_rotor_blk_t * blk ) {
     102           0 :   fd_rotor_slot_meta_t const * meta = fd_rotor_slot_meta( rotor, blk->slot );
     103           0 :   if( FD_UNLIKELY( meta->invalidated && !memcmp( &blk->dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) return;
     104           0 :   if( FD_UNLIKELY( blk->parent==fd_rotor_blk_pool_idx_null( rotor->blk_pool ) ) ) return; /* orphaned, even if partly consumed */
     105           0 :   if( FD_UNLIKELY( !blk->cons_fec_cnt ) ) {
     106           0 :     fd_rotor_blk_t const * parent = fd_rotor_blk_pool_ele_const( rotor->blk_pool, blk->parent );
     107           0 :     if( FD_LIKELY( parent!=rotor->root_blk && ( !parent->cmpl_fec_cnt || parent->cons_fec_cnt!=parent->cmpl_fec_cnt ) ) ) return;
     108           0 :   }
     109           0 :   ulong            idx   = fd_rotor_blk_pool_idx( rotor->blk_pool, blk );
     110           0 :   fd_rotor_blk_t * eager = fd_rotor_blk_pool_ele( rotor->blk_pool, meta->eager );
     111           0 :   if( FD_UNLIKELY( eager && meta->eager!=idx && !meta->invalidated && !memcmp( &eager->dmr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     112           0 :     if( FD_UNLIKELY( eager->parent==fd_rotor_blk_pool_idx_null( rotor->blk_pool ) && blk->fecs[ 0 ]!=fd_rotor_fec_pool_idx_null( rotor->fec_pool ) && eager->fecs[ 0 ]==blk->fecs[ 0 ] ) ) {
     113           0 :       if( FD_LIKELY( eager!=rotor->root_blk && memcmp( &eager->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) fd_rotor_orphan_map_ele_remove_fast( rotor->orphan_map, eager, rotor->blk_pool );
     114           0 :       fd_rotor_blk_t * parent = fd_rotor_blk_pool_ele( rotor->blk_pool, blk->parent );
     115           0 :       eager->parent_slot   = blk->parent_slot;
     116           0 :       eager->parent_blk_mr = blk->parent_blk_mr;
     117           0 :       eager->parent        = blk->parent;
     118           0 :       eager->sibling       = parent->child;
     119           0 :       parent->child        = meta->eager;
     120           0 :     }
     121           0 :     int dup = eager->parent==blk->parent;
     122           0 :     for( uint k=blk->cons_fec_cnt; dup && k<blk->buff_fec_cnt; k++ ) dup = eager->fecs[ k ]==blk->fecs[ k ];
     123           0 :     if( FD_LIKELY( dup ) ) {
     124           0 :       blk = eager;
     125           0 :       idx = meta->eager;
     126           0 :     }
     127           0 :   }
     128           0 :   if( FD_UNLIKELY( rotor->reconsume && blk->cons_fec_cnt<blk->buff_fec_cnt ) ) {
     129           0 :     rotor->reconsume = 0;
     130           0 :     for( fd_rotor_blk_t * anc = blk; anc && anc!=rotor->root_blk; anc = fd_rotor_blk_pool_ele( rotor->blk_pool, anc->parent ) ) {
     131           0 :       for( uint k=anc->cons_fec_cnt; k; k-- ) fd_rotor_deque_push_head( rotor->reasm_deque, (fd_rotor_deque_t){ .blk_idx = (uint)fd_rotor_blk_pool_idx( rotor->blk_pool, anc ), .fec_idx = k-1U } );
     132           0 :     }
     133           0 :   }
     134           0 :   if( FD_UNLIKELY( meta->leader && meta->eager==idx ) ) { /* our leader blk goes first, in FEC order */
     135           0 :     for( uint k=blk->buff_fec_cnt; k>blk->cons_fec_cnt; k-- ) fd_rotor_deque_push_head( rotor->reasm_deque, (fd_rotor_deque_t){ .blk_idx = (uint)idx, .fec_idx = k-1U } );
     136           0 :     blk->cons_fec_cnt = blk->buff_fec_cnt;
     137           0 :     return;
     138           0 :   }
     139           0 :   for( ; blk->cons_fec_cnt<blk->buff_fec_cnt; blk->cons_fec_cnt++ ) fd_rotor_deque_push_tail( rotor->reasm_deque, (fd_rotor_deque_t){ .blk_idx = (uint)idx, .fec_idx = blk->cons_fec_cnt } );
     140           0 : }
     141             : 
     142             : /* cascade performs a stackless (ie. O(1) space) preorder traversal. */
     143             : 
     144             : static void
     145             : cascade( fd_rotor_t *     rotor,
     146           0 :          fd_rotor_blk_t * root ) {
     147           0 :   fd_rotor_blk_t * pool = rotor->blk_pool;
     148           0 :   ulong            null = fd_rotor_blk_pool_idx_null( pool );
     149           0 :   ulong            tidx = fd_rotor_blk_pool_idx( pool, root );
     150           0 :   ulong            idx  = root->child;
     151           0 :   while( FD_LIKELY( idx!=null ) ) {
     152           0 :     fd_rotor_blk_t * blk = fd_rotor_blk_pool_ele( pool, idx );
     153           0 :     consume( rotor, blk );
     154           0 :     if( FD_LIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt && blk->child!=null ) ) { idx = blk->child; continue; }
     155           0 :     while( FD_LIKELY( idx!=tidx && fd_rotor_blk_pool_ele( pool, idx )->sibling==null ) ) idx = fd_rotor_blk_pool_ele( pool, idx )->parent;
     156           0 :     if( FD_UNLIKELY( idx==tidx ) ) break;
     157           0 :     idx = fd_rotor_blk_pool_ele( pool, idx )->sibling;
     158           0 :   }
     159           0 : }
     160             : 
     161             : /* promote gives blk the slot_meta role (notar or final), moving it to
     162             :    that role's treap if it was in one.  A blk with a role is not
     163             :    repaired by position. */
     164             : 
     165             : static void
     166             : promote( fd_rotor_t *     rotor,
     167             :          fd_rotor_blk_t * blk,
     168           0 :          ulong *          role ) {
     169           0 :   if( FD_LIKELY( *role==fd_rotor_blk_pool_idx( rotor->blk_pool, blk ) ) ) return;
     170           0 :   int in_blk_treap = blk->in_blk_treap;
     171           0 :   blk_treap_remove( rotor, blk );
     172           0 :   *role      = fd_rotor_blk_pool_idx( rotor->blk_pool, blk );
     173           0 :   blk->eager = 0;
     174           0 :   if( FD_LIKELY( in_blk_treap ) ) blk_treap_insert( rotor, blk );
     175           0 : }
     176             : 
     177             : /* connect_ancestors links a notar or final blk to its ancestors via
     178             :    parent dmr, creating the ones not held, up to the root or a blk whose
     179             :    parent is not known.
     180             : 
     181             :    For every blk B that is notar or final and whose header is known, the
     182             :    blk B names must be held, B must be linked under it, and if B is
     183             :    final so must it be. Then apply the same rule to that parent.
     184             : 
     185             :    If the parent was just created, the blk must be re-added to the
     186             :    treap. It's the responsibility of the blk's presence in the treap for
     187             :    the tile to make orphan requests; NOT the parent, which only gets a
     188             :    META request.
     189             : 
     190             :    This should be called anytime a blk becomes notar or final and its
     191             :    header is known, or vice versa.
     192             : 
     193             :    Only the highest blk linked here (else blk itself) can have become
     194             :    consumable: the blks above were linked before and their FEC sets are
     195             :    untouched, the blks below are reached by its cascade.  */
     196             : 
     197             : static void
     198             : connect_ancestors( fd_rotor_t *     rotor,
     199           0 :                    fd_rotor_blk_t * blk ) {
     200           0 :   fd_rotor_blk_t * pool = rotor->blk_pool;
     201           0 :   ulong            null = fd_rotor_blk_pool_idx_null( pool );
     202           0 :   fd_rotor_blk_t * top  = blk;
     203           0 :   for(;;) {
     204           0 :     if( FD_UNLIKELY( blk->parent_slot==ULONG_MAX || blk->parent_slot<rotor->root ) ) break;
     205           0 :     if( FD_UNLIKELY( blk->parent_slot!=rotor->root && !memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) break;
     206           0 :     fd_rotor_slot_meta_t * meta   = fd_rotor_slot_meta( rotor, blk->slot );
     207           0 :     fd_rotor_slot_meta_t * pmeta  = fd_rotor_slot_meta( rotor, blk->parent_slot );
     208           0 :     int                    final  = meta->final==fd_rotor_blk_pool_idx( pool, blk );
     209           0 :     fd_rotor_blk_t *       parent = fd_rotor_blk_map_ele_query( rotor->blk_map, &blk->parent_slot, NULL, pool );
     210           0 :     while( parent && memcmp( &parent->dmr, &blk->parent_blk_mr, sizeof(fd_mr32_t) ) ) parent = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( parent, NULL, pool );
     211           0 :     if( FD_UNLIKELY( !parent && blk->parent_slot==rotor->root ) ) break; /* names a root other than ours */
     212           0 :     int held = !!parent;
     213           0 :     if( FD_UNLIKELY( !parent ) ) parent = fd_rotor_blk_notarized( rotor, blk->parent_slot, &blk->parent_blk_mr );
     214           0 :     if( FD_UNLIKELY( !parent ) ) break; /* the parent slot is settled otherwise */
     215           0 :     if( FD_UNLIKELY( !held   ) ) { /* link_children linked blk under the new parent */
     216           0 :       blk_treap_insert( rotor, blk ); /* its tile asks ORPHAN for eager ancestry, which may merge with the named parents */
     217           0 :       top = blk;
     218           0 :     }
     219           0 :     if( FD_UNLIKELY( blk->parent==null ) ) {
     220           0 :       if( FD_LIKELY( blk!=rotor->root_blk /* && blk->parent==null */ && memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     221           0 :         fd_rotor_orphan_map_ele_remove_fast( rotor->orphan_map, blk, pool );
     222           0 :       }
     223           0 :       blk->parent   = fd_rotor_blk_pool_idx( pool, parent );
     224           0 :       blk->sibling  = parent->child;
     225           0 :       parent->child = fd_rotor_blk_pool_idx( pool, blk );
     226           0 :       top           = blk;
     227           0 :     }
     228           0 :     if( FD_UNLIKELY( final && pmeta->final==null ) ) promote( rotor, parent, &pmeta->final );
     229           0 :     blk = parent;
     230           0 :   }
     231           0 :   consume( rotor, top );
     232           0 :   if( FD_UNLIKELY( top->cmpl_fec_cnt && top->cons_fec_cnt==top->cmpl_fec_cnt ) ) cascade( rotor, top );
     233           0 : }
     234             : 
     235             : /* link_parent links an unlinked blk whose header is known under the
     236             :    held blk it names, if any.  Returns the parent linked under. */
     237             : 
     238             : static fd_rotor_blk_t *
     239             : link_parent( fd_rotor_t *     rotor,
     240           0 :              fd_rotor_blk_t * blk ) {
     241           0 :   fd_rotor_blk_t * pool = rotor->blk_pool;
     242           0 :   if( FD_UNLIKELY( blk->parent_slot==ULONG_MAX ) ) return NULL;
     243           0 :   if( FD_UNLIKELY( blk->parent_slot!=rotor->root && !memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) return NULL;
     244             : 
     245           0 :   fd_rotor_blk_t * parent = fd_rotor_blk_map_ele_query( rotor->blk_map, &blk->parent_slot, NULL, pool );
     246           0 :   while( parent && memcmp( &parent->dmr, &blk->parent_blk_mr, sizeof(fd_mr32_t) ) ) parent = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( parent, NULL, pool );
     247           0 :   if( FD_LIKELY( !parent ) ) return NULL;
     248             : 
     249           0 :   if( FD_LIKELY( blk!=rotor->root_blk && blk->parent==fd_rotor_blk_pool_idx_null( pool ) && memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     250           0 :     fd_rotor_orphan_map_ele_remove_fast( rotor->orphan_map, blk, pool );
     251           0 :   }
     252             : 
     253           0 :   blk->parent   = fd_rotor_blk_pool_idx( pool, parent );
     254           0 :   blk->sibling  = parent->child;
     255           0 :   parent->child = fd_rotor_blk_pool_idx( pool, blk );
     256           0 :   return parent;
     257           0 : }
     258             : 
     259             : /* link_children links the orphans naming parent's (slot, dmr) under
     260             :    it: their headers named it before it was held, a notar blk just
     261             :    created or an eager blk that just completed.
     262             :    A final child makes the parent final, as connect_ancestors would. */
     263             : 
     264             : static void
     265             : link_children( fd_rotor_t *     rotor,
     266           0 :                fd_rotor_blk_t * parent ) {
     267           0 :   fd_rotor_blk_t *       pool  = rotor->blk_pool;
     268           0 :   ulong                  null  = fd_rotor_blk_pool_idx_null( pool );
     269           0 :   fd_rotor_slot_meta_t * pmeta = fd_rotor_slot_meta( rotor, parent->slot );
     270           0 :   fd_rotor_blk_t *       next;
     271           0 :   for( fd_rotor_blk_t * blk = fd_rotor_orphan_map_ele_query( rotor->orphan_map, &parent->dmr, NULL, pool );
     272           0 :                         blk;
     273           0 :                         blk = next ) {
     274           0 :     next = (fd_rotor_blk_t *)fd_rotor_orphan_map_ele_next_const( blk, NULL, pool );
     275           0 :     if( FD_UNLIKELY( blk->parent_slot!=parent->slot ) ) continue;
     276           0 :     fd_rotor_orphan_map_ele_remove_fast( rotor->orphan_map, blk, pool );
     277           0 :     blk->parent   = fd_rotor_blk_pool_idx( pool, parent );
     278           0 :     blk->sibling  = parent->child;
     279           0 :     parent->child = fd_rotor_blk_pool_idx( pool, blk );
     280           0 :     if( FD_UNLIKELY( fd_rotor_slot_meta( rotor, blk->slot )->final==fd_rotor_blk_pool_idx( pool, blk ) && pmeta->final==null ) ) promote( rotor, parent, &pmeta->final );
     281           0 :   }
     282           0 : }
     283             : 
     284             : static void
     285             : unlink( fd_rotor_t *     rotor,
     286           0 :         fd_rotor_blk_t * blk ) {
     287           0 :   fd_rotor_blk_t * pool = rotor->blk_pool;
     288           0 :   ulong            null = fd_rotor_blk_pool_idx_null( pool );
     289           0 :   ulong            idx  = fd_rotor_blk_pool_idx( pool, blk );
     290           0 :   if( FD_LIKELY( blk->parent!=null ) ) {
     291           0 :     ulong * link = &fd_rotor_blk_pool_ele( pool, blk->parent )->child;
     292           0 :     while( *link!=idx ) link = &fd_rotor_blk_pool_ele( pool, *link )->sibling;
     293           0 :     *link = blk->sibling;
     294           0 :   }
     295           0 :   ulong next;
     296           0 :   for( ulong c=blk->child; c!=null; c=next ) {
     297           0 :     fd_rotor_blk_t * child = fd_rotor_blk_pool_ele( pool, c );
     298           0 :     next           = child->sibling;
     299           0 :     child->parent  = null;
     300           0 :     child->sibling = null;
     301           0 :     if( FD_LIKELY( child!=rotor->root_blk /* && child->parent==null */ && memcmp( &child->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     302           0 :       fd_rotor_orphan_map_ele_insert( rotor->orphan_map, child, pool ); /* waits for blk's dmr again, should it be held again */
     303           0 :     }
     304           0 :   }
     305           0 : }
     306             : 
     307             : static void
     308             : prune( fd_rotor_t *     rotor,
     309           0 :        fd_rotor_blk_t * blk ) {
     310           0 :   fd_rotor_blk_t * pool = rotor->blk_pool;
     311           0 :   if( FD_UNLIKELY( !blk->telemetry.reported && rotor->telemetry.report ) ) rotor->telemetry.report( rotor->telemetry.ctx, blk );
     312           0 :   for( ulong k=0UL; k<rotor->fec_blk_max; k++ ) {
     313           0 :     if( FD_LIKELY( blk->fecs[ k ]==fd_rotor_fec_pool_idx_null( rotor->fec_pool ) ) ) continue;
     314           0 :     int held = 0;
     315           0 :     for( fd_rotor_blk_t const * other = fd_rotor_blk_map_ele_query_const( rotor->blk_map, &blk->slot, NULL, pool );
     316           0 :                                 other && !held;
     317           0 :                                 other = fd_rotor_blk_map_ele_next_const( other, NULL, pool ) ) {
     318           0 :       held = other!=blk && other->fecs[ k ]==blk->fecs[ k ];
     319           0 :     }
     320           0 :     if( FD_UNLIKELY( held ) ) continue;
     321           0 :     fd_rotor_fec_t * fec = fd_rotor_fec_pool_ele( rotor->fec_pool, blk->fecs[ k ] );
     322           0 :     fd_rotor_fec_map_ele_remove_fast( rotor->fec_map, fec, rotor->fec_pool );
     323           0 :     fd_rotor_fec_pool_ele_release   ( rotor->fec_pool, fec );
     324           0 :   }
     325           0 :   unlink( rotor, blk );
     326           0 :   if( FD_UNLIKELY( blk!=rotor->root_blk && blk->parent==fd_rotor_blk_pool_idx_null( pool ) && memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     327           0 :     fd_rotor_orphan_map_ele_remove_fast( rotor->orphan_map, blk, pool );
     328           0 :   }
     329           0 :   blk_treap_remove( rotor, blk );
     330           0 :   fd_rotor_slot_meta_t * meta = fd_rotor_slot_meta( rotor, blk->slot );
     331           0 :   if( FD_UNLIKELY( meta->eager==fd_rotor_blk_pool_idx( pool, blk ) ) ) meta->eager = fd_rotor_blk_pool_idx_null( pool );
     332           0 :   if( FD_UNLIKELY( meta->notar==fd_rotor_blk_pool_idx( pool, blk ) ) ) meta->notar = fd_rotor_blk_pool_idx_null( pool );
     333           0 :   if( FD_UNLIKELY( meta->final==fd_rotor_blk_pool_idx( pool, blk ) ) ) meta->final = fd_rotor_blk_pool_idx_null( pool );
     334           0 :   fd_rotor_blk_map_ele_remove_fast( rotor->blk_map, blk, pool );
     335           0 :   fd_rotor_blk_pool_ele_release   ( pool, blk );
     336           0 : }
     337             : 
     338             : /* dedup checks for a duplicate of eager among the notar blks.  Only the
     339             :    eager blk can be duplicated, since notar blk DMRs are known a priori.
     340             : 
     341             :    Merge strategy if a duplicate is found:
     342             :    - eager is kept, notar is freed
     343             :    - eager inherits notar's children
     344             :    - eager takes notar's parent if it has none, as same DMR implies same
     345             :      parent
     346             : 
     347             :    Replay builds one bank for the block until then: consume holds notar
     348             :    behind eager, which delivers the FEC sets they share. */
     349             : 
     350             : static void
     351             : dedup( fd_rotor_t *     rotor,
     352             :        fd_rotor_blk_t * eager,
     353           0 :        long             ts ) {
     354           0 :   fd_rotor_blk_t * pool = rotor->blk_pool;
     355           0 :   ulong            null = fd_rotor_blk_pool_idx_null( pool );
     356           0 :   ulong            idx  = fd_rotor_blk_pool_idx( pool, eager );
     357             : 
     358           0 :   fd_rotor_blk_t * notar = fd_rotor_blk_map_ele_query( rotor->blk_map, &eager->slot, NULL, pool );
     359           0 :   while( notar && ( notar==eager || memcmp( &notar->dmr, &eager->dmr, sizeof(fd_mr32_t) ) ) ) {
     360           0 :     notar = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( notar, NULL, pool );
     361           0 :   }
     362           0 :   fd_rotor_slot_meta_t * meta = fd_rotor_slot_meta( rotor, eager->slot );
     363           0 :   if( FD_UNLIKELY( !notar && meta->final!=null ) ) {
     364           0 :     eager->telemetry.cancelled_ts     = fd_long_if( !eager->telemetry.cancelled_reason, ts, eager->telemetry.cancelled_ts );
     365           0 :     eager->telemetry.cancelled_reason = (uchar)FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_NOTARIZED_VERSION;
     366           0 :     prune( rotor, eager );
     367           0 :     return;
     368           0 :   } /* turbine built a blk the cluster did not finalize */
     369           0 :   if( FD_LIKELY( !notar ) ) return;
     370             : 
     371           0 :   ulong next;
     372           0 :   for( ulong c=notar->child; c!=null; c=next ) {
     373           0 :     next                                      = fd_rotor_blk_pool_ele( pool, c )->sibling;
     374           0 :     fd_rotor_blk_pool_ele( pool, c )->parent  = idx;
     375           0 :     fd_rotor_blk_pool_ele( pool, c )->sibling = eager->child;
     376           0 :     eager->child                              = c;
     377           0 :   }
     378           0 :   notar->child = null;
     379           0 :   if( FD_UNLIKELY( notar->parent!=null && eager->parent==null ) ) {
     380           0 :     if( FD_LIKELY( eager!=rotor->root_blk /* && eager->parent==null */ && memcmp( &eager->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     381           0 :       fd_rotor_orphan_map_ele_remove_fast( rotor->orphan_map, eager, pool );
     382           0 :     }
     383           0 :     eager->parent                                       = notar->parent;
     384           0 :     eager->sibling                                      = fd_rotor_blk_pool_ele( pool, notar->parent )->child;
     385           0 :     fd_rotor_blk_pool_ele( pool, notar->parent )->child = idx;
     386           0 :   }
     387           0 :   blk_treap_remove( rotor, notar );
     388           0 :   if( FD_UNLIKELY( meta->final==fd_rotor_blk_pool_idx( pool, notar ) ) ) { meta->final = null; promote( rotor, eager, &meta->final ); }
     389           0 :   if( FD_UNLIKELY( meta->notar==fd_rotor_blk_pool_idx( pool, notar ) ) ) { meta->notar = null; promote( rotor, eager, &meta->notar ); }
     390           0 :   notar->telemetry.reported = 1; /* the same block lives on as eager, we won't report two different lines for this */
     391             : 
     392           0 :   eager->telemetry.req_window_cnt    += notar->telemetry.req_window_cnt;
     393           0 :   eager->telemetry.req_highest_cnt   += notar->telemetry.req_highest_cnt;
     394           0 :   eager->telemetry.req_orphan_cnt    += notar->telemetry.req_orphan_cnt;
     395           0 :   eager->telemetry.req_shred_bid_cnt += notar->telemetry.req_shred_bid_cnt;
     396           0 :   eager->telemetry.req_parent_cnt    += notar->telemetry.req_parent_cnt;
     397           0 :   eager->telemetry.req_fec_root_cnt  += notar->telemetry.req_fec_root_cnt;
     398           0 :   eager->telemetry.shred_res_cnt     += notar->telemetry.shred_res_cnt;
     399           0 :   eager->telemetry.parent_res_cnt    += notar->telemetry.parent_res_cnt;
     400           0 :   eager->telemetry.fec_root_res_cnt  += notar->telemetry.fec_root_res_cnt;
     401             :   /* first timestamps */
     402           0 :   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      );
     403           0 :   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 );
     404           0 :   eager->telemetry.last_shred_res_ts  = fd_long_max( eager->telemetry.last_shred_res_ts, notar->telemetry.last_shred_res_ts );
     405           0 :   prune( rotor, notar );
     406           0 :   connect_ancestors( rotor, eager );
     407           0 : }
     408             : 
     409             : FD_FN_CONST ulong
     410           0 : fd_rotor_align( void ) {
     411           0 :   return fd_ulong_max( alignof(fd_rotor_t), 128UL );
     412           0 : }
     413             : 
     414             : FD_FN_CONST ulong
     415             : fd_rotor_footprint( ulong slot_max,
     416             :                     ulong fec_max,
     417           0 :                     ulong fec_blk_max ) {
     418           0 :   ulong blk_max = fd_rotor_blk_max( slot_max );
     419           0 :   return FD_LAYOUT_FINI(
     420           0 :     FD_LAYOUT_APPEND(
     421           0 :     FD_LAYOUT_APPEND(
     422           0 :     FD_LAYOUT_APPEND(
     423           0 :     FD_LAYOUT_APPEND(
     424           0 :     FD_LAYOUT_APPEND(
     425           0 :     FD_LAYOUT_APPEND(
     426           0 :     FD_LAYOUT_APPEND(
     427           0 :     FD_LAYOUT_APPEND(
     428           0 :     FD_LAYOUT_APPEND(
     429           0 :     FD_LAYOUT_APPEND(
     430           0 :     FD_LAYOUT_APPEND(
     431           0 :     FD_LAYOUT_APPEND(
     432           0 :     FD_LAYOUT_INIT,
     433           0 :       fd_rotor_align(),              sizeof(fd_rotor_t)                                                        ),
     434           0 :       alignof(fd_rotor_slot_meta_t), sizeof(fd_rotor_slot_meta_t)*slot_max                                     ),
     435           0 :       fd_rotor_blk_pool_align(),     fd_rotor_blk_pool_footprint ( blk_max                                   ) ),
     436           0 :       alignof(ulong),                sizeof(ulong)*blk_max*fec_blk_max                                         ),
     437           0 :       fd_rotor_blk_map_align(),      fd_rotor_blk_map_footprint   ( fd_rotor_blk_map_chain_cnt_est( blk_max ) ) ),
     438           0 :       fd_rotor_orphan_map_align(),   fd_rotor_orphan_map_footprint( fd_rotor_orphan_map_chain_cnt_est( blk_max ) ) ),
     439           0 :       fd_rotor_fec_pool_align(),     fd_rotor_fec_pool_footprint  ( fec_max                                   ) ),
     440           0 :       fd_rotor_fec_map_align(),      fd_rotor_fec_map_footprint   ( fd_rotor_fec_map_chain_cnt_est( fec_max ) ) ),
     441           0 :       fd_rotor_deque_align(),        fd_rotor_deque_footprint     ( fec_max                                   ) ),
     442           0 :       fd_rotor_treap_align(),        fd_rotor_treap_footprint     ( blk_max                                   ) ),
     443           0 :       fd_rotor_treap_align(),        fd_rotor_treap_footprint     ( blk_max                                   ) ),
     444           0 :       fd_rotor_treap_align(),        fd_rotor_treap_footprint     ( blk_max                                   ) ),
     445           0 :     fd_rotor_align() );
     446           0 : }
     447             : 
     448             : void *
     449             : fd_rotor_new( void * shmem,
     450             :               ulong  slot_max,
     451             :               ulong  fec_max,
     452             :               ulong  fec_blk_max,
     453           0 :               ulong  seed ) {
     454           0 :   ulong footprint = fd_rotor_footprint( slot_max, fec_max, fec_blk_max );
     455           0 :   if( FD_UNLIKELY( !footprint || fec_blk_max<FD_FEC_BLK_MAX || fec_blk_max>UINT_MAX ) ) {
     456           0 :     FD_LOG_WARNING(( "bad footprint: %lu %lu %lu", slot_max, fec_max, fec_blk_max ));
     457           0 :     return NULL;
     458           0 :   }
     459             : 
     460           0 :   fd_memset( shmem, 0, footprint );
     461           0 :   fd_rotor_t * rotor;
     462             : 
     463           0 :   ulong blk_max = fd_rotor_blk_max( slot_max );
     464             : 
     465           0 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     466           0 :   rotor              = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_align(),              sizeof(fd_rotor_t)                                                        );
     467           0 :   void * slot_meta   = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_rotor_slot_meta_t), sizeof(fd_rotor_slot_meta_t)*slot_max                                     );
     468           0 :   void * blk_pool    = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_blk_pool_align(),     fd_rotor_blk_pool_footprint ( blk_max                                   ) );
     469           0 :   ulong * blk_fecs   = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong),                sizeof(ulong)*blk_max*fec_blk_max                                         );
     470           0 :   void * blk_map     = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_blk_map_align(),      fd_rotor_blk_map_footprint  ( fd_rotor_blk_map_chain_cnt_est( blk_max ) ) );
     471           0 :   void * orphan_map  = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_orphan_map_align(),   fd_rotor_orphan_map_footprint( fd_rotor_orphan_map_chain_cnt_est( blk_max ) ) );
     472           0 :   void * fec_pool    = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_fec_pool_align(),     fd_rotor_fec_pool_footprint ( fec_max                                   ) );
     473           0 :   void * fec_map     = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_fec_map_align(),      fd_rotor_fec_map_footprint  ( fd_rotor_fec_map_chain_cnt_est( fec_max ) ) );
     474           0 :   void * reasm_deque = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_deque_align(),        fd_rotor_deque_footprint    ( fec_max                                   ) );
     475           0 :   void * eager_treap = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_treap_align(),        fd_rotor_treap_footprint    ( blk_max                                   ) );
     476           0 :   void * notar_treap = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_treap_align(),        fd_rotor_treap_footprint    ( blk_max                                   ) );
     477           0 :   void * final_treap = FD_SCRATCH_ALLOC_APPEND( l, fd_rotor_treap_align(),        fd_rotor_treap_footprint    ( blk_max                                   ) );
     478           0 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_rotor_align() ) == (ulong)shmem + footprint );
     479             : 
     480           0 :   rotor->root        = ULONG_MAX;
     481           0 :   rotor->slot_max    = slot_max;
     482           0 :   rotor->fec_blk_max = fec_blk_max;
     483           0 :   rotor->slot_meta   = (fd_rotor_slot_meta_t *)slot_meta;
     484           0 :   for( ulong i=0UL; i<slot_max; i++ ) rotor->slot_meta[ i ].slot = ULONG_MAX;
     485           0 :   rotor->blk_pool    = fd_rotor_blk_pool_join( fd_rotor_blk_pool_new( blk_pool,    blk_max                                         ) );
     486           0 :   for( ulong i=0UL; i<blk_max; i++ ) rotor->blk_pool[ i ].fecs = blk_fecs + i*fec_blk_max;
     487           0 :   rotor->blk_map     = fd_rotor_blk_map_join ( fd_rotor_blk_map_new ( blk_map,     fd_rotor_blk_map_chain_cnt_est( blk_max ), seed ) );
     488           0 :   rotor->orphan_map  = fd_rotor_orphan_map_join( fd_rotor_orphan_map_new( orphan_map, fd_rotor_orphan_map_chain_cnt_est( blk_max ), seed ) );
     489           0 :   rotor->fec_pool    = fd_rotor_fec_pool_join( fd_rotor_fec_pool_new( fec_pool,    fec_max                                         ) );
     490           0 :   rotor->fec_map     = fd_rotor_fec_map_join ( fd_rotor_fec_map_new ( fec_map,     fd_rotor_fec_map_chain_cnt_est( fec_max ), seed ) );
     491           0 :   rotor->reasm_deque = fd_rotor_deque_join   ( fd_rotor_deque_new   ( reasm_deque, fec_max                                         ) );
     492           0 :   fd_rotor_treap_seed( rotor->blk_pool, blk_max, seed ); /* the treaps share each blk's prio */
     493           0 :   rotor->eager_treap = fd_rotor_treap_join( fd_rotor_treap_new( eager_treap, blk_max ) );
     494           0 :   rotor->notar_treap = fd_rotor_treap_join( fd_rotor_treap_new( notar_treap, blk_max ) );
     495           0 :   rotor->final_treap = fd_rotor_treap_join( fd_rotor_treap_new( final_treap, blk_max ) );
     496             : 
     497           0 :   return shmem;
     498           0 : }
     499             : 
     500             : fd_rotor_t *
     501           0 : fd_rotor_join( void * shrotor ) {
     502           0 :   return (fd_rotor_t *)shrotor;
     503           0 : }
     504             : 
     505             : void *
     506           0 : fd_rotor_leave( fd_rotor_t const * rotor ) {
     507           0 :   return (void *)rotor;
     508           0 : }
     509             : 
     510             : void *
     511           0 : fd_rotor_delete( void * shrotor ) {
     512           0 :   return shrotor;
     513           0 : }
     514             : 
     515             : void
     516             : fd_rotor_init( fd_rotor_t *         rotor,
     517             :                ulong                root,
     518             :                fd_mr32_t const *    root_blk_mr,
     519             :                fd_rotor_report_fn_t report,
     520           0 :                void *               report_ctx ) {
     521           0 :   rotor->telemetry.report = report;
     522           0 :   rotor->telemetry.ctx    = report_ctx;
     523           0 :   fd_rotor_blk_t * blk = blk_insert( rotor, root, root_blk_mr );
     524           0 :   blk->telemetry.reported = 1; /* the root is not a received block */
     525           0 :   blk_treap_remove( rotor, blk ); /* the root is never repaired */
     526           0 :   fd_rotor_slot_meta( rotor, root )->final = fd_rotor_blk_pool_idx( rotor->blk_pool, blk );
     527           0 :   fd_rotor_slot_meta( rotor, root )->eager = fd_rotor_blk_pool_idx_null( rotor->blk_pool ); /* the genesis root has a null dmr, but is not turbine's */
     528           0 :   blk->eager          = 0;
     529           0 :   rotor->root         = root;
     530           0 :   rotor->root_blk     = blk;
     531           0 :   rotor->catchup_slot = root;
     532           0 : }
     533             : 
     534             : void
     535           0 : fd_rotor_fini( fd_rotor_t * rotor ) {
     536           0 :   for( ulong i=0UL; i<rotor->slot_max; i++ ) rotor->slot_meta[ i ].slot = ULONG_MAX;
     537           0 :   fd_rotor_blk_map_reset( rotor->blk_map );
     538           0 :   fd_rotor_orphan_map_reset( rotor->orphan_map );
     539           0 :   fd_rotor_blk_pool_reset( rotor->blk_pool );
     540           0 :   fd_rotor_fec_map_reset( rotor->fec_map );
     541           0 :   fd_rotor_fec_pool_reset( rotor->fec_pool );
     542           0 :   fd_rotor_deque_remove_all( rotor->reasm_deque );
     543           0 :   rotor->eager_treap = fd_rotor_treap_join( fd_rotor_treap_new( fd_rotor_treap_leave( rotor->eager_treap ), fd_rotor_blk_pool_max( rotor->blk_pool ) ) );
     544           0 :   rotor->notar_treap = fd_rotor_treap_join( fd_rotor_treap_new( fd_rotor_treap_leave( rotor->notar_treap ), fd_rotor_blk_pool_max( rotor->blk_pool ) ) );
     545           0 :   rotor->final_treap = fd_rotor_treap_join( fd_rotor_treap_new( fd_rotor_treap_leave( rotor->final_treap ), fd_rotor_blk_pool_max( rotor->blk_pool ) ) );
     546           0 :   rotor->root         = ULONG_MAX;
     547           0 :   rotor->root_blk     = NULL;
     548           0 :   rotor->catchup_slot = 0UL;
     549           0 :   rotor->reconsume    = 0;
     550           0 : }
     551             : 
     552             : void
     553             : fd_rotor_blk_dead( fd_rotor_t *      rotor,
     554             :                    ulong             slot,
     555           0 :                    fd_mr32_t const * blk_mr ) {
     556           0 :   fd_rotor_blk_t * dead = fd_rotor_blk_map_ele_query( rotor->blk_map, &slot, NULL, rotor->blk_pool );
     557           0 :   while( dead && memcmp( &dead->dmr, blk_mr, sizeof(fd_mr32_t) ) ) dead = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( dead, NULL, rotor->blk_pool );
     558           0 :   if( FD_UNLIKELY( !dead ) ) return;
     559             : 
     560           0 :   fd_rotor_slot_meta_t * meta   = fd_rotor_slot_meta( rotor, slot );
     561           0 :   int                    eager  = meta->eager==fd_rotor_blk_pool_idx( rotor->blk_pool, dead );
     562           0 :   ulong                  parent = dead->parent;
     563           0 :   if( FD_UNLIKELY( eager ) ) meta->invalidated = 1; /* turbine must not rebuild it */
     564           0 :   prune( rotor, dead );
     565           0 :   if( FD_UNLIKELY( eager && parent!=fd_rotor_blk_pool_idx_null( rotor->blk_pool ) ) ) cascade( rotor, fd_rotor_blk_pool_ele( rotor->blk_pool, parent ) ); /* delivers the notar blks held behind it */
     566           0 : }
     567             : 
     568             : fd_rotor_blk_t *
     569             : fd_rotor_blk_finalized( fd_rotor_t *      rotor,
     570             :                         ulong             slot,
     571             :                         fd_mr32_t const * blk_mr,
     572           0 :                         long              ts ) {
     573           0 :   fd_rotor_slot_meta_t * meta = fd_rotor_slot_meta( rotor, slot );
     574             : 
     575           0 :   fd_rotor_blk_t * fin = fd_rotor_blk_map_ele_query( rotor->blk_map, &slot, NULL, rotor->blk_pool );
     576           0 :   while( fin && memcmp( &fin->dmr, blk_mr, sizeof(fd_mr32_t) ) ) fin = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( fin, NULL, rotor->blk_pool );
     577           0 :   if( FD_UNLIKELY( !fin ) ) fin = fd_rotor_blk_notarized( rotor, slot, blk_mr );
     578           0 :   if( FD_UNLIKELY( !fin ) ) return NULL; /* the slot is already finalized with another blk */
     579             : 
     580           0 :   fd_rotor_blk_t * pool = rotor->blk_pool;
     581           0 :   promote( rotor, fin, &meta->final );
     582             :   /* The eager blk keeps its positional repair: peers without the whole
     583             :      block still serve its shreds, which by-id repair cannot ask them for. */
     584             : 
     585             :   /* Remove all equivocating blocks except the finalized one.  Their
     586             :      children name them, not the finalized blk, and are unlinked. */
     587             : 
     588           0 :   fd_rotor_blk_t * next;
     589           0 :   for( fd_rotor_blk_t * blk = fd_rotor_blk_map_ele_query( rotor->blk_map, &slot, NULL, pool );
     590           0 :                         blk;
     591           0 :                         blk = next ) {
     592           0 :     next = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( blk, NULL, pool );
     593           0 :     if( FD_LIKELY( blk==fin ) ) continue;
     594           0 :     if( FD_UNLIKELY( meta->eager==fd_rotor_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 */
     595           0 :     blk->telemetry.cancelled_ts     = fd_long_if( !blk->telemetry.cancelled_reason, ts, blk->telemetry.cancelled_ts );
     596           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 */
     597           0 :     prune( rotor, blk );
     598           0 :   }
     599           0 :   connect_ancestors( rotor, fin );
     600           0 :   return fin;
     601           0 : }
     602             : 
     603             : fd_rotor_blk_t *
     604             : fd_rotor_blk_notarized( fd_rotor_t *      rotor,
     605             :                         ulong             slot,
     606           0 :                         fd_mr32_t const * blk_mr ) {
     607           0 :   fd_rotor_slot_meta_t *       meta = fd_rotor_slot_meta( rotor, slot );
     608           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 */
     609           0 :   if( FD_UNLIKELY( meta->final!=fd_rotor_blk_pool_idx_null( rotor->blk_pool ) || meta->skipped ) ) return NULL; /* the slot is settled */
     610           0 :   for( fd_rotor_blk_t * dup = fd_rotor_blk_map_ele_query( rotor->blk_map, &slot, NULL, rotor->blk_pool );
     611           0 :                         dup;
     612           0 :                         dup = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( dup, NULL, rotor->blk_pool ) ) {
     613           0 :     if( FD_UNLIKELY( memcmp( &dup->dmr, blk_mr, sizeof(fd_mr32_t) ) ) ) continue;
     614           0 :     if( FD_LIKELY( meta->notar==fd_rotor_blk_pool_idx_null( rotor->blk_pool ) ) ) promote( rotor, dup, &meta->notar ); /* already have it, now named */
     615           0 :     connect_ancestors( rotor, dup );
     616           0 :     return NULL;
     617           0 :   }
     618           0 :   fd_rotor_blk_t * blk = blk_insert( rotor, slot, blk_mr );
     619           0 :   link_children( rotor, blk );
     620           0 :   return blk;
     621           0 : }
     622             : 
     623             : fd_rotor_blk_t *
     624             : fd_rotor_blk_parented( fd_rotor_t *      rotor,
     625             :                        ulong             slot,
     626             :                        fd_mr32_t const * blk_mr,
     627             :                        ulong             parent_slot,
     628             :                        fd_mr32_t const * parent_blk_mr,
     629             :                        uint              fec_set_cnt,
     630           0 :                        long              ts ) {
     631           0 :   fd_rotor_blk_t * blk = fd_rotor_blk_map_ele_query( rotor->blk_map, &slot, NULL, rotor->blk_pool );
     632           0 :   while( FD_LIKELY( blk && memcmp( &blk->dmr, blk_mr, sizeof(fd_mr32_t) ) ) ) blk = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( blk, NULL, rotor->blk_pool );
     633           0 :   if( FD_UNLIKELY( !blk                         ) ) return NULL; /* blk was pruned while the response was in flight */
     634           0 :   blk->telemetry.parent_res_cnt++;
     635           0 :   blk->telemetry.first_meta_res_ts = fd_long_if( !blk->telemetry.first_meta_res_ts, ts, blk->telemetry.first_meta_res_ts );
     636             : 
     637           0 :   if( FD_UNLIKELY( blk->parent_slot!=ULONG_MAX ) ) return NULL; /* duplicate response, eg. a retried request */
     638           0 :   if( FD_UNLIKELY( parent_slot>=slot           ) ) return NULL; /* a blk's parent precedes it */
     639             : 
     640           0 :   blk->parent_slot   = parent_slot;
     641           0 :   blk->parent_blk_mr = *parent_blk_mr;
     642           0 :   blk->cmpl_fec_cnt  = fec_set_cnt;
     643           0 :   blk_treap_insert( rotor, blk ); /* its FEC sets can be discovered now */
     644             : 
     645           0 :   if( FD_LIKELY( blk!=rotor->root_blk && blk->parent==fd_rotor_blk_pool_idx_null( rotor->blk_pool ) && memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     646           0 :     fd_rotor_orphan_map_ele_insert( rotor->orphan_map, blk, rotor->blk_pool ); /* names a parent now, link_parent takes it out if the parent is held */
     647           0 :   }
     648             : 
     649           0 :   fd_rotor_blk_t * parent  = link_parent( rotor, blk );
     650           0 :   fd_rotor_blk_t * created = NULL;
     651           0 :   if( FD_LIKELY( !parent ) ) parent = created = fd_rotor_blk_notarized( rotor, parent_slot, parent_blk_mr ); /* links the child blks naming it */
     652           0 :   if( FD_LIKELY( parent ) ) {
     653           0 :     if( FD_UNLIKELY( fd_rotor_slot_meta( rotor, slot )->final==fd_rotor_blk_pool_idx( rotor->blk_pool, blk ) ) ) promote( rotor, parent, &fd_rotor_slot_meta( rotor, parent_slot )->final ); /* finalized ancestry */
     654           0 :     consume( rotor, blk );
     655           0 :     if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt ) ) cascade( rotor, blk );
     656           0 :     if( FD_LIKELY( !created ) ) connect_ancestors( rotor, parent ); /* a held parent may itself be orphaned */
     657           0 :   }
     658           0 :   return created;
     659           0 : }
     660             : 
     661             : void
     662             : fd_rotor_slot_catchup( fd_rotor_t * rotor,
     663           0 :                        ulong        slot ) {
     664           0 :   for( ulong s=fd_ulong_max( rotor->root+1UL, rotor->catchup_slot ); s<slot; s++ ) {
     665           0 :     fd_rotor_slot_meta_t * meta = fd_rotor_slot_meta( rotor, s );
     666           0 :     if( FD_UNLIKELY( meta->skipped || meta->invalidated || meta->final!=fd_rotor_blk_pool_idx_null( rotor->blk_pool ) || meta->eager!=fd_rotor_blk_pool_idx_null( rotor->blk_pool ) ) ) continue;
     667           0 :     meta->eager = fd_rotor_blk_pool_idx( rotor->blk_pool, blk_insert( rotor, s, &hash_null ) );
     668           0 :   }
     669           0 :   rotor->catchup_slot = fd_ulong_max( rotor->catchup_slot, slot );
     670           0 : }
     671             : 
     672             : fd_rotor_fec_t *
     673             : fd_rotor_fec_complete( fd_rotor_t *       rotor,
     674             :                        fd_shred_t const * last_shred,
     675             :                        fd_mr32_t const *  fec_mr,
     676             :                        int                is_leader,
     677             :                        uint               turbine_cnt,
     678             :                        uint               repair_cnt,
     679             :                        uint               recovered_cnt,
     680             :                        uint               data_rcvd,
     681             :                        uint               parity_rcvd,
     682             :                        uint               repair_rcvd,
     683           0 :                        long               ts ) {
     684           0 :   fd_rotor_shred_insert( rotor, last_shred, fec_mr, 0, ts ); /* the last shred's header, its source was given when it arrived */
     685             : 
     686           0 :   fd_mr20_t key;
     687           0 :   memcpy( key.uc, fec_mr->uc, sizeof(fd_mr20_t) );
     688           0 :   fd_rotor_fec_t * fec = fd_rotor_fec_map_ele_query( rotor->fec_map, &key, NULL, rotor->fec_pool );
     689           0 :   if( FD_UNLIKELY( !fec ) ) return NULL; /* fd_rotor_shred_insert dropped it as conflicting with the eager blk */
     690             : 
     691           0 :   int first_complete = !fec->complete;
     692           0 :   fec->cmpl_ts       = fd_long_if( first_complete, ts, fec->cmpl_ts );
     693           0 :   fec->final_src     = fd_int_if( first_complete && is_leader, FD_EVENT_BLOCK_RECEIVED_FEC_SETS_FINAL_SHRED_SOURCE_LEADER, fec->final_src );
     694           0 :   fec->data_rcvd     = fd_uint_if( first_complete, data_rcvd,   fec->data_rcvd   );
     695           0 :   fec->parity_rcvd   = fd_uint_if( first_complete, parity_rcvd, fec->parity_rcvd );
     696           0 :   fec->repair_rcvd   = fd_uint_if( first_complete, repair_rcvd, fec->repair_rcvd );
     697           0 :   fec->mr32          = *fec_mr;
     698           0 :   fec->rcvd          = UINT_MAX;
     699           0 :   fec->complete      = 1;
     700           0 :   fec->data_complete = !!( last_shred->data.flags & FD_SHRED_DATA_FLAG_DATA_COMPLETE );
     701           0 :   fec->is_leader    |= !!is_leader; /* a completion again from the network does not clear our own */
     702             : 
     703           0 :   ulong                  slot  = last_shred->slot; /* fd_shred_t is packed, the map query needs an aligned key */
     704           0 :   fd_rotor_slot_meta_t * meta  = fd_rotor_slot_meta( rotor, slot );
     705           0 :   fd_rotor_blk_t *       pool  = rotor->blk_pool;
     706           0 :   fd_rotor_blk_t *       eager = fd_rotor_blk_pool_ele( pool, meta->eager );
     707           0 :   int                    cmpl  = eager && !meta->invalidated && eager->cmpl_fec_cnt && !memcmp( &eager->dmr, &hash_null, sizeof(fd_mr32_t) );
     708           0 :   for( uint k=0U; cmpl && k<eager->cmpl_fec_cnt; k++ ) {
     709           0 :     cmpl = eager->fecs[ k ]!=fd_rotor_fec_pool_idx_null( rotor->fec_pool ) && fd_rotor_fec_pool_ele( rotor->fec_pool, eager->fecs[ k ] )->complete;
     710           0 :   }
     711           0 :   meta->leader |= (uint)!!is_leader;
     712           0 :   if( FD_UNLIKELY( is_leader && eager ) ) eager->eager = 0; /* our own, never repaired */
     713           0 :   if( FD_UNLIKELY( is_leader && eager && meta->final!=meta->eager && meta->notar!=meta->eager ) ) blk_treap_remove( rotor, eager );
     714           0 :   if( FD_UNLIKELY( cmpl ) ) {
     715           0 :     compute_dmr( rotor, eager );
     716           0 :     link_children( rotor, eager ); /* the blks naming its dmr waited for it */
     717           0 :     dedup( rotor, eager, ts );
     718           0 :     if( FD_UNLIKELY( fd_rotor_fec_map_ele_query( rotor->fec_map, &key, NULL, rotor->fec_pool )!=fec ) ) return NULL; /* dedup pruned eager and with it the FEC set */
     719           0 :   }
     720             : 
     721           0 :   for( fd_rotor_blk_t * blk = fd_rotor_blk_map_ele_query( rotor->blk_map, &slot, NULL, pool );
     722           0 :                         blk;
     723           0 :                         blk = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( blk, NULL, pool ) ) {
     724           0 :     if( FD_LIKELY( blk->fecs[ fec->fec_idx ]!=fd_rotor_fec_pool_idx( rotor->fec_pool, fec ) ) ) continue;
     725           0 :     uint end = fd_uint_if( !!blk->cmpl_fec_cnt, blk->cmpl_fec_cnt, (uint)rotor->fec_blk_max );
     726           0 :     while(    blk->buff_fec_cnt<end
     727           0 :            && blk->fecs[ blk->buff_fec_cnt ]!=fd_rotor_fec_pool_idx_null( rotor->fec_pool )
     728           0 :            && fd_rotor_fec_pool_ele( rotor->fec_pool, blk->fecs[ blk->buff_fec_cnt ] )->complete ) {
     729           0 :       blk->buff_fec_cnt++;
     730           0 :     }
     731           0 :     if( FD_LIKELY( first_complete ) ) {
     732           0 :       blk->telemetry.turbine_shred_cnt   += turbine_cnt;
     733           0 :       blk->telemetry.repair_shred_cnt    += repair_cnt;
     734           0 :       blk->telemetry.recovered_shred_cnt += recovered_cnt;
     735           0 :       blk->telemetry.last_cmpl_fec_idx    = fec->fec_idx*FD_FEC_SHRED_CNT;
     736           0 :     }
     737           0 :     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 );
     738           0 :     if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->buff_fec_cnt==blk->cmpl_fec_cnt && !blk->telemetry.reported && rotor->telemetry.report ) ) { blk->telemetry.reported = 1; rotor->telemetry.report( rotor->telemetry.ctx, blk ); }
     739           0 :     consume( rotor, blk );
     740           0 :     if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt ) ) cascade( rotor, blk );
     741           0 :   }
     742           0 :   return fec;
     743           0 : }
     744             : 
     745             : void
     746             : fd_rotor_fec_evicted( fd_rotor_t *      rotor,
     747             :                       ulong             slot,
     748             :                       uint              fec_set_idx,
     749           0 :                       fd_mr20_t const * fec_mr ) {
     750           0 :   uint             k   = fec_set_idx/FD_FEC_SHRED_CNT;
     751           0 :   fd_rotor_fec_t * fec = fd_rotor_fec_map_ele_query( rotor->fec_map, fec_mr, NULL, rotor->fec_pool );
     752           0 :   if( FD_UNLIKELY( !fec || fec->complete || fec->slot!=slot || fec->fec_idx!=k ) ) return;
     753             : 
     754           0 :   fec->rcvd = 0U; /* its req, still waiting since the set is incomplete, asks for every shred again */
     755           0 : }
     756             : 
     757             : fd_rotor_fec_t *
     758             : fd_rotor_fec_notarized( fd_rotor_t *      rotor,
     759             :                         ulong             slot,
     760             :                         fd_mr32_t const * blk_mr,
     761             :                         uint              fec_set_idx,
     762             :                         fd_mr20_t const * fec_mr,
     763           0 :                         long              ts ) {
     764             : 
     765           0 :   uint fec_idx = fec_set_idx / FD_FEC_SHRED_CNT;
     766             : 
     767           0 :   fd_rotor_blk_t * blk = fd_rotor_blk_map_ele_query( rotor->blk_map, &slot, NULL, rotor->blk_pool );
     768           0 :   while( blk && memcmp( &blk->dmr, blk_mr, sizeof(fd_mr32_t) ) ) blk = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( blk, NULL, rotor->blk_pool );
     769           0 :   if( FD_UNLIKELY( !blk ) ) return NULL; /* blk was pruned while the response was in flight */
     770           0 :   blk->telemetry.fec_root_res_cnt++;
     771           0 :   blk->telemetry.first_meta_res_ts = fd_long_if( !blk->telemetry.first_meta_res_ts, ts, blk->telemetry.first_meta_res_ts );
     772             : 
     773           0 :   if( FD_UNLIKELY( blk->fecs[ fec_idx ]!=fd_rotor_fec_pool_idx_null( rotor->fec_pool ) ) ) return NULL; /* duplicate response, eg. a retried request */
     774             : 
     775           0 :   fd_rotor_slot_meta_t * meta  = fd_rotor_slot_meta( rotor, slot );
     776           0 :   fd_rotor_blk_t *       eager = fd_rotor_blk_pool_ele( rotor->blk_pool, meta->eager );
     777           0 :   int                    eqvoc = eager && eager->fecs[ fec_idx ]!=fd_rotor_fec_pool_idx_null( rotor->fec_pool ) && memcmp( &fd_rotor_fec_pool_ele( rotor->fec_pool, eager->fecs[ fec_idx ] )->key, fec_mr, sizeof(fd_mr20_t) );
     778           0 :   if( FD_UNLIKELY( eqvoc ) ) fd_rotor_slot_invalidated( rotor, slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_MERKLE_ROOT_MISMATCH, ts );
     779             : 
     780           0 :   fd_rotor_fec_t * fec = fd_rotor_fec_map_ele_query( rotor->fec_map, fec_mr, NULL, rotor->fec_pool );
     781           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 */
     782           0 :   if( FD_UNLIKELY( !fec ) ) {
     783           0 :     FD_TEST( fd_rotor_fec_pool_free( rotor->fec_pool ) );
     784           0 :     fec                = fd_rotor_fec_pool_ele_acquire( rotor->fec_pool );
     785           0 :     fec->key           = *fec_mr;
     786           0 :     fec->mr32          = hash_null;
     787           0 :     fec->fec_idx       = fec_idx;
     788           0 :     fec->slot          = slot;
     789           0 :     fec->rcvd          = 0U;
     790           0 :     fec->complete      = 0;
     791           0 :     fec->data_complete = 0;
     792           0 :     fec->final_src     = 0;
     793           0 :     fec->first_ts      = 0L;
     794           0 :     fec->cmpl_ts       = 0L;
     795           0 :     fec->is_leader     = 0;
     796           0 :     fec->data_rcvd     = 0U;
     797           0 :     fec->parity_rcvd   = 0U;
     798           0 :     fec->repair_rcvd   = 0U;
     799           0 :     fd_rotor_fec_map_ele_insert( rotor->fec_map, fec, rotor->fec_pool );
     800           0 :   }
     801           0 :   fec->notarized       = 1;
     802           0 :   blk->fecs[ fec_idx ] = fd_rotor_fec_pool_idx( rotor->fec_pool, fec );
     803           0 :   if( FD_LIKELY( !fec->complete ) ) return fec;
     804             : 
     805           0 :   uint end = fd_uint_if( !!blk->cmpl_fec_cnt, blk->cmpl_fec_cnt, (uint)rotor->fec_blk_max );
     806           0 :   while(    blk->buff_fec_cnt<end
     807           0 :          && blk->fecs[ blk->buff_fec_cnt ]!=fd_rotor_fec_pool_idx_null( rotor->fec_pool )
     808           0 :          && fd_rotor_fec_pool_ele( rotor->fec_pool, blk->fecs[ blk->buff_fec_cnt ] )->complete ) {
     809           0 :     blk->buff_fec_cnt++;
     810           0 :   }
     811           0 :   if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->buff_fec_cnt==blk->cmpl_fec_cnt && !blk->telemetry.reported && rotor->telemetry.report ) ) { blk->telemetry.reported = 1; rotor->telemetry.report( rotor->telemetry.ctx, blk ); }
     812           0 :   consume( rotor, blk );
     813           0 :   if( FD_UNLIKELY( blk->cmpl_fec_cnt && blk->cons_fec_cnt==blk->cmpl_fec_cnt ) ) cascade( rotor, blk );
     814           0 :   return fec;
     815           0 : }
     816             : 
     817             : void
     818             : fd_rotor_root_advanced( fd_rotor_t *      rotor,
     819             :                         ulong             slot,
     820           0 :                         fd_mr32_t const * blk_mr ) {
     821           0 :   ulong            root     = rotor->root;
     822           0 :   fd_rotor_blk_t * root_blk = NULL; /* the new root, if held. rotor->root_blk is the old root until freed below: it left orphan_map on becoming root and must not be removed again */
     823           0 :   for( ulong s=root; s<=slot; s++ ) {
     824           0 :     fd_rotor_blk_t * next;
     825           0 :     for( fd_rotor_blk_t * blk = fd_rotor_blk_map_ele_query( rotor->blk_map, &s, NULL, rotor->blk_pool );
     826           0 :                           blk;
     827           0 :                           blk = next ) {
     828           0 :       next = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( blk, NULL, rotor->blk_pool );
     829           0 :       if( FD_UNLIKELY( !blk->telemetry.reported && rotor->telemetry.report ) ) { blk->telemetry.reported = 1; rotor->telemetry.report( rotor->telemetry.ctx, blk ); }
     830           0 :       for( ulong k=0UL; k<rotor->fec_blk_max; k++ ) {
     831           0 :         ulong idx = blk->fecs[ k ];
     832           0 :         blk->fecs[ k ] = fd_rotor_fec_pool_idx_null( rotor->fec_pool );
     833           0 :         if( FD_LIKELY( idx==fd_rotor_fec_pool_idx_null( rotor->fec_pool ) ) ) continue;
     834           0 :         fd_rotor_fec_t * fec = fd_rotor_fec_pool_ele( rotor->fec_pool, idx );
     835           0 :         if( FD_LIKELY( fd_rotor_fec_map_ele_query( rotor->fec_map, &fec->key, NULL, rotor->fec_pool )!=fec ) ) continue; /* freed through a sibling */
     836           0 :         fd_rotor_fec_map_ele_remove_fast( rotor->fec_map, fec, rotor->fec_pool );
     837           0 :         fd_rotor_fec_pool_ele_release   ( rotor->fec_pool, fec );
     838           0 :       }
     839           0 :       if( FD_UNLIKELY( s==slot && !memcmp( &blk->dmr, blk_mr, sizeof(fd_mr32_t) ) ) ) { /* the new root */
     840           0 :         blk_treap_remove( rotor, blk ); /* the root is never repaired */
     841           0 :         if( FD_LIKELY( blk!=rotor->root_blk && blk->parent==fd_rotor_blk_pool_idx_null( rotor->blk_pool ) && memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) {
     842           0 :           fd_rotor_orphan_map_ele_remove_fast( rotor->orphan_map, blk, rotor->blk_pool ); /* its parent was just freed, the root waits for nothing */
     843           0 :         }
     844           0 :         fd_rotor_slot_meta( rotor, slot )->final = fd_rotor_blk_pool_idx( rotor->blk_pool, blk );
     845           0 :         blk->eager = 0;
     846           0 :         root_blk   = blk;
     847           0 :         continue;
     848           0 :       }
     849             : 
     850           0 :       unlink( rotor, blk );
     851           0 :       if( FD_UNLIKELY( blk!=rotor->root_blk && blk->parent==fd_rotor_blk_pool_idx_null( rotor->blk_pool ) && memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) fd_rotor_orphan_map_ele_remove_fast( rotor->orphan_map, blk, rotor->blk_pool );
     852           0 :       blk_treap_remove( rotor, blk );
     853           0 :       fd_rotor_slot_meta_t * meta = fd_rotor_slot_meta( rotor, s );
     854           0 :       if( FD_UNLIKELY( meta->eager==fd_rotor_blk_pool_idx( rotor->blk_pool, blk ) ) ) meta->eager = fd_rotor_blk_pool_idx_null( rotor->blk_pool ); /* the root slot's meta stays live, its roles must not name freed blks */
     855           0 :       if( FD_UNLIKELY( meta->notar==fd_rotor_blk_pool_idx( rotor->blk_pool, blk ) ) ) meta->notar = fd_rotor_blk_pool_idx_null( rotor->blk_pool );
     856           0 :       fd_rotor_blk_map_ele_remove_fast( rotor->blk_map, blk, rotor->blk_pool );
     857           0 :       fd_rotor_blk_pool_ele_release   ( rotor->blk_pool, blk );
     858           0 :     }
     859           0 :   }
     860           0 :   rotor->root_blk = root_blk;
     861           0 :   rotor->root     = slot;
     862           0 : }
     863             : 
     864             : void
     865             : fd_rotor_shred_insert( fd_rotor_t *       rotor,
     866             :                        fd_shred_t const * shred,
     867             :                        fd_mr32_t const *  fec_mr,
     868             :                        int                src,
     869           0 :                        long               ts ) {
     870             : 
     871           0 :   fd_mr20_t key;
     872           0 :   memcpy( key.uc, fec_mr->uc, sizeof(fd_mr20_t) );
     873             : 
     874             :   /* First, handle a Notar shred (shred for a FEC set that has already
     875             :      reached a notarized status ie. SafeToNotar or stronger).  It also
     876             :      belongs to the eager blk, so fall through. */
     877             : 
     878           0 :   fd_rotor_fec_t * fec     = fd_rotor_fec_map_ele_query( rotor->fec_map, &key, NULL, rotor->fec_pool );
     879           0 :   uint             fec_idx = shred->idx / FD_FEC_SHRED_CNT;
     880           0 :   if( FD_UNLIKELY( fec && ( fec->slot!=shred->slot || fec->fec_idx!=fec_idx ) ) ) return; /* a 20-byte root collision */
     881           0 :   if( FD_UNLIKELY( fec && fec->notarized ) ) fec->rcvd = fd_uint_set_bit( fec->rcvd, (int)( shred->idx - fec_idx * FD_FEC_SHRED_CNT ) );
     882           0 :   if( FD_UNLIKELY( fec && !fec->first_ts ) ) fec->first_ts = ts;
     883           0 :   if( FD_LIKELY( fec && src && !fec->complete ) ) fec->final_src = src; /* frozen once complete */
     884             : 
     885             :   /* Second, handle an Eager shred (from Turbine or Eager Repair). */
     886             : 
     887           0 :   ulong                  parent_slot = shred->slot - shred->data.parent_off;
     888           0 :   fd_rotor_blk_t *       pool        = rotor->blk_pool;
     889           0 :   ulong                  null        = fd_rotor_blk_pool_idx_null( pool );
     890           0 :   fd_rotor_slot_meta_t * meta        = fd_rotor_slot_meta( rotor, shred->slot );
     891           0 :   fd_rotor_blk_t const * fin         = fd_rotor_blk_pool_ele_const( pool, meta->final );
     892           0 :   int                    fin_done    = fin && fin->cmpl_fec_cnt && fin->buff_fec_cnt==fin->cmpl_fec_cnt;
     893           0 :   if( FD_UNLIKELY( meta->skipped || meta->invalidated || ( fin_done && meta->eager==null ) ) ) return; /* a final cert ahead of turbine still lets turbine build the blk, dedup merges it */
     894           0 :   if( FD_UNLIKELY( meta->eager==null ) ) meta->eager = fd_rotor_blk_pool_idx( pool, blk_insert( rotor, shred->slot, &hash_null ) );
     895           0 :   fd_rotor_blk_t * blk = fd_rotor_blk_pool_ele( pool, meta->eager );
     896           0 :   if( FD_UNLIKELY( blk->parent_slot==ULONG_MAX ) ) blk->parent_slot = parent_slot; /* created empty by a child or catchup */
     897             : 
     898             :   /* Check for misbehavior that can invalidate the eager block. */
     899             : 
     900           0 :   fd_block_marker_t marker[1];
     901           0 :   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 );
     902           0 :   fd_mr32_t const * parent_blk_mr = shred->idx==0U ? &marker->header.parent_block_id : NULL;
     903             : 
     904           0 :   int slot_complete     = !!( shred->data.flags & FD_SHRED_DATA_FLAG_SLOT_COMPLETE );
     905           0 :   int eqvoc             = blk->fecs[ fec_idx ]!=fd_rotor_fec_pool_idx_null( rotor->fec_pool ) && blk->fecs[ fec_idx ]!=fd_rotor_fec_pool_idx( rotor->fec_pool, fec ); /* the leader signed two FEC sets at this index, eg. two shred 0s */
     906           0 :   int parent_conflict   = bad_header || blk->parent_slot!=parent_slot;
     907           0 :   int complete_conflict = ( blk->cmpl_fec_cnt && fec_idx>=blk->cmpl_fec_cnt ) || ( slot_complete && fec_idx+1U<blk->rcvd_fec_cnt );
     908           0 :   if( FD_UNLIKELY( eqvoc ) )             { fd_rotor_slot_invalidated( rotor, shred->slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_MERKLE_ROOT_MISMATCH, ts ); return; }
     909           0 :   if( FD_UNLIKELY( parent_conflict ) )   { fd_rotor_slot_invalidated( rotor, shred->slot, bad_header ? FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_INVALID_BLOCK_HEADER : FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_PARENT_OFF_MISMATCH, ts ); return; }
     910           0 :   if( FD_UNLIKELY( complete_conflict ) ) { fd_rotor_slot_invalidated( rotor, shred->slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_SLOT_COMPLETE_MISMATCH, ts ); return; }
     911             : 
     912           0 :   if( FD_UNLIKELY( parent_blk_mr ) ) { /* shred 0 again names the same parent, the key must not change while in orphan_map */
     913           0 :     if( FD_UNLIKELY( blk!=rotor->root_blk && blk->parent==null && memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) fd_rotor_orphan_map_ele_remove_fast( rotor->orphan_map, blk, pool );
     914           0 :     blk->parent_blk_mr = *parent_blk_mr;
     915           0 :     if( FD_LIKELY  ( blk!=rotor->root_blk && blk->parent==null && memcmp( &blk->parent_blk_mr, &hash_null, sizeof(fd_mr32_t) ) ) ) fd_rotor_orphan_map_ele_insert     ( rotor->orphan_map, blk, pool );
     916           0 :   }
     917           0 :   if( FD_UNLIKELY( parent_blk_mr && blk->parent==null && !link_parent( rotor, blk ) ) ) { /* waits unlinked for the named parent to be held, eg. to complete on turbine */
     918           0 :     fd_rotor_slot_meta_t * pmeta = fd_rotor_slot_meta( rotor, parent_slot );
     919           0 :     if( FD_UNLIKELY( pmeta->eager==null && pmeta->final==null && !pmeta->skipped && !pmeta->invalidated ) ) blk_insert( rotor, parent_slot, &hash_null ); /* seed the parent slot's eager blk, turbine may have lost it */
     920           0 :   }
     921           0 :   if( FD_UNLIKELY( slot_complete ) ) blk->cmpl_fec_cnt = fec_idx+1U;
     922           0 :   if( FD_UNLIKELY( slot_complete || fec_idx+1U>blk->rcvd_fec_cnt ) ) { /* its repair limit moved */
     923           0 :     blk->rcvd_fec_ts = fd_long_if( fec_idx+1U>blk->rcvd_fec_cnt, ts, blk->rcvd_fec_ts );
     924           0 :     blk_treap_insert( rotor, blk );
     925           0 :   }
     926             : 
     927           0 :   if( FD_UNLIKELY( !fec ) ) {
     928           0 :     FD_TEST( fd_rotor_fec_pool_free( rotor->fec_pool ) );
     929           0 :     fec                = fd_rotor_fec_pool_ele_acquire( rotor->fec_pool );
     930           0 :     fec->key           = key;
     931           0 :     fec->mr32          = hash_null;
     932           0 :     fec->fec_idx       = fec_idx;
     933           0 :     fec->slot          = shred->slot;
     934           0 :     fec->rcvd          = 0U;
     935           0 :     fec->complete      = 0;
     936           0 :     fec->data_complete = 0;
     937           0 :     fec->final_src     = src;
     938           0 :     fec->first_ts      = ts;
     939           0 :     fec->cmpl_ts       = 0L;
     940           0 :     fec->is_leader     = 0;
     941           0 :     fec->notarized     = 0;
     942           0 :     fec->data_rcvd     = 0U;
     943           0 :     fec->parity_rcvd   = 0U;
     944           0 :     fec->repair_rcvd   = 0U;
     945           0 :     fd_rotor_fec_map_ele_insert( rotor->fec_map, fec, rotor->fec_pool );
     946           0 :   }
     947           0 :   blk->fecs[ fec_idx ] = fd_rotor_fec_pool_idx( rotor->fec_pool, fec );
     948           0 :   blk->rcvd_fec_cnt    = fd_uint_max( blk->rcvd_fec_cnt, fec_idx+1U );
     949           0 :   fec->rcvd            = fd_uint_set_bit( fec->rcvd, (int)( shred->idx - fec_idx*FD_FEC_SHRED_CNT ) );
     950           0 : }
     951             : 
     952             : void
     953             : fd_rotor_slot_invalidated( fd_rotor_t * rotor,
     954             :                            ulong        slot,
     955             :                            int          reason,
     956           0 :                            long         ts ) {
     957           0 :   fd_rotor_slot_meta_t * meta  = fd_rotor_slot_meta( rotor, slot );
     958           0 :   fd_rotor_blk_t *       eager = fd_rotor_blk_pool_ele( rotor->blk_pool, meta->eager );
     959           0 :   int                    first = !meta->invalidated;
     960           0 :   meta->invalidated = 1;
     961           0 :   if( FD_LIKELY( eager ) ) {
     962           0 :     eager->telemetry.cancelled_ts     = fd_long_if( !eager->telemetry.cancelled_reason, ts, eager->telemetry.cancelled_ts );
     963           0 :     eager->telemetry.cancelled_reason = (uchar)reason;
     964           0 :     eager->eager = 0;
     965           0 :   }
     966           0 :   if( FD_LIKELY( eager && meta->final!=meta->eager && meta->notar!=meta->eager ) ) blk_treap_remove( rotor, eager ); /* still the eager blk, never repaired */
     967           0 :   if( FD_LIKELY( first && eager && eager->parent!=fd_rotor_blk_pool_idx_null( rotor->blk_pool ) ) ) cascade( rotor, fd_rotor_blk_pool_ele( rotor->blk_pool, eager->parent ) ); /* delivers the notar blks held behind it */
     968           0 : }
     969             : 
     970             : fd_rotor_slot_meta_t *
     971             : fd_rotor_slot_meta( fd_rotor_t * rotor,
     972           0 :                     ulong        slot ) {
     973           0 :   fd_rotor_slot_meta_t * meta = &rotor->slot_meta[ slot % rotor->slot_max ];
     974           0 :   if( FD_UNLIKELY( meta->slot!=slot ) ) {
     975           0 :     meta->slot        = slot;
     976           0 :     meta->eager       = fd_rotor_blk_pool_idx_null( rotor->blk_pool );
     977           0 :     meta->notar       = fd_rotor_blk_pool_idx_null( rotor->blk_pool );
     978           0 :     meta->final       = fd_rotor_blk_pool_idx_null( rotor->blk_pool );
     979           0 :     meta->invalidated = 0;
     980           0 :     meta->skipped     = 0;
     981           0 :     meta->leader      = 0;
     982           0 :   }
     983           0 :   return meta;
     984           0 : }
     985             : 
     986             : void
     987             : fd_rotor_slot_skipped( fd_rotor_t * rotor,
     988           0 :                        ulong        slot ) {
     989           0 :   fd_rotor_slot_meta_t * meta = fd_rotor_slot_meta( rotor, slot );
     990           0 :   meta->skipped = 1; /* late repair requests or shreds for the slot must not recreate blks in it */
     991             : 
     992           0 :   fd_rotor_blk_t * next;
     993           0 :   for( fd_rotor_blk_t * blk = fd_rotor_blk_map_ele_query( rotor->blk_map, &slot, NULL, rotor->blk_pool );
     994           0 :                         blk;
     995           0 :                         blk = next ) {
     996           0 :     next = (fd_rotor_blk_t *)fd_rotor_blk_map_ele_next_const( blk, NULL, rotor->blk_pool );
     997           0 :     prune( rotor, blk );
     998           0 :   }
     999           0 : }

Generated by: LCOV version 1.14