LCOV - code coverage report
Current view: top level - discof/reasm - fd_reasm.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 265 351 75.5 %
Date: 2025-12-06 04:45:29 Functions: 14 26 53.8 %

          Line data    Source code
       1             : #include "fd_reasm.h"
       2             : #include "fd_reasm_private.h"
       3             : 
       4             : #define LOGGING 0
       5             : 
       6             : FD_FN_CONST ulong
       7          78 : fd_reasm_align( void ) {
       8          78 :   return alignof(fd_reasm_t);
       9          78 : }
      10             : 
      11             : FD_FN_CONST ulong
      12          18 : fd_reasm_footprint( ulong fec_max ) {
      13          18 :   int lgf_max = fd_ulong_find_msb( fd_ulong_pow2_up( fec_max ) );
      14          18 :   return FD_LAYOUT_FINI(
      15          18 :     FD_LAYOUT_APPEND(
      16          18 :     FD_LAYOUT_APPEND(
      17          18 :     FD_LAYOUT_APPEND(
      18          18 :     FD_LAYOUT_APPEND(
      19          18 :     FD_LAYOUT_APPEND(
      20          18 :     FD_LAYOUT_APPEND(
      21          18 :     FD_LAYOUT_APPEND(
      22          18 :     FD_LAYOUT_APPEND(
      23          18 :     FD_LAYOUT_APPEND(
      24          18 :     FD_LAYOUT_INIT,
      25          18 :       alignof(fd_reasm_t), sizeof(fd_reasm_t)            ),
      26          18 :       pool_align(),        pool_footprint    ( fec_max ) ),
      27          18 :       ancestry_align(),    ancestry_footprint( fec_max ) ),
      28          18 :       frontier_align(),    frontier_footprint( fec_max ) ),
      29          18 :       orphaned_align(),    orphaned_footprint( fec_max ) ),
      30          18 :       subtrees_align(),    subtrees_footprint( fec_max ) ),
      31          18 :       bfs_align(),         bfs_footprint     ( fec_max ) ),
      32          18 :       out_align(),         out_footprint     ( fec_max ) ),
      33          18 :       slot_mr_align(),     slot_mr_footprint ( lgf_max ) ),
      34          18 :     fd_reasm_align() );
      35          18 : }
      36             : 
      37             : void *
      38             : fd_reasm_new( void * shmem,
      39             :               ulong  fec_max,
      40           9 :               ulong  seed ) {
      41             : 
      42           9 :   if( FD_UNLIKELY( !shmem ) ) {
      43           0 :     FD_LOG_WARNING(( "NULL mem" ));
      44           0 :     return NULL;
      45           0 :   }
      46             : 
      47           9 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_reasm_align() ) ) ) {
      48           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      49           0 :     return NULL;
      50           0 :   }
      51             : 
      52           9 :   ulong footprint = fd_reasm_footprint( fec_max );
      53           9 :   if( FD_UNLIKELY( !footprint ) ) {
      54           0 :     FD_LOG_WARNING(( "bad fec_max (%lu)", fec_max ));
      55           0 :     return NULL;
      56           0 :   }
      57             : 
      58           9 :   fd_wksp_t * wksp = fd_wksp_containing( shmem );
      59           9 :   if( FD_UNLIKELY( !wksp ) ) {
      60           0 :     FD_LOG_WARNING(( "shmem must be part of a workspace" ));
      61           0 :     return NULL;
      62           0 :   }
      63             : 
      64           9 :   fd_memset( shmem, 0, footprint );
      65             : 
      66           9 :   int lgf_max = fd_ulong_find_msb( fd_ulong_pow2_up( fec_max ) );
      67             : 
      68           9 :   fd_reasm_t * reasm;
      69           9 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
      70           9 :   reasm           = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_reasm_t), sizeof(fd_reasm_t)            );
      71           9 :   void * pool     = FD_SCRATCH_ALLOC_APPEND( l, pool_align(),        pool_footprint    ( fec_max ) );
      72           9 :   void * ancestry = FD_SCRATCH_ALLOC_APPEND( l, ancestry_align(),    ancestry_footprint( fec_max ) );
      73           9 :   void * frontier = FD_SCRATCH_ALLOC_APPEND( l, frontier_align(),    frontier_footprint( fec_max ) );
      74           9 :   void * orphaned = FD_SCRATCH_ALLOC_APPEND( l, orphaned_align(),    orphaned_footprint( fec_max ) );
      75           9 :   void * subtrees = FD_SCRATCH_ALLOC_APPEND( l, subtrees_align(),    subtrees_footprint( fec_max ) );
      76           9 :   void * bfs      = FD_SCRATCH_ALLOC_APPEND( l, bfs_align(),         bfs_footprint     ( fec_max ) );
      77           9 :   void * out      = FD_SCRATCH_ALLOC_APPEND( l, out_align(),         out_footprint     ( fec_max ) );
      78           9 :   void * slot_mr  = FD_SCRATCH_ALLOC_APPEND( l, slot_mr_align(),     slot_mr_footprint ( lgf_max ) );
      79           9 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_reasm_align() ) == (ulong)shmem + footprint );
      80             : 
      81           9 :   reasm->slot0    = ULONG_MAX;
      82           9 :   reasm->root     = pool_idx_null( pool                    );
      83           9 :   reasm->pool     = pool_new     ( pool,     fec_max       );
      84           9 :   reasm->ancestry = ancestry_new ( ancestry, fec_max, seed );
      85           9 :   reasm->frontier = frontier_new ( frontier, fec_max, seed );
      86           9 :   reasm->orphaned = orphaned_new ( orphaned, fec_max, seed );
      87           9 :   reasm->subtrees = subtrees_new ( subtrees, fec_max, seed );
      88           9 :   /*             */ subtreel_new ( reasm->_subtrlf         );
      89           9 :   reasm->bfs      = bfs_new      ( bfs,      fec_max       );
      90           9 :   reasm->out      = out_new      ( out,      fec_max       );
      91           9 :   reasm->slot_mr  = slot_mr_new  ( slot_mr,  lgf_max, seed );
      92             : 
      93           9 :   return shmem;
      94           9 : }
      95             : 
      96             : fd_reasm_t *
      97           9 : fd_reasm_join( void * shreasm ) {
      98           9 :   fd_reasm_t * reasm = (fd_reasm_t *)shreasm;
      99             : 
     100           9 :   if( FD_UNLIKELY( !reasm ) ) {
     101           0 :     FD_LOG_WARNING(( "NULL reasm" ));
     102           0 :     return NULL;
     103           0 :   }
     104             : 
     105           9 :   reasm->pool     = pool_join    ( reasm->pool     );
     106           9 :   reasm->ancestry = ancestry_join( reasm->ancestry );
     107           9 :   reasm->frontier = frontier_join( reasm->frontier );
     108           9 :   reasm->orphaned = orphaned_join( reasm->orphaned );
     109           9 :   reasm->subtrees = subtrees_join( reasm->subtrees );
     110           9 :   reasm->subtreel = subtreel_join( reasm->_subtrlf );
     111           9 :   reasm->bfs      = bfs_join     ( reasm->bfs      );
     112           9 :   reasm->out      = out_join     ( reasm->out      );
     113           9 :   reasm->slot_mr  = slot_mr_join ( reasm->slot_mr  );
     114             : 
     115           9 :   return reasm;
     116           9 : }
     117             : 
     118             : void *
     119           6 : fd_reasm_leave( fd_reasm_t * reasm ) {
     120             : 
     121           6 :   if( FD_UNLIKELY( !reasm ) ) {
     122           0 :     FD_LOG_WARNING(( "NULL reasm" ));
     123           0 :     return NULL;
     124           0 :   }
     125             : 
     126           6 :   return (void *)reasm;
     127           6 : }
     128             : 
     129             : void *
     130           6 : fd_reasm_delete( void * shreasm ) {
     131           6 :   fd_reasm_t * reasm = (fd_reasm_t *)shreasm;
     132             : 
     133           6 :   if( FD_UNLIKELY( !reasm ) ) {
     134           0 :     FD_LOG_WARNING(( "NULL reasm" ));
     135           0 :     return NULL;
     136           0 :   }
     137             : 
     138           6 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)reasm, fd_reasm_align() ) ) ) {
     139           0 :     FD_LOG_WARNING(( "misaligned reasm" ));
     140           0 :     return NULL;
     141           0 :   }
     142             : 
     143           6 :   return reasm;
     144           6 : }
     145             : 
     146           6 : fd_reasm_fec_t       * fd_reasm_root         ( fd_reasm_t       * reasm                                 ) { return pool_ele      ( reasm->pool, reasm->root      ); }
     147           0 : fd_reasm_fec_t const * fd_reasm_root_const   ( fd_reasm_t const * reasm                                 ) { return pool_ele_const( reasm->pool, reasm->root      ); }
     148           0 : fd_reasm_fec_t       * fd_reasm_parent       ( fd_reasm_t       * reasm, fd_reasm_fec_t       * child   ) { return pool_ele      ( reasm->pool, child->parent    ); }
     149           0 : fd_reasm_fec_t const * fd_reasm_parent_const ( fd_reasm_t const * reasm, fd_reasm_fec_t const * child   ) { return pool_ele_const( reasm->pool, child->parent    ); }
     150           0 : fd_reasm_fec_t       * fd_reasm_child        ( fd_reasm_t       * reasm, fd_reasm_fec_t       * parent  ) { return pool_ele      ( reasm->pool, parent->child    ); }
     151           0 : fd_reasm_fec_t const * fd_reasm_child_const  ( fd_reasm_t const * reasm, fd_reasm_fec_t const * parent  ) { return pool_ele_const( reasm->pool, parent->child    ); }
     152           0 : fd_reasm_fec_t       * fd_reasm_sibling      ( fd_reasm_t       * reasm, fd_reasm_fec_t       * sibling ) { return pool_ele      ( reasm->pool, sibling->sibling ); }
     153           0 : fd_reasm_fec_t const * fd_reasm_sibling_const( fd_reasm_t const * reasm, fd_reasm_fec_t const * sibling ) { return pool_ele_const( reasm->pool, sibling->sibling ); }
     154             : 
     155             : ulong
     156           0 : fd_reasm_slot0( fd_reasm_t * reasm ) {
     157           0 :   return reasm->slot0;
     158           0 : }
     159             : 
     160             : ulong
     161           0 : fd_reasm_free( fd_reasm_t * reasm ) {
     162           0 :   return pool_free( reasm->pool );
     163           0 : }
     164             : 
     165             : fd_reasm_fec_t *
     166           0 : fd_reasm_peek( fd_reasm_t * reasm ) {
     167           0 :   if( FD_UNLIKELY( out_empty( reasm->out ) ) ) return NULL;
     168           0 :   return pool_ele( reasm->pool, *out_peek_head( reasm->out ) );
     169           0 : }
     170             : 
     171             : fd_reasm_fec_t *
     172          24 : fd_reasm_out( fd_reasm_t * reasm ) {
     173          24 :   if( FD_UNLIKELY( out_empty( reasm->out ) ) ) return NULL;
     174          21 :   return pool_ele( reasm->pool, out_pop_head( reasm->out ) );
     175          24 : }
     176             : 
     177             : fd_reasm_fec_t *
     178             : fd_reasm_query( fd_reasm_t const * reasm,
     179         180 :                 fd_hash_t  const * merkle_root ) {
     180         180 :   fd_reasm_fec_t * fec = NULL;
     181         180 :   fec =                  ancestry_ele_query( reasm->ancestry, merkle_root, NULL, reasm->pool );
     182         180 :   fec = fd_ptr_if( !fec, frontier_ele_query( reasm->frontier, merkle_root, NULL, reasm->pool ), fec );
     183         180 :   fec = fd_ptr_if( !fec, orphaned_ele_query( reasm->orphaned, merkle_root, NULL, reasm->pool ), fec );
     184         180 :   fec = fd_ptr_if( !fec, subtrees_ele_query( reasm->subtrees, merkle_root, NULL, reasm->pool ), fec );
     185         180 :   return fec;
     186         180 : }
     187             : 
     188             : static void
     189         123 : overwrite_invalid_cmr( fd_reasm_t * reasm, fd_reasm_fec_t * child ) {
     190         123 :   if( FD_UNLIKELY( child->fec_set_idx==0 && !fd_reasm_query( reasm, &child->cmr ) ) ) {
     191          54 :     slot_mr_t * slot_mr_parent = slot_mr_query( reasm->slot_mr, child->slot - child->parent_off, NULL );
     192          54 :     if( FD_LIKELY( slot_mr_parent ) ) {
     193           6 :       fd_reasm_fec_t * parent = fd_reasm_query( reasm, &slot_mr_parent->block_id );
     194           6 :       if( FD_LIKELY( parent ) ) {
     195           6 :         FD_LOG_INFO(( "overwriting invalid cmr for FEC slot: %lu fec_set_idx: %u from %s (CMR) to %s (parent's block id)", child->slot, child->fec_set_idx, FD_BASE58_ENC_32_ALLOCA( &child->cmr ), FD_BASE58_ENC_32_ALLOCA( &parent->key ) ));
     196           6 :         child->cmr = parent->key; /* use the parent's merkle root */
     197           6 :       }
     198           6 :     }
     199          54 :   }
     200         123 : }
     201             : 
     202             : static void
     203             : link( fd_reasm_t     * reasm,
     204             :       fd_reasm_fec_t * parent,
     205          54 :       fd_reasm_fec_t * child ) {
     206          54 :   child->parent = pool_idx( reasm->pool, parent );
     207          54 :   if( FD_LIKELY( parent->child == pool_idx_null( reasm->pool ) ) ) {
     208          45 :     parent->child = pool_idx( reasm->pool, child ); /* set as left-child. */
     209          45 :   } else {
     210           9 :     fd_reasm_fec_t * curr = pool_ele( reasm->pool, parent->child );
     211           9 :     while( curr->sibling != pool_idx_null( reasm->pool ) ) curr = pool_ele( reasm->pool, curr->sibling );
     212           9 :     curr->sibling = pool_idx( reasm->pool, child ); /* set as right-sibling. */
     213           9 :     if( FD_UNLIKELY( !parent->slot_complete ) ) child->eqvoc = 1; /* only the last FEC set in a slot
     214             :                                                                      can have multiple children and
     215             :                                                                      be non-equivocating */
     216           9 :   }
     217          54 : }
     218             : 
     219             : fd_reasm_fec_t *
     220             : fd_reasm_insert( fd_reasm_t *      reasm,
     221             :                  fd_hash_t const * merkle_root,
     222             :                  fd_hash_t const * chained_merkle_root,
     223             :                  ulong             slot,
     224             :                  uint              fec_set_idx,
     225             :                  ushort            parent_off,
     226             :                  ushort            data_cnt,
     227             :                  int               data_complete,
     228             :                  int               slot_complete,
     229          63 :                  int               leader ) {
     230             : 
     231             : # if LOGGING
     232             :   FD_LOG_NOTICE(( "inserting (%lu %u) %s %s. %u %d %d", slot, fec_set_idx, FD_BASE58_ENC_32_ALLOCA( merkle_root ), FD_BASE58_ENC_32_ALLOCA( chained_merkle_root ), data_cnt, data_complete, slot_complete ));
     233             : # endif
     234             : 
     235          63 : # if FD_REASM_USE_HANDHOLDING
     236          63 :   FD_TEST( pool_free( reasm->pool ) );
     237          63 :   FD_TEST( !fd_reasm_query( reasm, merkle_root ) );
     238          63 : # endif
     239             : 
     240          63 :   fd_reasm_fec_t * pool = reasm->pool;
     241          63 :   ulong            null = pool_idx_null( pool );
     242             : 
     243          63 :   ancestry_t * ancestry = reasm->ancestry;
     244          63 :   frontier_t * frontier = reasm->frontier;
     245          63 :   orphaned_t * orphaned = reasm->orphaned;
     246          63 :   subtrees_t * subtrees = reasm->subtrees;
     247          63 :   subtreel_t * subtreel = reasm->subtreel;
     248             : 
     249          63 :   ulong * bfs = reasm->bfs;
     250          63 :   ulong * out = reasm->out;
     251             : 
     252          63 :   fd_reasm_fec_t * fec = pool_ele_acquire( pool );
     253          63 :   fec->key             = *merkle_root;
     254          63 :   fec->next            = null;
     255          63 :   fec->parent          = null;
     256          63 :   fec->child           = null;
     257          63 :   fec->sibling         = null;
     258          63 :   fec->slot            = slot;
     259          63 :   fec->parent_off      = parent_off;
     260          63 :   fec->fec_set_idx     = fec_set_idx;
     261          63 :   fec->data_cnt        = data_cnt;
     262          63 :   fec->free            = 0;
     263          63 :   fec->data_complete   = data_complete;
     264          63 :   fec->slot_complete   = slot_complete;
     265          63 :   fec->leader          = leader;
     266          63 :   fec->eqvoc           = 0;
     267          63 :   fec->bank_idx        = null;
     268          63 :   fec->parent_bank_idx = null;
     269             : 
     270          63 :   if( FD_UNLIKELY( !chained_merkle_root ) ) { /* initialize the reasm with the root */
     271           9 :     FD_TEST( reasm->root==pool_idx_null( reasm->pool )         );
     272           9 :     slot_mr_t * slot_mr = slot_mr_insert( reasm->slot_mr, slot );
     273           9 :     slot_mr->block_id   = fec->key;
     274           9 :     reasm->root         = pool_idx( pool, fec );
     275           9 :     reasm->slot0        = slot;
     276           9 :     frontier_ele_insert( reasm->frontier, fec, pool );
     277           9 :     return fec;
     278           9 :   }
     279             : 
     280          54 :   fec->cmr = *chained_merkle_root;
     281             : 
     282             :   /* This is a gross case reasm needs to handle because Agave currently
     283             :      does not validate chained merkle roots across slots ie. if a leader
     284             :      sends a bad chained merkle root on a slot boundary, the cluster
     285             :      might converge on the leader's block anyways.  So we overwrite the
     286             :      chained merkle root based on the slot and parent_off metadata.
     287             :      There are two cases: 1. we receive the parent before the child.  In
     288             :      this case we just overwrite the child's CMR.  2. we receive the
     289             :      child before the parent.  In this case every time we receive a new
     290             :      FEC set we need to check the orphan roots for whether we can link
     291             :      the orphan to the new FEC via slot metadata, since the chained
     292             :      merkle root metadata on that orphan root might be wrong. */
     293             : 
     294          54 :   if( FD_UNLIKELY( slot_complete ) ) {
     295          39 :     slot_mr_t * slot_mr = slot_mr_query( reasm->slot_mr, slot, NULL );
     296          39 :     if( FD_UNLIKELY( slot_mr ) ) {
     297           3 :       FD_LOG_WARNING(( "equivocating block_id for FEC slot: %lu fec_set_idx: %u prev: %s curr: %s", fec->slot, fec->fec_set_idx, FD_BASE58_ENC_32_ALLOCA( &slot_mr->block_id ), FD_BASE58_ENC_32_ALLOCA( &fec->key ) )); /* it's possible there's equivocation... */
     298          36 :     } else {
     299          36 :       slot_mr           = slot_mr_insert( reasm->slot_mr, slot );
     300          36 :       slot_mr->block_id = fec->key;
     301          36 :     }
     302          39 :   }
     303          54 :   overwrite_invalid_cmr( reasm, fec ); /* handle receiving parent before child */
     304             : 
     305             :   /* First, we search for the parent of this new FEC and link if found.
     306             :      The new FEC set may result in a new leaf or a new orphan tree root
     307             :      so we need to check that. */
     308             : 
     309          54 :   fd_reasm_fec_t * parent = NULL;
     310          54 :   ulong            idx    = pool_idx( pool, fec );
     311          54 :   if(        FD_LIKELY ( parent = ancestry_ele_query ( ancestry, &fec->cmr, NULL, pool ) ) ) { /* parent is connected non-leaf */
     312           6 :     frontier_ele_insert( frontier, fec,    pool );
     313           6 :     out_push_tail      ( out,      idx          );
     314          48 :   } else if( FD_LIKELY ( parent = frontier_ele_remove( frontier, &fec->cmr, NULL, pool ) ) ) { /* parent is connected leaf     */
     315          21 :     ancestry_ele_insert( ancestry, parent, pool );
     316          21 :     frontier_ele_insert( frontier, fec,    pool );
     317          21 :     out_push_tail( out, pool_idx( pool, fec ) );
     318          27 :   } else if( FD_LIKELY( parent = orphaned_ele_query ( orphaned, &fec->cmr, NULL, pool ) ) ) { /* parent is orphaned non-root */
     319           0 :     orphaned_ele_insert( orphaned, fec, pool );
     320          27 :   } else if( FD_LIKELY( parent = subtrees_ele_query ( subtrees, &fec->cmr, NULL, pool ) ) ) { /* parent is orphaned root     */
     321           0 :     orphaned_ele_insert( orphaned, fec, pool );
     322          27 :   } else {                                                                                    /* parent not found            */
     323          27 :     subtrees_ele_insert   ( subtrees, fec, pool );
     324          27 :     subtreel_ele_push_tail( subtreel, fec, pool );
     325          27 :   }
     326             : 
     327          54 :   if( FD_LIKELY( parent ) ) link( reasm, parent, fec );
     328             : 
     329             :   /* Second, we search for children of this new FEC and link them to it.
     330             :      By definition any children must be orphaned (a child cannot be part
     331             :      of a connected tree before its parent).  Therefore, we only search
     332             :      through the orphaned subtrees.  As part of this operation, we also
     333             :      coalesce connected orphans into the same tree.  This way we only
     334             :      need to search the orphan tree roots (vs. all orphaned nodes). */
     335             : 
     336          54 :   FD_TEST( bfs_empty( bfs ) );
     337          54 :   for( subtreel_iter_t iter = subtreel_iter_fwd_init(       subtreel, pool );
     338         123 :                              !subtreel_iter_done    ( iter, subtreel, pool );
     339          69 :                        iter = subtreel_iter_fwd_next( iter, subtreel, pool ) ) {
     340          69 :     bfs_push_tail( bfs, subtreel_iter_idx( iter, subtreel, pool ) );
     341          69 :   }
     342         123 :   while( FD_LIKELY( !bfs_empty( bfs ) ) ) { /* link orphan subtrees to the new FEC */
     343          69 :     fd_reasm_fec_t * orphan_root = pool_ele( reasm->pool, bfs_pop_head( bfs ) );
     344          69 :     overwrite_invalid_cmr( reasm, orphan_root ); /* handle receiving child before parent */
     345          69 :     if( FD_LIKELY( orphan_root && 0==memcmp( orphan_root->cmr.uc, fec->key.uc, sizeof(fd_hash_t) ) ) ) { /* this orphan_root is a direct child of fec */
     346          27 :       link( reasm, fec, orphan_root );
     347          27 :       subtrees_ele_remove( subtrees, &orphan_root->key, NULL, pool );
     348          27 :       subtreel_ele_remove( subtreel, orphan_root,             pool );
     349          27 :       orphaned_ele_insert( orphaned, orphan_root,             pool );
     350          27 :     }
     351          69 :   }
     352             : 
     353             :   /* Third, we advance the frontier outward beginning from fec as we may
     354             :      have connected orphaned descendants to fec in the above step.  This
     355             :      does a BFS outward from fec until it reaches leaves, moving fec and
     356             :      its non-leaf descendants into ancestry and leaves into frontier.
     357             : 
     358             :      parent (ancestry)     orphan root  (subtrees)
     359             :        |                        |
     360             :       fec   (frontier)     orphan child (orphaned)
     361             : 
     362             :      parent
     363             :        |
     364             :       fec         <- frontier is here
     365             :        |
     366             :      orphan root
     367             :        |
     368             :      orphan child <- advance to here */
     369             : 
     370          54 :   if( FD_LIKELY( frontier_ele_query( frontier, &fec->key, NULL, pool ) ) ) bfs_push_tail( bfs, pool_idx( pool, fec ) );
     371         108 :   while( FD_LIKELY( !bfs_empty( bfs ) ) ) {
     372          54 :     fd_reasm_fec_t * parent  = pool_ele( pool, bfs_pop_head( bfs ) );
     373          54 :     fd_reasm_fec_t * child = pool_ele( pool, parent->child );
     374          54 :     if( FD_LIKELY( child ) ) {
     375          24 :       frontier_ele_remove( frontier, &parent->key, NULL, pool );
     376          24 :       ancestry_ele_insert( ancestry, parent,             pool );
     377          24 :     }
     378          81 :     while( FD_LIKELY( child ) ) {
     379          27 :       FD_TEST( orphaned_ele_remove( orphaned, &child->key, NULL, pool ) );
     380          27 :       frontier_ele_insert( frontier, child,              pool );
     381          27 :       bfs_push_tail( bfs, pool_idx( pool, child ) );
     382          27 :       out_push_tail( out, pool_idx( pool, child ) );
     383          27 :       child = pool_ele( pool, child->sibling );
     384          27 :     }
     385          54 :   }
     386          54 :   return fec;
     387          54 : }
     388             : 
     389             : static fd_reasm_fec_t *
     390             : publish_remove( fd_reasm_t *      reasm,
     391          15 :                 fd_hash_t const * merkle_root ) {
     392          15 :   fd_reasm_fec_t *          fec = ancestry_ele_remove( reasm->ancestry, merkle_root, NULL, reasm->pool );
     393          15 :   if( FD_UNLIKELY( !fec ) ) fec = frontier_ele_remove( reasm->frontier, merkle_root, NULL, reasm->pool );
     394          15 :   return fec;
     395          15 : }
     396             : 
     397             : fd_reasm_fec_t *
     398           3 : fd_reasm_publish( fd_reasm_t * reasm, fd_hash_t const * merkle_root ) {
     399           3 : # if FD_REASM_USE_HANDHOLDING
     400           3 :   if( FD_UNLIKELY( !pool_ele( reasm->pool, reasm->root ) ) ) { FD_LOG_WARNING(( "missing root"                                                     )); return NULL; }
     401           3 :   if( FD_UNLIKELY( !fd_reasm_query( reasm, merkle_root ) ) ) { FD_LOG_WARNING(( "merkle root %s not found", FD_BASE58_ENC_32_ALLOCA( merkle_root ) )); return NULL; }
     402           3 : # endif
     403             : 
     404           3 :   fd_reasm_fec_t *  pool = reasm->pool;
     405           3 :   ulong             null = pool_idx_null( pool );
     406           3 :   fd_reasm_fec_t  * oldr = pool_ele( pool, reasm->root );
     407           3 :   fd_reasm_fec_t  * newr = fd_reasm_query( reasm, merkle_root );
     408             : 
     409             :   /* First, remove the previous root, and push it as the first element
     410             :      of the BFS queue. */
     411             : 
     412           3 :   fd_reasm_fec_t * head = publish_remove( reasm, &oldr->key ); /* initialize BFS queue */
     413           3 :   head->next            = null;                                /* clear map next */
     414           3 :   fd_reasm_fec_t * tail = head;                                /* tail of BFS queue */
     415             : 
     416             :   /* Second, BFS down the tree, pruning all of root's ancestors and also
     417             :      any descendants of those ancestors. */
     418             : 
     419          18 :   while( FD_LIKELY( head ) ) {
     420          15 :     fd_reasm_fec_t * child = pool_ele( pool, head->child );                  /* left-child */
     421          30 :     while( FD_LIKELY( child ) ) {                                            /* iterate over children */
     422          15 :       if( FD_LIKELY( child != newr ) ) {                                     /* stop at new root */
     423          12 :         tail->next = pool_idx( pool, publish_remove( reasm, &child->key ) ); /* remove node from map to reuse `.next` */
     424          12 :         tail       = pool_ele( pool, tail->next );                           /* push onto BFS queue (so descendants can be pruned) */
     425          12 :         tail->next = null;                                                   /* clear map next */
     426          12 :       }
     427          15 :       child = pool_ele( pool, child->sibling );                              /* right-sibling */
     428          15 :     }
     429          15 :     slot_mr_t * slot_mr = slot_mr_query( reasm->slot_mr, head->slot, NULL );
     430          15 :     if( FD_UNLIKELY( slot_mr ) ) slot_mr_remove( reasm->slot_mr, slot_mr  ); /* only first FEC */
     431             : 
     432          15 :     fd_reasm_fec_t * next = pool_ele( pool, head->next ); /* pophead */
     433          15 :     pool_ele_release( pool, head );                       /* release */
     434          15 :     head->free = 1;
     435          15 :     head = next;                                          /* advance */
     436          15 :   }
     437             : 
     438             :   /* Clear out any stale, pruned entries from the out queue. */
     439           3 :   ulong deq_cnt = out_cnt( reasm->out );
     440          21 :   for( ulong i=0UL; i<deq_cnt; i++ ) {
     441          18 :     ulong idx = out_pop_head( reasm->out );
     442          18 :     if( FD_LIKELY( pool_ele( pool, idx )->free==0 ) ) out_push_tail( reasm->out, idx );
     443          18 :   }
     444             : 
     445           3 :   newr->parent = null;                   /* unlink old root */
     446           3 :   reasm->root  = pool_idx( pool, newr ); /* replace with new root */
     447           3 :   return newr;
     448           3 : }
     449             : 
     450             : #include <stdio.h>
     451             : 
     452             : FD_FN_UNUSED static void
     453           0 : print( fd_reasm_t const * reasm, fd_reasm_fec_t const * fec, int space, const char * prefix ) {
     454           0 :   fd_reasm_fec_t * pool = reasm->pool;
     455           0 : 
     456           0 :   if( fec == NULL ) return;
     457           0 : 
     458           0 :   if( space > 0 ) printf( "\n" );
     459           0 :   for( int i = 0; i < space; i++ ) printf( " " );
     460           0 :   printf( "%s%s", prefix, FD_BASE58_ENC_32_ALLOCA( &fec->key ) );
     461           0 : 
     462           0 :   fd_reasm_fec_t const * curr = pool_ele_const( pool, fec->child );
     463           0 :   char new_prefix[1024]; /* FIXME size this correctly */
     464           0 :   while( curr ) {
     465           0 :     if( pool_ele_const( pool, curr->sibling ) ) {
     466           0 :       sprintf( new_prefix, "├── " ); /* branch indicating more siblings follow */
     467           0 :       print( reasm, curr, space + 4, new_prefix );
     468           0 :     } else {
     469           0 :       sprintf( new_prefix, "└── " ); /* end branch */
     470           0 :       print( reasm, curr, space + 4, new_prefix );
     471           0 :     }
     472           0 :     curr = pool_ele_const( pool, curr->sibling );
     473           0 :   }
     474           0 : }
     475             : 
     476             : void
     477           0 : fd_reasm_print( fd_reasm_t const * reasm ) {
     478           0 :   FD_LOG_NOTICE( ( "\n\n[Reasm]" ) );
     479           0 :   fd_reasm_fec_t * pool = reasm->pool;
     480             : 
     481           0 :   printf(("\n\n[Frontier]\n" ) );
     482           0 :   frontier_t * frontier = reasm->frontier;
     483           0 :   for( frontier_iter_t iter = frontier_iter_init(       frontier, pool );
     484           0 :                              !frontier_iter_done( iter, frontier, pool );
     485           0 :                        iter = frontier_iter_next( iter, frontier, pool ) ) {
     486           0 :     fd_reasm_fec_t const * fec = pool_ele_const( reasm->pool, iter.ele_idx );
     487           0 :     printf( "(%lu, %u) %s\n", fec->slot, fec->fec_set_idx, FD_BASE58_ENC_32_ALLOCA( &fec->key ) );
     488           0 :   }
     489             : 
     490           0 :   printf(("\n\n[Subtrees]\n" ) );
     491           0 :   subtrees_t * subtrees = reasm->subtrees;
     492           0 :   for( subtrees_iter_t iter = subtrees_iter_init(       subtrees, pool );
     493           0 :                              !subtrees_iter_done( iter, subtrees, pool );
     494           0 :                        iter = subtrees_iter_next( iter, subtrees, pool ) ) {
     495           0 :     fd_reasm_fec_t const * fec = pool_ele_const( reasm->pool, iter.ele_idx );
     496           0 :     printf( "(%lu, %u) %s\n", fec->slot, fec->fec_set_idx, FD_BASE58_ENC_32_ALLOCA( &fec->key ) );
     497           0 :   }
     498             : 
     499             :   // print( reasm, pool_ele_const( reasm->pool, reasm->root ), 0, "" );
     500             :   // printf( "\n\n" );
     501             :   // for( out_iter_t iter = out_iter_init( reasm->out ); !out_iter_done( reasm->out, iter ); iter = out_iter_next( reasm->out, iter ) ) {
     502             :   //   ulong * idx = out_iter_ele( reasm->out, iter );
     503             :   //   printf( "%s\n", FD_BASE58_ENC_32_ALLOCA( pool_ele_const( reasm->pool, *idx ) ) );
     504             :   // }
     505           0 :   printf( "\n\n" );
     506           0 : }

Generated by: LCOV version 1.14