LCOV - code coverage report
Current view: top level - choreo/ghost - fd_ghost.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 443 0.0 %
Date: 2024-11-13 11:58:15 Functions: 0 15 0.0 %

          Line data    Source code
       1             : #include "fd_ghost.h"
       2             : #include "stdio.h"
       3             : #include <string.h>
       4             : 
       5             : /* clang-format off */
       6             : 
       7             : void *
       8           0 : fd_ghost_new( void * shmem, ulong node_max, ulong vote_max, ulong seed ) {
       9             : 
      10           0 :   if( FD_UNLIKELY( !shmem ) ) {
      11           0 :     FD_LOG_WARNING(( "NULL mem" ));
      12           0 :     return NULL;
      13           0 :   }
      14             : 
      15           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)shmem, fd_ghost_align() ) ) ) {
      16           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      17           0 :     return NULL;
      18           0 :   }
      19             : 
      20           0 :   ulong footprint = fd_ghost_footprint( node_max, vote_max );
      21           0 :   if( FD_UNLIKELY( !footprint ) ) {
      22           0 :     FD_LOG_WARNING(( "bad node_max (%lu) or vote_max (%lu)", node_max, vote_max ));
      23           0 :     return NULL;
      24           0 :   }
      25             : 
      26           0 :   fd_memset( shmem, 0, footprint );
      27           0 :   ulong        laddr = (ulong)shmem;
      28           0 :   fd_ghost_t * ghost = (void *)laddr;
      29           0 :   laddr             += sizeof(fd_ghost_t);
      30             : 
      31           0 :   laddr            = fd_ulong_align_up( laddr, fd_ghost_node_pool_align() );
      32           0 :   ghost->node_pool = fd_ghost_node_pool_new( (void *)laddr, node_max );
      33           0 :   laddr           += fd_ghost_node_pool_footprint( node_max );
      34             : 
      35           0 :   laddr           = fd_ulong_align_up( laddr, fd_ghost_node_map_align() );
      36           0 :   ghost->node_map = fd_ghost_node_map_new( (void *)laddr, node_max, seed );
      37           0 :   laddr          += fd_ghost_node_map_footprint( node_max );
      38             : 
      39           0 :   laddr            = fd_ulong_align_up( laddr, fd_ghost_vote_pool_align() );
      40           0 :   ghost->vote_pool = fd_ghost_vote_pool_new( (void *)laddr, vote_max );
      41           0 :   laddr           += fd_ghost_vote_pool_footprint( vote_max );
      42             : 
      43           0 :   laddr           = fd_ulong_align_up( laddr, fd_ghost_vote_map_align() );
      44           0 :   ghost->vote_map = fd_ghost_vote_map_new( (void *)laddr, vote_max, seed );
      45           0 :   laddr          += fd_ghost_vote_map_footprint( vote_max );
      46             : 
      47           0 :   laddr = fd_ulong_align_up( laddr, fd_ghost_align() );
      48           0 :   FD_TEST( laddr == (ulong)shmem + footprint );
      49             : 
      50           0 :   return shmem;
      51           0 : }
      52             : 
      53             : fd_ghost_t *
      54           0 : fd_ghost_join( void * shghost ) {
      55             : 
      56           0 :   if( FD_UNLIKELY( !shghost ) ) {
      57           0 :     FD_LOG_WARNING(( "NULL ghost" ));
      58           0 :     return NULL;
      59           0 :   }
      60             : 
      61           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)shghost, fd_ghost_align() ) ) ) {
      62           0 :     FD_LOG_WARNING(( "misaligned ghost" ));
      63           0 :     return NULL;
      64           0 :   }
      65             : 
      66           0 :   ulong        laddr = (ulong)shghost; /* offset from a memory region */
      67           0 :   fd_ghost_t * ghost = (void *)shghost;
      68           0 :   laddr             += sizeof(fd_ghost_t);
      69             : 
      70           0 :   laddr            = fd_ulong_align_up( laddr, fd_ghost_node_pool_align() );
      71           0 :   ghost->node_pool = fd_ghost_node_pool_join( (void *)laddr );
      72           0 :   ulong node_max   = fd_ghost_node_pool_max( ghost->node_pool );
      73           0 :   laddr           += fd_ghost_node_pool_footprint( node_max );
      74             : 
      75           0 :   laddr           = fd_ulong_align_up( laddr, fd_ghost_node_map_align() );
      76           0 :   ghost->node_map = fd_ghost_node_map_join( (void *)laddr );
      77           0 :   laddr          += fd_ghost_node_map_footprint( node_max );
      78             : 
      79           0 :   laddr            = fd_ulong_align_up( laddr, fd_ghost_vote_pool_align() );
      80           0 :   ghost->vote_pool = fd_ghost_vote_pool_join( (void *)laddr );
      81           0 :   ulong vote_max   = fd_ghost_vote_pool_max( ghost->vote_pool );
      82           0 :   laddr           += fd_ghost_vote_pool_footprint( vote_max );
      83             : 
      84           0 :   laddr           = fd_ulong_align_up( laddr, fd_ghost_vote_map_align() );
      85           0 :   ghost->vote_map = fd_ghost_vote_map_join( (void *)laddr );
      86           0 :   laddr          += fd_ghost_vote_map_footprint( vote_max );
      87             : 
      88           0 :   laddr = fd_ulong_align_up( laddr, fd_ghost_align() );
      89           0 :   FD_TEST( laddr == (ulong)shghost + fd_ghost_footprint( node_max, vote_max ));
      90             : 
      91           0 :   return ghost;
      92           0 : }
      93             : 
      94             : void *
      95           0 : fd_ghost_leave( fd_ghost_t const * ghost ) {
      96             : 
      97           0 :   if( FD_UNLIKELY( !ghost ) ) {
      98           0 :     FD_LOG_WARNING(( "NULL ghost" ));
      99           0 :     return NULL;
     100           0 :   }
     101             : 
     102           0 :   return (void *)ghost;
     103           0 : }
     104             : 
     105             : void *
     106           0 : fd_ghost_delete( void * ghost ) {
     107             : 
     108           0 :   if( FD_UNLIKELY( !ghost ) ) {
     109           0 :     FD_LOG_WARNING(( "NULL ghost" ));
     110           0 :     return NULL;
     111           0 :   }
     112             : 
     113           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)ghost, fd_ghost_align() ) ) ) {
     114           0 :     FD_LOG_WARNING(( "misaligned ghost" ));
     115           0 :     return NULL;
     116           0 :   }
     117             : 
     118           0 :   return ghost;
     119           0 : }
     120             : 
     121             : void
     122           0 : fd_ghost_init( fd_ghost_t * ghost, ulong root, ulong total_stake ) {
     123             : 
     124           0 :   if( FD_UNLIKELY( !ghost ) ) {
     125           0 :     FD_LOG_WARNING(( "NULL ghost" ));
     126           0 :     return;
     127           0 :   }
     128             : 
     129           0 :   if( FD_UNLIKELY( root == FD_SLOT_NULL ) ) {
     130           0 :     FD_LOG_WARNING(( "NULL slot" ));
     131           0 :     return;
     132           0 :   }
     133             : 
     134           0 :   if( FD_UNLIKELY( ghost->root ) ) {
     135           0 :     FD_LOG_WARNING(( "ghost already initialized" ));
     136           0 :     return;
     137           0 :   }
     138             : 
     139           0 :   fd_ghost_node_t * node = fd_ghost_node_pool_ele_acquire( ghost->node_pool );
     140           0 :   memset( node, 0, sizeof( fd_ghost_node_t ));
     141           0 :   node->slot = root;
     142             : 
     143           0 :   fd_ghost_node_map_ele_insert( ghost->node_map, node, ghost->node_pool );
     144           0 :   ghost->root        = node;
     145           0 :   ghost->total_stake = total_stake;
     146             : 
     147           0 :   return;
     148           0 : }
     149             : 
     150             : /* clang-format on */
     151             : 
     152             : fd_ghost_node_t *
     153           0 : fd_ghost_insert( fd_ghost_t * ghost, ulong slot, ulong parent_slot ) {
     154           0 :   FD_LOG_DEBUG(( "[ghost] node_insert: %lu. parent: %lu.", slot, parent_slot ));
     155             : 
     156             : /* Caller promises slot >= SMR. */
     157             : 
     158           0 : #if FD_GHOST_USE_HANDHOLDING
     159           0 :   if (FD_UNLIKELY( slot < ghost->root->slot )) {
     160           0 :     FD_LOG_ERR(( "slot %lu is older than ghost root %lu", slot, ghost->root->slot ));
     161           0 :   }
     162           0 : #endif
     163             : 
     164             : /* Caller promises slot is not already in ghost. */
     165             : 
     166           0 : #if FD_GHOST_USE_HANDHOLDING
     167           0 :   if( FD_UNLIKELY( fd_ghost_node_map_ele_query( ghost->node_map,
     168           0 :                                                 &slot,
     169           0 :                                                 NULL,
     170           0 :                                                 ghost->node_pool ) ) ) {
     171           0 :     FD_LOG_ERR(( "slot %lu is already in ghost.", slot ));
     172           0 :   }
     173           0 : #endif
     174             : 
     175           0 :   fd_ghost_node_t * parent = fd_ghost_node_map_ele_query( ghost->node_map,
     176           0 :                                                           &parent_slot,
     177           0 :                                                           NULL,
     178           0 :                                                           ghost->node_pool );
     179             : 
     180             : /* Caller promises parent_slot is already in ghost. */
     181             : 
     182           0 : #if FD_GHOST_USE_HANDHOLDING
     183           0 :   if( FD_UNLIKELY( !fd_ghost_node_map_ele_query( ghost->node_map,
     184           0 :                                                  &parent_slot,
     185           0 :                                                  NULL,
     186           0 :                                                  ghost->node_pool ) ) ) {
     187           0 :     FD_LOG_ERR(( "[fd_ghost_node_insert] missing parent_slot %lu.", parent_slot ));
     188           0 :   }
     189           0 : #endif
     190             : 
     191             : /* Caller promises node pool has a free element. */
     192             : 
     193           0 : #if FD_GHOST_USE_HANDHOLDING
     194           0 :   if( FD_UNLIKELY( !fd_ghost_node_pool_free( ghost->node_pool ) ) ) {
     195           0 :     FD_LOG_ERR(( "[ghost] node_pool full. check pruning logic." ));
     196           0 :   }
     197           0 : #endif
     198             : 
     199           0 :   fd_ghost_node_t * node = fd_ghost_node_pool_ele_acquire( ghost->node_pool );
     200           0 :   memset( node, 0, sizeof( fd_ghost_node_t ) );
     201           0 :   node->slot = slot;
     202             : 
     203             :   /* Insert into the map for O(1) random access. */
     204             : 
     205           0 :   fd_ghost_node_map_ele_insert( ghost->node_map, node, ghost->node_pool );
     206             : 
     207             :   /* Insert into the ghost tree. */
     208             : 
     209           0 :   node->child   = NULL;
     210           0 :   node->sibling = NULL;
     211             : 
     212             : 
     213             :   /* Link node->parent. */
     214             : 
     215           0 :   node->parent = parent;
     216             : 
     217             :   /* Link parent->node and sibling->node. */
     218             : 
     219           0 :   if( FD_LIKELY( !parent->child ) ) {
     220             : 
     221             :     /* No siblings, which means no forks. This is the likely case. */
     222             : 
     223           0 :     parent->child = node;
     224             : 
     225           0 :   } else {
     226             : 
     227             :     /* Iterate to right-most sibling. */
     228             : 
     229           0 :     fd_ghost_node_t * curr = parent->child;
     230           0 :     while( curr->sibling ) {
     231           0 :       curr = curr->sibling;
     232           0 :     }
     233             : 
     234             :     /* Link to right-most sibling. */
     235             : 
     236           0 :     curr->sibling = node;
     237           0 :   }
     238             : 
     239             :   /* Return newly-created node. */
     240             : 
     241           0 :   return node;
     242           0 : }
     243             : 
     244             : fd_ghost_node_t const *
     245           0 : fd_ghost_head( fd_ghost_t const * ghost ) {
     246           0 :   fd_ghost_node_t const * head = ghost->root;
     247           0 :   while( head->child ) {
     248           0 :     head                         = head->child;
     249           0 :     fd_ghost_node_t const * curr = head;
     250           0 :     while( curr ) {
     251             : 
     252             :       /* clang-format off */
     253             : 
     254           0 :       head = fd_ptr_if(
     255           0 :         fd_int_if(
     256             :           /* if the weights are equal... */
     257             : 
     258           0 :           curr->weight == head->weight,
     259             : 
     260             :           /* ...tie-break by slot number */
     261             : 
     262           0 :           curr->slot < head->slot,
     263             : 
     264             :           /* otherwise return curr if curr > head */
     265             : 
     266           0 :           curr->weight > head->weight ),
     267           0 :         curr, head );
     268             : 
     269             :       /* clang-format on */
     270             : 
     271           0 :       curr = curr->sibling;
     272           0 :     }
     273           0 :   }
     274           0 :   return head;
     275           0 : }
     276             : 
     277             : fd_ghost_node_t const *
     278           0 : fd_ghost_replay_vote( fd_ghost_t * ghost, ulong slot, fd_pubkey_t const * pubkey, ulong stake ) {
     279           0 :   FD_LOG_DEBUG(( "[%s] slot %lu, pubkey %s, stake %lu", __func__, slot, FD_BASE58_ENC_32_ALLOCA( pubkey ), stake ));
     280             : 
     281           0 : #if FD_GHOST_USE_HANDHOLDING
     282           0 :   if( FD_UNLIKELY( slot < ghost->root->slot ) ) {
     283           0 :     FD_LOG_ERR(( "caller must only insert vote slots >= ghost root. vote: %lu, root: %lu",
     284           0 :                  slot,
     285           0 :                  ghost->root->slot ));
     286           0 :   }
     287           0 : #endif
     288             : 
     289           0 :   fd_ghost_node_t * node = fd_ghost_node_map_ele_query( ghost->node_map,
     290           0 :                                                         &slot,
     291           0 :                                                         NULL,
     292           0 :                                                         ghost->node_pool );
     293             : 
     294           0 : #if FD_GHOST_USE_HANDHOLDING
     295             :   /* Caller promises node is already in ghost. */
     296           0 :   if( FD_UNLIKELY( !node ) ) {
     297           0 :     FD_LOG_ERR(( "missing ghost node" ));
     298           0 :   }
     299             : 
     300           0 : #endif
     301             : 
     302             :   /* Query pubkey's latest vote. */
     303             : 
     304           0 :   fd_ghost_vote_t * latest_vote = fd_ghost_vote_map_ele_query( ghost->vote_map,
     305           0 :                                                                pubkey,
     306           0 :                                                                NULL,
     307           0 :                                                                ghost->vote_pool );
     308             : 
     309           0 :   if( FD_LIKELY( latest_vote ) ) {
     310             : 
     311             :     /* Return if this new vote slot is not > than latest vote. It is
     312             :        important that the vote slots are monotonically increasing,
     313             :        because the order we receive blocks is non-deterministic (due to
     314             :        network propagation variance), so we may process forks in a
     315             :        different order from the sender of this vote.
     316             : 
     317             :        For example, if a validator votes on A then switches to B, we
     318             :        might instead process B then A. In this case, the validator's
     319             :        vote account state on B would contain a strictly higher vote slot
     320             :        than A (due to lockout), so we would observe while processing A,
     321             :        that the vote slot < the latest vote slot we have saved for that
     322             :        validator. */
     323             : 
     324           0 :     if( FD_UNLIKELY( slot <= latest_vote->slot ) ) return node;
     325             : 
     326           0 :     fd_ghost_node_t * node = fd_ghost_node_map_ele_query( ghost->node_map,
     327           0 :                                                           &latest_vote->slot,
     328           0 :                                                           NULL,
     329           0 :                                                           ghost->node_pool );
     330             : 
     331           0 :     if( FD_UNLIKELY( !node ) ) {
     332             : 
     333           0 :       FD_LOG_NOTICE(( "[ghost] %s's latest vote slot %lu was too old and already pruned.",
     334           0 :                       FD_BASE58_ENC_32_ALLOCA( pubkey ),
     335           0 :                       latest_vote->slot ));
     336             : 
     337           0 :     } else {
     338             : 
     339             :       /* Subtract pubkey's stake from the prev voted slot hash and propagate. */
     340             : 
     341           0 :       FD_LOG_DEBUG(( "[ghost] removing (%s, %lu, %lu)",
     342           0 :                       FD_BASE58_ENC_32_ALLOCA( pubkey ),
     343           0 :                       latest_vote->stake,
     344           0 :                       latest_vote->slot ));
     345             : 
     346           0 :       int cf = __builtin_usubl_overflow( node->stake, latest_vote->stake, &node->stake );
     347           0 :       if( FD_UNLIKELY( cf ) ) {
     348           0 :         FD_LOG_WARNING(( "[%s] sub overflow. node->stake %lu latest_vote->stake %lu",
     349           0 :                          __func__,
     350           0 :                          node->stake,
     351           0 :                          latest_vote->stake ));
     352           0 :         node->stake = 0;
     353           0 :       }
     354           0 :       fd_ghost_node_t * ancestor = node;
     355           0 :       while( ancestor->parent ) {
     356           0 :         int cf = __builtin_usubl_overflow( ancestor->weight,
     357           0 :                                            latest_vote->stake,
     358           0 :                                            &ancestor->weight );
     359           0 :         if( FD_UNLIKELY( cf ) ) {
     360           0 :           FD_LOG_WARNING(( "[%s] sub overflow. ancestor->weight %lu latest_vote->stake %lu",
     361           0 :                            __func__,
     362           0 :                            ancestor->weight,
     363           0 :                            latest_vote->stake ));
     364           0 :           ancestor->weight = 0;
     365           0 :         }
     366           0 :         ancestor = ancestor->parent;
     367           0 :       }
     368           0 :     }
     369             : 
     370           0 :   } else {
     371             : 
     372             :     /* Ghost has not seen this pubkey vote yet, so insert. */
     373             : 
     374           0 : #if FD_GHOST_USE_HANDHOLDING
     375             :     /* OOM: we've exceeded the max number of voter pubkeys that were
     376             :        statically allocated. */
     377           0 :     if( FD_UNLIKELY( !fd_ghost_vote_pool_free( ghost->vote_pool ) ) ) {
     378           0 :       FD_LOG_ERR(( "[ghost] vote_pool full. check # of validators." ));
     379           0 :     }
     380           0 : #endif
     381             : 
     382           0 :     latest_vote         = fd_ghost_vote_pool_ele_acquire( ghost->vote_pool );
     383           0 :     latest_vote->pubkey = *pubkey;
     384           0 :     latest_vote->slot   = slot;
     385           0 :     latest_vote->stake  = stake;
     386           0 :     fd_ghost_vote_map_ele_insert( ghost->vote_map, latest_vote, ghost->vote_pool );
     387           0 :   }
     388           0 :   latest_vote->slot  = slot;
     389           0 :   latest_vote->stake = stake;
     390             : 
     391             :   /* Propagate the vote stake up the ancestry, including updating the
     392             :      head. */
     393             : 
     394           0 :   FD_LOG_DEBUG(( "[ghost] adding (%s, %lu, %lu)", FD_BASE58_ENC_32_ALLOCA( pubkey ), stake, latest_vote->slot ));
     395           0 :   int cf = __builtin_uaddl_overflow( node->stake, latest_vote->stake, &node->stake );
     396           0 :   if( FD_UNLIKELY( cf ) ) {
     397           0 :     FD_LOG_ERR(( "[%s] add overflow. node->stake %lu latest_vote->stake %lu",
     398           0 :                  __func__,
     399           0 :                  node->stake,
     400           0 :                  latest_vote->stake ));
     401           0 :   }
     402           0 :   fd_ghost_node_t * ancestor = node;
     403           0 :   while( ancestor->parent ) {
     404           0 :     int cf = __builtin_uaddl_overflow( ancestor->weight, latest_vote->stake, &ancestor->weight );
     405           0 :     if( FD_UNLIKELY( cf ) ) {
     406           0 :       FD_LOG_ERR(( "[%s] add overflow. ancestor->weight %lu latest_vote->stake %lu",
     407           0 :                    __func__,
     408           0 :                    ancestor->weight,
     409           0 :                    latest_vote->stake ));
     410           0 :     }
     411           0 :     ancestor = ancestor->parent;
     412           0 :   }
     413             : 
     414           0 : #if FD_GHOST_USE_HANDHOLDING
     415           0 :   if( FD_UNLIKELY( node->stake > ghost->total_stake ) ) {
     416           0 :     FD_LOG_ERR(( "[%s] invariant violation. node->stake > total stake."
     417           0 :                  "slot: %lu, "
     418           0 :                  "node->stake %lu, "
     419           0 :                  "ghost->total_stake %lu",
     420           0 :                  __func__,
     421           0 :                  slot,
     422           0 :                  node->stake,
     423           0 :                  ghost->total_stake ));
     424           0 :   }
     425           0 : #endif
     426             : 
     427           0 :   return node;
     428           0 : }
     429             : 
     430             : fd_ghost_node_t const *
     431             : fd_ghost_gossip_vote( FD_PARAM_UNUSED fd_ghost_t *        ghost,
     432             :                       FD_PARAM_UNUSED ulong               slot,
     433             :                       FD_PARAM_UNUSED fd_pubkey_t const * pubkey,
     434           0 :                       FD_PARAM_UNUSED ulong               stake ) {
     435           0 :   FD_LOG_ERR(( "unimplemented" ));
     436           0 : }
     437             : 
     438             : fd_ghost_node_t const *
     439           0 : fd_ghost_rooted_vote( fd_ghost_t * ghost, ulong root, fd_pubkey_t const * pubkey, ulong stake ) {
     440           0 :   FD_LOG_DEBUG(( "[%s] root %lu, pubkey %s, stake %lu", __func__, root, FD_BASE58_ENC_32_ALLOCA( pubkey ), stake ));
     441             : 
     442           0 : #if FD_GHOST_USE_HANDHOLDING
     443           0 :   if( FD_UNLIKELY( root < ghost->root->slot ) ) {
     444           0 :     FD_LOG_ERR(( "caller must only insert vote slots >= ghost root. vote: %lu, root: %lu",
     445           0 :                  root,
     446           0 :                  ghost->root->slot ));
     447           0 :   }
     448           0 : #endif
     449             : 
     450             :   /* It is invariant that the voter's root is found in ghost (as long as
     451             :      voter's root >= our root ). This is because voter's root is sourced
     452             :      from their vote state, so it must be on the fork we're replaying
     453             :      and we must have already inserted their root slot into ghost. */
     454             : 
     455           0 :   fd_ghost_node_t * node = fd_ghost_node_map_ele_query( ghost->node_map,
     456           0 :                                                         &root,
     457           0 :                                                         NULL,
     458           0 :                                                         ghost->node_pool );
     459             : 
     460           0 : #if FD_GHOST_USE_HANDHOLDING
     461           0 :   if( FD_UNLIKELY( !node ) ) {
     462           0 :     FD_LOG_ERR(( "[%s] invariant violation. missing voter %s's root %lu.", __func__, FD_BASE58_ENC_32_ALLOCA( pubkey ), root ));
     463           0 :   }
     464           0 : #endif
     465             : 
     466             :   /* Add to the rooted stake. */
     467             : 
     468           0 :   node->rooted_stake += stake;
     469           0 :   return node;
     470           0 : }
     471             : 
     472             : fd_ghost_node_t const *
     473           0 : fd_ghost_publish( fd_ghost_t * ghost, ulong slot ) {
     474             : 
     475           0 : #if FD_GHOST_USE_HANDHOLDING
     476           0 :   if( FD_UNLIKELY( slot < ghost->root->slot ) ) {
     477           0 :     FD_LOG_WARNING(( "[fd_ghost_publish] trying to publish slot %lu older than ghost->root %lu.",
     478           0 :                       slot,
     479           0 :                       ghost->root->slot ));
     480           0 :     return NULL;
     481           0 :   }
     482           0 :   if( FD_UNLIKELY( slot == ghost->root->slot ) ) {
     483           0 :     FD_LOG_WARNING(( "[fd_ghost_publish] publishing same slot %lu as ghost->root %lu.",
     484           0 :                       slot,
     485           0 :                       ghost->root->slot ));
     486           0 :     return NULL;
     487           0 :   }
     488           0 : #endif
     489             : 
     490           0 :   fd_ghost_node_t * root = fd_ghost_node_map_ele_query( ghost->node_map,
     491           0 :                                                         &slot,
     492           0 :                                                         NULL,
     493           0 :                                                         ghost->node_pool );
     494             : 
     495           0 : #if FD_GHOST_USE_HANDHOLDING
     496           0 :   if( FD_UNLIKELY( !root ) ) {
     497           0 :     FD_LOG_ERR(( "[fd_ghost_publish] publish slot %lu not found in ghost", slot ));
     498           0 :   }
     499             : 
     500           0 : #endif
     501             : 
     502             :   /* First, remove the previous root, and add it to the prune list.
     503             : 
     504             :      In this context, head is the list head (not to be confused with the
     505             :      ghost head.) */
     506             : 
     507           0 :   fd_ghost_node_t * head = fd_ghost_node_map_ele_remove( ghost->node_map,
     508           0 :                                                          &ghost->root->slot,
     509           0 :                                                          NULL,
     510           0 :                                                          ghost->node_pool );
     511           0 :   head->next             = fd_ghost_node_pool_idx_null( ghost->node_pool );
     512           0 :   fd_ghost_node_t * tail = head;
     513             : 
     514             :   /* Second, BFS down the tree, adding nodes to the prune queue except
     515             :      for the new root.
     516             : 
     517             :      Loop invariant: the old root must be in new root's ancestry. */
     518             : 
     519           0 :   while( head ) {
     520           0 :     fd_ghost_node_t * child = head->child;
     521             : 
     522           0 :     while( FD_LIKELY( child ) ) {
     523             : 
     524             :       /* Do not prune the new root. */
     525             : 
     526           0 :       if( FD_LIKELY( child != root ) ) {
     527             : 
     528             :         /* Remove the child from the map and push the child onto the list. */
     529             : 
     530           0 :         tail->next = fd_ghost_node_map_idx_remove( ghost->node_map,
     531           0 :                                                    &child->slot,
     532           0 :                                                    fd_ghost_node_pool_idx_null( ghost->node_pool ),
     533           0 :                                                    ghost->node_pool );
     534           0 : #if FD_GHOST_USE_HANDHOLDING
     535           0 :         if( FD_UNLIKELY( tail->next == fd_ghost_node_pool_idx_null( ghost->node_pool ) ) ) {
     536           0 :           FD_LOG_ERR(( "Failed to remove child. Child must exist given the while condition. "
     537           0 :                        "Possible memory corruption or data race." ));
     538           0 :         }
     539           0 : #endif
     540           0 :         tail       = fd_ghost_node_pool_ele( ghost->node_pool, tail->next );
     541           0 :         tail->next = fd_ghost_node_pool_idx_null( ghost->node_pool );
     542           0 :       }
     543             : 
     544           0 :       child = child->sibling;
     545           0 :     }
     546             : 
     547             :     /* Free the head, and move the head pointer forward. */
     548             : 
     549           0 :     fd_ghost_node_t * next = fd_ghost_node_pool_ele( ghost->node_pool, head->next );
     550           0 :     fd_ghost_node_pool_ele_release( ghost->node_pool, head );
     551           0 :     head = next;
     552           0 :   }
     553             : 
     554             :   /* Unlink the root and set the new root. */
     555             : 
     556           0 :   root->parent = NULL;
     557           0 :   ghost->root  = root;
     558             : 
     559           0 :   return root;
     560           0 : }
     561             : 
     562             : fd_ghost_node_t const *
     563           0 : fd_ghost_gca( fd_ghost_t const * ghost, ulong slot1, ulong slot2 ) {
     564           0 :   fd_ghost_node_t const * node1 = fd_ghost_query( ghost, slot1 );
     565           0 :   fd_ghost_node_t const * node2 = fd_ghost_query( ghost, slot2 );
     566             : 
     567           0 : #if FD_GHOST_USE_HANDHOLDING
     568           0 :   if( FD_UNLIKELY( !node1 ) ) {
     569           0 :     FD_LOG_WARNING(( "slot1 %lu is missing from ghost", slot1 ));
     570           0 :     return NULL;
     571           0 :   }
     572             : 
     573           0 :   if( FD_UNLIKELY( !node2 ) ) {
     574           0 :     FD_LOG_WARNING(( "slot2 %lu is missing from ghost", slot2 ));
     575           0 :     return NULL;
     576           0 :   }
     577           0 : #endif
     578             : 
     579             :   /* Find the greatest common ancestor. */
     580             : 
     581           0 :   while( node1 && node2 ) {
     582           0 :     if( FD_UNLIKELY( node1->slot == node2->slot ) ) return node1;
     583           0 :     if( node1->slot > node2->slot ) {
     584           0 :       node1 = node1->parent;
     585           0 :     } else {
     586           0 :       node2 = node2->parent;
     587           0 :     }
     588           0 :   }
     589             : 
     590           0 :   FD_LOG_ERR(( "Unable to find GCA. Is this a valid ghost?" ));
     591           0 : }
     592             : 
     593             : int
     594           0 : fd_ghost_is_descendant( fd_ghost_t const * ghost, ulong slot, ulong ancestor_slot ) {
     595           0 :   fd_ghost_node_t const * ancestor = fd_ghost_query( ghost, slot );
     596           0 : #if FD_GHOST_USE_HANDHOLDING
     597             : 
     598           0 :   if( FD_UNLIKELY( ancestor_slot < ghost->root->slot ) ) {
     599           0 :     FD_LOG_ERR(( "[%s] ancestor_slot %lu is older than ghost root %lu.",
     600           0 :                  __func__,
     601           0 :                  ancestor_slot,
     602           0 :                  ghost->root->slot ));
     603           0 :   }
     604             : 
     605           0 :   if( FD_UNLIKELY( !ancestor ) ) {
     606             : 
     607             :     /* Slot not found, so we won't find the ancestor. */
     608             : 
     609           0 :     FD_LOG_WARNING(( "[%s] unable to find slot %lu in ghost.", __func__, slot ));
     610           0 :     return 0;
     611           0 :   }
     612           0 : #endif
     613             : 
     614             :   /* Look for ancestor_slot in the fork ancestry.
     615             : 
     616             :      Stop looking when there is either no ancestry remaining or there is
     617             :      no reason to look further because we've searched past the
     618             :      ancestor_slot. */
     619             : 
     620           0 :   while( FD_LIKELY( ancestor && ancestor->slot >= ancestor_slot ) ) {
     621           0 :     if( FD_UNLIKELY( ancestor->slot == ancestor_slot ) ) return 1; /* optimize for not found */
     622           0 :     ancestor = ancestor->parent;
     623           0 :   }
     624           0 :   return 0; /* not found */
     625           0 : }
     626             : 
     627             : static void
     628           0 : print( fd_ghost_node_t const * node, int space, const char * prefix, ulong total ) {
     629           0 :   if( node == NULL ) return;
     630             : 
     631           0 :   if( space > 0 ) printf( "\n" );
     632           0 :   for( int i = 0; i < space; i++ )
     633           0 :     printf( " " );
     634           0 :   if( FD_UNLIKELY( node->weight > 100 ) ) {
     635           0 :   }
     636           0 :   double pct = ( (double)node->weight / (double)total ) * 100;
     637           0 :   if( FD_UNLIKELY( total == 0 ) ) {
     638           0 :     printf( "%s%lu (%lu)", prefix, node->slot, node->weight );
     639           0 :   } else if( FD_UNLIKELY( pct < 0.99 ) ) {
     640           0 :     printf( "%s%lu (%.0lf%%, %lu)", prefix, node->slot, pct, node->weight );
     641           0 :   } else {
     642           0 :     printf( "%s%lu (%.0lf%%)", prefix, node->slot, pct );
     643           0 :   }
     644             : 
     645           0 :   fd_ghost_node_t * curr = node->child;
     646           0 :   char              new_prefix[1024]; /* Large enough to hold the new prefix string */
     647           0 :   while( curr ) {
     648           0 :     if( curr->sibling ) {
     649           0 :       sprintf( new_prefix, "├── " ); /* Branch indicating more siblings follow */
     650           0 :       print( curr, space + 4, new_prefix, total );
     651           0 :     } else {
     652           0 :       sprintf( new_prefix, "└── " ); /* End branch */
     653           0 :       print( curr, space + 4, new_prefix, total );
     654           0 :     }
     655           0 :     curr = curr->sibling;
     656           0 :   }
     657           0 : }
     658             : 
     659             : void
     660           0 : fd_ghost_slot_print( fd_ghost_t * ghost, ulong slot, ulong depth ) {
     661           0 :   fd_ghost_node_t const * node = fd_ghost_query( ghost, slot );
     662           0 :   if( FD_UNLIKELY( !node ) ) {
     663           0 :     FD_LOG_WARNING(( "[fd_ghost_print_node] NULL node." ));
     664           0 :     return;
     665           0 :   }
     666           0 :   fd_ghost_node_t const * ancestor = node;
     667           0 :   for( ulong i = 0; i < depth; i++ ) {
     668           0 :     if( !ancestor->parent ) break;
     669           0 :     ancestor = ancestor->parent;
     670           0 :   }
     671           0 :   print( ancestor, 0, "", ghost->total_stake );
     672           0 :   printf( "\n\n" );
     673           0 : }

Generated by: LCOV version 1.14