LCOV - code coverage report
Current view: top level - disco/store - fd_store.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 185 248 74.6 %
Date: 2025-09-19 04:41:14 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             : 
     151          54 : # if FD_STORE_USE_HANDHOLDING
     152          54 :   if( FD_UNLIKELY( fd_store_query_const( store, merkle_root ) ) ) { FD_LOG_WARNING(( "merkle root %s already in store", FD_BASE58_ENC_32_ALLOCA( merkle_root ) )); return NULL; }
     153          51 : # endif
     154          51 :   int err;
     155             : 
     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_WARNING(( "store corrupt %s", fd_store_pool_strerror( err ) )); return NULL; }
     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         ) ) ) { FD_LOG_WARNING(( "missing merkle root %s",         FD_BASE58_ENC_32_ALLOCA( merkle_root         ) ) ); return NULL; }
     182          39 :   if( FD_UNLIKELY( !fd_store_query_const( store, chained_merkle_root ) ) ) { FD_LOG_WARNING(( "missing chained merkle root %s", FD_BASE58_ENC_32_ALLOCA( chained_merkle_root ) ) ); return NULL; }
     183          39 : # endif
     184             : 
     185          39 :   fd_store_pool_t   pool   = fd_store_pool( store );
     186          39 :   fd_store_fec_t  * parent = fd_store_query( store, chained_merkle_root );
     187          39 :   fd_store_fec_t  * child  = fd_store_query( store, merkle_root );
     188             : 
     189          39 :   child->parent = fd_store_pool_idx( &pool, parent );
     190          39 :   if( FD_LIKELY( parent->child == null ) ) {
     191          36 :     parent->child = fd_store_pool_idx( &pool, child ); /* set as left-child. */
     192          36 :   } else {
     193           3 :     fd_store_fec_t * curr = fd_store_pool_ele( &pool, parent->child );
     194           3 :     while( curr->sibling != null ) curr = fd_store_pool_ele( &pool, curr->sibling );
     195           3 :     curr->sibling = fd_store_pool_idx( &pool, child ); /* set as right-sibling. */
     196           3 :   }
     197          39 :   return child;
     198          39 : }
     199             : 
     200             : fd_store_fec_t *
     201             : fd_store_publish( fd_store_t *      store,
     202           3 :                   fd_hash_t const * merkle_root ) {
     203             : 
     204           3 : # if FD_STORE_USE_HANDHOLDING
     205           3 :   if( FD_UNLIKELY( !fd_store_query( store, merkle_root ) ) ) { FD_LOG_WARNING(( "merkle root %s not found", FD_BASE58_ENC_32_ALLOCA( merkle_root ) )); return NULL; }
     206           3 : # endif
     207             : 
     208           3 :   fd_store_map_t  * map  = fd_store_map ( store );
     209           3 :   fd_store_pool_t   pool = fd_store_pool( store );
     210           3 :   fd_store_fec_t  * fec0 = fd_store_fec0( store );
     211           3 :   fd_store_fec_t  * oldr = fd_store_root( store );
     212           3 :   fd_store_fec_t  * newr = fd_store_query( store, merkle_root );
     213             : 
     214             :   /* First, remove the previous root, and push it as the first element
     215             :      of the BFS queue. */
     216             : 
     217           3 :   fd_store_fec_t * head = fd_store_map_ele_remove( map, &oldr->key, NULL, fec0 );
     218           3 :   head->next            = null; /* clear map next */
     219           3 :   fd_store_fec_t * tail = head; /* tail of BFS queue */
     220             : 
     221             :   /* Second, BFS down the tree, pruning all of root's ancestors and also
     222             :      any descendants of those ancestors. */
     223             : 
     224          42 :   while( FD_LIKELY( head ) ) {
     225          39 :     fd_store_fec_t * child = fd_store_pool_ele( &pool, head->child );         /* left-child */
     226          78 :     while( FD_LIKELY( child ) ) {                                             /* iterate over children */
     227          39 :       if( FD_LIKELY( child != newr ) ) {                                      /* stop at new root */
     228          36 :         tail->next = fd_store_map_idx_remove( map, &child->key, null, fec0 ); /* remove node from map to reuse `.next` */
     229          36 :         tail       = fd_store_pool_ele( &pool, tail->next );                  /* push onto BFS queue (so descendants can be pruned) */
     230          36 :         tail->next = null;                                                    /* clear map next */
     231          36 :       }
     232          39 :       child = fd_store_pool_ele( &pool, child->sibling );                     /* right-sibling */
     233          39 :     }
     234          39 :     fd_store_fec_t * next = fd_store_pool_ele( &pool, head->next ); /* pophead */
     235          39 :     int err = fd_store_pool_release( &pool, head, BLOCKING );       /* release */
     236          39 :     if( FD_UNLIKELY( err != FD_POOL_SUCCESS ) ) {
     237           0 :       FD_LOG_WARNING(( "failed to release fec %s", fd_store_pool_strerror( err ) ));
     238           0 :       return NULL;
     239           0 :     }
     240          39 :     head = next;                                                    /* advance */
     241          39 :   }
     242           3 :   newr->parent = null;                             /* unlink old root */
     243           3 :   store->root  = fd_store_pool_idx( &pool, newr ); /* replace with new root */
     244           3 :   return newr;
     245           3 : }
     246             : 
     247             : fd_store_t *
     248           3 : fd_store_clear( fd_store_t * store ) {
     249             : 
     250           3 : # if FD_STORE_USE_HANDHOLDING
     251           3 :   if( FD_UNLIKELY( !fd_store_root( store ) ) ) { FD_LOG_WARNING(( "calling clear on an empty store" )); return NULL; }
     252           3 : # endif
     253             : 
     254           3 :   fd_store_map_t * map  = fd_store_map( store );
     255           3 :   fd_store_pool_t  pool = fd_store_pool( store );
     256           3 :   fd_store_fec_t * fec0 = fd_store_fec0( store );
     257             : 
     258           3 :   fd_store_fec_t * head = fd_store_root( store );
     259           3 :   fd_store_fec_t * tail = head;
     260             : 
     261           3 :   for( fd_store_map_iter_t iter = fd_store_map_iter_init( map, fec0 );
     262           6 :        !fd_store_map_iter_done( iter, map, fec0 );
     263           3 :        iter = fd_store_map_iter_next( iter, map, fec0 ) ) {
     264           3 :     ulong idx = fd_store_map_iter_idx( iter, map, fec0 );
     265           3 :     if( FD_UNLIKELY( idx == fd_store_pool_idx( &pool, head ) ) ) continue;
     266           0 :     tail->sibling = idx;
     267           0 :     tail          = fd_store_pool_ele( &pool, tail->sibling );
     268           0 :   }
     269           3 :   tail->sibling = null;
     270           3 :   for( ulong idx = fd_store_pool_idx( &pool, head );
     271           6 :        FD_LIKELY( idx != null );
     272           3 :        idx = fd_store_pool_ele( &pool, idx )->sibling ) {
     273           3 :     fd_store_fec_t * fec = fd_store_pool_ele( &pool, idx );
     274           3 :     fd_store_map_idx_remove( map, &fec->key, null, fec0 );
     275           3 :     fd_store_pool_release( &pool, fec, 1 );
     276           3 :   }
     277           3 :   store->root = null;
     278           3 :   return store;
     279           3 : }
     280             : 
     281             : int
     282           3 : fd_store_verify( fd_store_t * store ) {
     283             : 
     284           3 :   fd_store_map_t * map  = fd_store_map( store );
     285           3 :   fd_store_fec_t * fec0 = fd_store_fec0( store );
     286             : 
     287           3 :   ulong part_sz = store->fec_max / store->part_cnt;
     288           3 :   if( part_sz != map->seed ) {
     289           0 :     FD_LOG_WARNING(( "part_sz (%lu) != map->seed (%lu)", part_sz, map->seed ));
     290           0 :     return -1;
     291           0 :   }
     292             : 
     293             :   /* iter the map, check that the partitions are correct */
     294             : 
     295           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 ) ) {
     296           0 :     fd_store_fec_t const * fec = fd_store_map_iter_ele_const( iter, map, fec0 );
     297           0 :     if( FD_UNLIKELY( !fec ) ) {
     298           0 :       FD_LOG_WARNING(( "NULL ele" ));
     299           0 :       return -1;
     300           0 :     }
     301           0 :     ulong chain_idx = fd_store_map_private_chain_idx( &fec->key, map->seed, map->chain_cnt );
     302             :     /* the chain_idx should be in the range of the partition */
     303           0 :     if( FD_UNLIKELY( chain_idx < part_sz * fec->key.part || chain_idx >= part_sz * (fec->key.part + 1) ) ) {
     304           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) ) );
     305           0 :       return -1;
     306           0 :     }
     307           0 :   }
     308           3 :   fd_store_pool_t pool = fd_store_pool_const( store );
     309           3 :   if( FD_UNLIKELY( fd_store_pool_verify( &pool )==-1 ) ) return -1;
     310           3 :   return fd_store_map_verify( map, store->fec_max, fec0 );
     311           3 : }
     312             : 
     313             : #include <stdio.h>
     314             : 
     315             : static void
     316          42 : print( fd_store_t const * store, fd_store_fec_t const * fec, int space, const char * prefix ) {
     317             : 
     318          42 :   if( fec == NULL ) return;
     319          42 :   if( space > 0 ) printf( "\n" );
     320         798 :   for( int i = 0; i < space; i++ ) printf( " " );
     321          42 :   printf( "%s%s", prefix, FD_BASE58_ENC_32_ALLOCA( &fec->key.mr ) );
     322             : 
     323           0 :   fd_store_pool_t pool = fd_store_pool_const( store );
     324          42 :   fd_store_fec_t const * curr = fd_store_pool_ele_const( &pool, fec->child );
     325          42 :   char new_prefix[1024]; /* FIXME size this correctly */
     326          81 :   while( curr ) {
     327          39 :     if( fd_store_pool_ele_const( &pool, curr->sibling ) ) {
     328           3 :       sprintf( new_prefix, "├── " ); /* branch indicating more siblings follow */
     329           3 :       print( store, curr, space + 4, new_prefix );
     330          36 :     } else {
     331          36 :       sprintf( new_prefix, "└── " ); /* end branch */
     332          36 :       print( store, curr, space + 4, new_prefix );
     333          36 :     }
     334          39 :     curr = fd_store_pool_ele_const( &pool, curr->sibling );
     335          39 :   }
     336          42 : }
     337             : 
     338             : void
     339           3 : fd_store_print( fd_store_t const * store ) {
     340           3 :   FD_LOG_NOTICE( ( "\n\n[Store]" ) );
     341           3 :   print( store, fd_store_root_const( store ), 0, "" );
     342             :   printf( "\n\n" );
     343           3 : }

Generated by: LCOV version 1.14