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

Generated by: LCOV version 1.14