LCOV - code coverage report
Current view: top level - discof/reasm - fd_reasm.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 257 339 75.8 %
Date: 2025-10-13 04:42:14 Functions: 14 25 56.0 %

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

Generated by: LCOV version 1.14