LCOV - code coverage report
Current view: top level - disco/store - fd_store.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 190 261 72.8 %
Date: 2026-01-23 05:02:40 Functions: 11 11 100.0 %

          Line data    Source code
       1             : #include "fd_store.h"
       2             : #include "../../flamenco/fd_flamenco_base.h"
       3             : 
       4             : static const fd_hash_t hash_null = { 0 };
       5             : 
       6         303 : #define null fd_store_pool_idx_null()
       7             : 
       8          90 : #define BLOCKING 1
       9             : 
      10             : void *
      11           9 : fd_store_new( void * shmem, ulong fec_max, ulong part_cnt ) {
      12             : 
      13           9 :   if( FD_UNLIKELY( part_cnt==0UL ) ) {
      14           0 :     FD_LOG_WARNING(( "partition count must be greater than 0, should match the number of writers/shred tiles" ));
      15           0 :     return NULL;
      16           0 :   }
      17             : 
      18           9 :   if( FD_UNLIKELY( !shmem ) ) {
      19           0 :     FD_LOG_WARNING(( "NULL mem" ));
      20           0 :     return NULL;
      21           0 :   }
      22             : 
      23           9 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_store_align() ) ) ) {
      24           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      25           0 :     return NULL;
      26           0 :   }
      27             : 
      28           9 :   ulong footprint = fd_store_footprint( fec_max );
      29           9 :   if( FD_UNLIKELY( !footprint ) ) {
      30           0 :     FD_LOG_WARNING(( "bad fec_max (%lu)", fec_max ));
      31           0 :     return NULL;
      32           0 :   }
      33             : 
      34           9 :   fd_wksp_t * wksp = fd_wksp_containing( shmem );
      35           9 :   if( FD_UNLIKELY( !wksp ) ) {
      36           0 :     FD_LOG_WARNING(( "shmem must be part of a workspace" ));
      37           0 :     return NULL;
      38           0 :   }
      39             : 
      40             :   /* This seed value is very important. We have fec_max chains in the
      41             :      map, which means the size of each partition of buckets should be
      42             :      fec_max / part_cnt. When inserting into the map, we use the
      43             :      partition_slot_cnt as the seed, so that the modified hash function
      44             :      can use the seed/partition_slot_cnt to hash the key into the
      45             :      correct partition. */
      46             : 
      47           9 :   ulong part_slot_cnt = fec_max / part_cnt;
      48           9 :   ulong seed          = part_slot_cnt;
      49             : 
      50           9 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
      51           9 :   fd_store_t * store  = FD_SCRATCH_ALLOC_APPEND( l, fd_store_align(),        sizeof(fd_store_t)                 );
      52           9 :   void *       map    = FD_SCRATCH_ALLOC_APPEND( l, fd_store_map_align(),    fd_store_map_footprint ( fec_max ) );
      53           9 :   void *       shpool = FD_SCRATCH_ALLOC_APPEND( l, fd_store_pool_align(),   fd_store_pool_footprint()          );
      54           9 :   void *       shele  = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_store_fec_t), sizeof(fd_store_fec_t)*fec_max     );
      55           9 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_store_align() ) == (ulong)shmem + footprint );
      56             : 
      57           9 :   fd_memset( store, 0, sizeof(fd_store_t) );
      58           9 :   store->store_gaddr    = fd_wksp_gaddr_fast( wksp, store  );
      59           9 :   store->pool_mem_gaddr = fd_wksp_gaddr_fast( wksp, shpool );
      60           9 :   store->pool_ele_gaddr = fd_wksp_gaddr_fast( wksp, shele  );
      61           9 :   store->map_gaddr      = fd_wksp_gaddr_fast( wksp, fd_store_map_join( fd_store_map_new ( map, fec_max, seed ) ) );
      62             : 
      63           9 :   store->part_cnt = part_cnt;
      64           9 :   store->fec_max  = fec_max;
      65           9 :   store->root     = null;
      66             : 
      67           9 :   fd_store_pool_t pool = fd_store_pool( store );
      68           9 :   if( FD_UNLIKELY( !fd_store_pool_new( shpool ) ) ) {
      69           0 :     FD_LOG_WARNING(( "fd_store_pool_new failed" ));
      70           0 :     return NULL;
      71           0 :   }
      72           9 :   fd_store_pool_reset( &pool, 0 );
      73             : 
      74           9 :   FD_COMPILER_MFENCE();
      75           9 :   FD_VOLATILE( store->magic ) = FD_STORE_MAGIC;
      76           9 :   FD_COMPILER_MFENCE();
      77             : 
      78           9 :   return shmem;
      79           9 : }
      80             : 
      81             : fd_store_t *
      82           9 : fd_store_join( void * shstore ) {
      83             : 
      84           9 :   if( FD_UNLIKELY( !shstore ) ) {
      85           0 :     FD_LOG_WARNING(( "NULL store" ));
      86           0 :     return NULL;
      87           0 :   }
      88             : 
      89           9 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)shstore, fd_store_align() ) ) ) {
      90           0 :     FD_LOG_WARNING(( "misaligned store" ));
      91           0 :     return NULL;
      92           0 :   }
      93             : 
      94           9 :   fd_wksp_t * wksp = fd_wksp_containing( shstore );
      95           9 :   if( FD_UNLIKELY( !wksp ) ) {
      96           0 :     FD_LOG_WARNING(( "store must be part of a workspace" ));
      97           0 :     return NULL;
      98           0 :   }
      99             : 
     100           9 :   fd_store_t * store = (fd_store_t *)shstore;
     101           9 :   if( FD_UNLIKELY( store->magic!=FD_STORE_MAGIC ) ) {
     102           0 :     FD_LOG_WARNING(( "bad magic" ));
     103           0 :     return NULL;
     104           0 :   }
     105             : 
     106           9 :   return store;
     107           9 : }
     108             : 
     109             : void *
     110           3 : fd_store_leave( fd_store_t const * store ) {
     111             : 
     112           3 :   if( FD_UNLIKELY( !store ) ) {
     113           0 :     FD_LOG_WARNING(( "NULL store" ));
     114           0 :     return NULL;
     115           0 :   }
     116             : 
     117           3 :   return (void *)store;
     118           3 : }
     119             : 
     120             : void *
     121           3 : fd_store_delete( void * shstore ) {
     122             : 
     123           3 :   if( FD_UNLIKELY( !shstore ) ) {
     124           0 :     FD_LOG_WARNING(( "NULL store" ));
     125           0 :     return NULL;
     126           0 :   }
     127             : 
     128           3 :   if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)shstore, fd_store_align() ) ) ) {
     129           0 :     FD_LOG_WARNING(( "misaligned store" ));
     130           0 :     return NULL;
     131           0 :   }
     132             : 
     133           3 :   fd_store_t * store = (fd_store_t *)shstore;
     134           3 :   if( FD_UNLIKELY( store->magic!=FD_STORE_MAGIC ) ) {
     135           0 :     FD_LOG_WARNING(( "bad magic" ));
     136           0 :     return NULL;
     137           0 :   }
     138             : 
     139           3 :   FD_COMPILER_MFENCE();
     140           3 :   FD_VOLATILE( store->magic ) = 0UL;
     141           3 :   FD_COMPILER_MFENCE();
     142             : 
     143           3 :   return shstore;
     144           3 : }
     145             : 
     146             : fd_store_fec_t *
     147             : fd_store_insert( fd_store_t * store,
     148             :                  ulong        part_idx,
     149          54 :                  fd_hash_t  * merkle_root ) {
     150          54 :   if( FD_UNLIKELY( fd_store_query_const( store, merkle_root ) ) ) {
     151           3 :     FD_BASE58_ENCODE_32_BYTES( merkle_root->key, merkle_root_b58 );
     152           3 :     FD_LOG_WARNING(( "Merkle root %s already in store.  Ignoring insert.", merkle_root_b58 ));
     153           3 :     return NULL;
     154           3 :   }
     155             : 
     156          51 :   int err;
     157          51 :   fd_store_pool_t  pool = fd_store_pool( store );
     158          51 :   fd_store_fec_t * fec  = fd_store_pool_acquire( &pool, NULL, BLOCKING, &err );
     159             : 
     160          51 :   if( FD_UNLIKELY( err == FD_POOL_ERR_EMPTY   ) ) { FD_LOG_WARNING(( "store full %s",    fd_store_pool_strerror( err ) )); return NULL; } /* FIXME: eviction? max bound guaranteed for worst-case? */
     161          51 :   if( FD_UNLIKELY( err == FD_POOL_ERR_CORRUPT ) ) { FD_LOG_CRIT   (( "store corrupt %s", fd_store_pool_strerror( err ) )); }
     162          51 :   FD_TEST( fec );
     163             : 
     164          51 :   fec->key.mr           = *merkle_root;
     165          51 :   fec->key.part         = part_idx;
     166          51 :   fec->cmr              = hash_null;
     167          51 :   fec->next             = null;
     168          51 :   fec->parent           = null;
     169          51 :   fec->child            = null;
     170          51 :   fec->sibling          = null;
     171          51 :   fec->data_sz          = 0UL;
     172          51 :   if( FD_UNLIKELY( store->root == null ) ) store->root = fd_store_pool_idx( &pool, fec );
     173          51 :   fd_store_map_ele_insert( fd_store_map( store ), fec, fd_store_fec0( store ) );
     174             : 
     175          51 :   return fec;
     176          51 : }
     177             : 
     178             : fd_store_fec_t *
     179          39 : fd_store_link( fd_store_t * store, fd_hash_t * merkle_root, fd_hash_t * chained_merkle_root ) {
     180             : 
     181          39 : # if FD_STORE_USE_HANDHOLDING
     182          39 :   if( FD_UNLIKELY( !fd_store_query_const( store, merkle_root         ) ) ) {
     183           0 :     FD_BASE58_ENCODE_32_BYTES( merkle_root->key, merkle_root_b58 );
     184           0 :     FD_LOG_WARNING(( "missing merkle root %s", merkle_root_b58 ) );
     185           0 :     return NULL;
     186           0 :   }
     187          39 :   if( FD_UNLIKELY( !fd_store_query_const( store, chained_merkle_root ) ) ) {
     188           0 :     FD_BASE58_ENCODE_32_BYTES( chained_merkle_root->key, chained_merkle_root_b58 );
     189           0 :     FD_LOG_WARNING(( "missing chained merkle root %s", chained_merkle_root_b58 ) );
     190           0 :     return NULL;
     191           0 :   }
     192          39 : # endif
     193             : 
     194          39 :   fd_store_pool_t   pool   = fd_store_pool( store );
     195          39 :   fd_store_fec_t  * parent = fd_store_query( store, chained_merkle_root );
     196          39 :   fd_store_fec_t  * child  = fd_store_query( store, merkle_root );
     197             : 
     198          39 :   if( FD_UNLIKELY( child->parent != null ) ) return child; /* already linked */
     199          39 :   child->parent = fd_store_pool_idx( &pool, parent );
     200          39 :   if( FD_LIKELY( parent->child == null ) ) {
     201          36 :     parent->child = fd_store_pool_idx( &pool, child ); /* set as left-child. */
     202          36 :   } else {
     203           3 :     fd_store_fec_t * curr = fd_store_pool_ele( &pool, parent->child );
     204           3 :     while( curr->sibling != null ) curr = fd_store_pool_ele( &pool, curr->sibling );
     205           3 :     curr->sibling = fd_store_pool_idx( &pool, child ); /* set as right-sibling. */
     206           3 :   }
     207          39 :   return child;
     208          39 : }
     209             : 
     210             : fd_store_fec_t *
     211             : fd_store_publish( fd_store_t *      store,
     212           3 :                   fd_hash_t const * merkle_root ) {
     213             : 
     214           3 : # if FD_STORE_USE_HANDHOLDING
     215           3 :   if( FD_UNLIKELY( !fd_store_query( store, merkle_root ) ) ) {
     216           0 :     FD_BASE58_ENCODE_32_BYTES( merkle_root->key, merkle_root_b58 );
     217           0 :     FD_LOG_WARNING(( "merkle root %s not found", merkle_root_b58 ));
     218           0 :     return NULL;
     219           0 :   }
     220           3 : # endif
     221             : 
     222           3 :   fd_store_map_t  * map  = fd_store_map ( store );
     223           3 :   fd_store_pool_t   pool = fd_store_pool( store );
     224           3 :   fd_store_fec_t  * fec0 = fd_store_fec0( store );
     225           3 :   fd_store_fec_t  * oldr = fd_store_root( store );
     226           3 :   fd_store_fec_t  * newr = fd_store_query( store, merkle_root );
     227             : 
     228             :   /* First, remove the previous root, and push it as the first element
     229             :      of the BFS queue. */
     230             : 
     231           3 :   fd_store_fec_t * head = fd_store_map_ele_remove( map, &oldr->key, NULL, fec0 );
     232           3 :   head->next            = null; /* clear map next */
     233           3 :   fd_store_fec_t * tail = head; /* tail of BFS queue */
     234             : 
     235             :   /* Second, BFS down the tree, pruning all of root's ancestors and also
     236             :      any descendants of those ancestors. */
     237             : 
     238          42 :   while( FD_LIKELY( head ) ) {
     239          39 :     fd_store_fec_t * child = fd_store_pool_ele( &pool, head->child );         /* left-child */
     240          78 :     while( FD_LIKELY( child ) ) {                                             /* iterate over children */
     241          39 :       if( FD_LIKELY( child != newr ) ) {                                      /* stop at new root */
     242          36 :         tail->next = fd_store_map_idx_remove( map, &child->key, null, fec0 ); /* remove node from map to reuse `.next` */
     243          36 :         tail       = fd_store_pool_ele( &pool, tail->next );                  /* push onto BFS queue (so descendants can be pruned) */
     244          36 :         tail->next = null;                                                    /* clear map next */
     245          36 :       }
     246          39 :       child = fd_store_pool_ele( &pool, child->sibling );                     /* right-sibling */
     247          39 :     }
     248          39 :     fd_store_fec_t * next = fd_store_pool_ele( &pool, head->next ); /* pophead */
     249          39 :     int err = fd_store_pool_release( &pool, head, BLOCKING );       /* release */
     250          39 :     if( FD_UNLIKELY( err ) ) FD_LOG_CRIT(( "failed to release fec %s", fd_store_pool_strerror( err ) ));
     251          39 :     head = next; /* advance */
     252          39 :   }
     253           3 :   newr->parent = null;                             /* unlink old root */
     254           3 :   store->root  = fd_store_pool_idx( &pool, newr ); /* replace with new root */
     255           3 :   return newr;
     256           3 : }
     257             : 
     258             : fd_store_t *
     259           3 : fd_store_clear( fd_store_t * store ) {
     260             : 
     261           3 : # if FD_STORE_USE_HANDHOLDING
     262           3 :   if( FD_UNLIKELY( !fd_store_root( store ) ) ) { FD_LOG_WARNING(( "calling clear on an empty store" )); return NULL; }
     263           3 : # endif
     264             : 
     265           3 :   fd_store_map_t * map  = fd_store_map( store );
     266           3 :   fd_store_pool_t  pool = fd_store_pool( store );
     267           3 :   fd_store_fec_t * fec0 = fd_store_fec0( store );
     268             : 
     269           3 :   fd_store_fec_t * head = fd_store_root( store );
     270           3 :   fd_store_fec_t * tail = head;
     271             : 
     272           3 :   for( fd_store_map_iter_t iter = fd_store_map_iter_init( map, fec0 );
     273           6 :        !fd_store_map_iter_done( iter, map, fec0 );
     274           3 :        iter = fd_store_map_iter_next( iter, map, fec0 ) ) {
     275           3 :     ulong idx = fd_store_map_iter_idx( iter, map, fec0 );
     276           3 :     if( FD_UNLIKELY( idx == fd_store_pool_idx( &pool, head ) ) ) continue;
     277           0 :     tail->sibling = idx;
     278           0 :     tail          = fd_store_pool_ele( &pool, tail->sibling );
     279           0 :   }
     280           3 :   tail->sibling = null;
     281           3 :   for( ulong idx = fd_store_pool_idx( &pool, head );
     282           6 :        FD_LIKELY( idx != null );
     283           3 :        idx = fd_store_pool_ele( &pool, idx )->sibling ) {
     284           3 :     fd_store_fec_t * fec = fd_store_pool_ele( &pool, idx );
     285           3 :     fd_store_map_idx_remove( map, &fec->key, null, fec0 );
     286           3 :     fd_store_pool_release( &pool, fec, 1 );
     287           3 :   }
     288           3 :   store->root = null;
     289           3 :   return store;
     290           3 : }
     291             : 
     292             : int
     293           3 : fd_store_verify( fd_store_t * store ) {
     294             : 
     295           3 :   fd_store_map_t * map  = fd_store_map( store );
     296           3 :   fd_store_fec_t * fec0 = fd_store_fec0( store );
     297             : 
     298           3 :   ulong part_sz = store->fec_max / store->part_cnt;
     299           3 :   if( part_sz != map->seed ) {
     300           0 :     FD_LOG_WARNING(( "part_sz (%lu) != map->seed (%lu)", part_sz, map->seed ));
     301           0 :     return -1;
     302           0 :   }
     303             : 
     304             :   /* iter the map, check that the partitions are correct */
     305             : 
     306           3 :   for( fd_store_map_iter_t iter = fd_store_map_iter_init( map, fec0 ); !fd_store_map_iter_done( iter, map, fec0 ); iter = fd_store_map_iter_next( iter, map, fec0 ) ) {
     307           0 :     fd_store_fec_t const * fec = fd_store_map_iter_ele_const( iter, map, fec0 );
     308           0 :     if( FD_UNLIKELY( !fec ) ) {
     309           0 :       FD_LOG_WARNING(( "NULL ele" ));
     310           0 :       return -1;
     311           0 :     }
     312           0 :     ulong chain_idx = fd_store_map_private_chain_idx( &fec->key, map->seed, map->chain_cnt );
     313             :     /* the chain_idx should be in the range of the partition */
     314           0 :     if( FD_UNLIKELY( chain_idx < part_sz * fec->key.part || chain_idx >= part_sz * (fec->key.part + 1) ) ) {
     315           0 :       FD_LOG_WARNING(( "chain_idx %lu not in range %lu-%lu", chain_idx, part_sz * fec->key.part, part_sz * (fec->key.part + 1) ) );
     316           0 :       return -1;
     317           0 :     }
     318           0 :   }
     319           3 :   fd_store_pool_t pool = fd_store_pool( store );
     320           3 :   if( FD_UNLIKELY( fd_store_pool_verify( &pool )==-1 ) ) return -1;
     321           3 :   return fd_store_map_verify( map, store->fec_max, fec0 );
     322           3 : }
     323             : 
     324             : #include <stdio.h>
     325             : 
     326             : static void
     327          42 : print( fd_store_t const * store, fd_store_fec_t const * fec, int space, const char * prefix ) {
     328             : 
     329          42 :   if( fec == NULL ) return;
     330          42 :   if( space > 0 ) printf( "\n" );
     331         798 :   for( int i = 0; i < space; i++ ) printf( " " );
     332          42 :   FD_BASE58_ENCODE_32_BYTES( fec->key.mr.key, key_mr_b58 );
     333          42 :   printf( "%s%s", prefix, key_mr_b58 );
     334             : 
     335          42 :   fd_store_pool_t pool = fd_store_pool( store );
     336          42 :   fd_store_fec_t const * curr = fd_store_pool_ele_const( &pool, fec->child );
     337          42 :   char new_prefix[1024]; /* FIXME size this correctly */
     338          81 :   while( curr ) {
     339          39 :     if( fd_store_pool_ele_const( &pool, curr->sibling ) ) {
     340           3 :       sprintf( new_prefix, "├── " ); /* branch indicating more siblings follow */
     341           3 :       print( store, curr, space + 4, new_prefix );
     342          36 :     } else {
     343          36 :       sprintf( new_prefix, "└── " ); /* end branch */
     344          36 :       print( store, curr, space + 4, new_prefix );
     345          36 :     }
     346          39 :     curr = fd_store_pool_ele_const( &pool, curr->sibling );
     347          39 :   }
     348          42 : }
     349             : 
     350             : void
     351           3 : fd_store_print( fd_store_t const * store ) {
     352           3 :   FD_LOG_NOTICE( ( "\n\n[Store]" ) );
     353           3 :   print( store, fd_store_root_const( store ), 0, "" );
     354             :   printf( "\n\n" );
     355           3 : }

Generated by: LCOV version 1.14