LCOV - code coverage report
Current view: top level - discof/genesis - fd_genesis_client.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 222 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 13 0.0 %

          Line data    Source code
       1             : #include "fd_genesis_client_private.h"
       2             : 
       3             : #include "../../third_party/picohttpparser/picohttpparser.h"
       4             : #include "../../util/fd_util.h"
       5             : #include "../../waltz/http/fd_http.h"
       6             : #include "../../ballet/sha256/fd_sha256.h"
       7             : 
       8             : #include <errno.h>
       9             : #include <netinet/in.h>
      10             : #include <string.h>
      11             : #include <strings.h>
      12             : #include <sys/epoll.h>
      13             : #include <sys/socket.h>
      14             : #include <unistd.h>
      15             : #include <stdlib.h>
      16             : 
      17             : FD_FN_CONST ulong
      18           0 : fd_genesis_client_align( void ) {
      19           0 :   return alignof(fd_genesis_client_t);
      20           0 : }
      21             : 
      22             : FD_FN_CONST ulong
      23           0 : fd_genesis_client_footprint( void ) {
      24           0 :   return sizeof(fd_genesis_client_t);
      25           0 : }
      26             : 
      27             : void *
      28           0 : fd_genesis_client_new( void * shmem ) {
      29           0 :   fd_genesis_client_t * gen = (fd_genesis_client_t *)shmem;
      30             : 
      31           0 :   if( FD_UNLIKELY( !shmem ) ) {
      32           0 :     FD_LOG_WARNING(( "NULL shmem" ));
      33           0 :     return NULL;
      34           0 :   }
      35             : 
      36           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_genesis_client_align() ) ) ) {
      37           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
      38           0 :     return NULL;
      39           0 :   }
      40             : 
      41           0 :   FD_COMPILER_MFENCE();
      42           0 :   FD_VOLATILE( gen->magic ) = FD_GENESIS_CLIENT_MAGIC;
      43           0 :   FD_COMPILER_MFENCE();
      44             : 
      45           0 :   return (void *)gen;
      46           0 : }
      47             : 
      48             : fd_genesis_client_t *
      49           0 : fd_genesis_client_join( void * shgen ) {
      50           0 :   if( FD_UNLIKELY( !shgen ) ) {
      51           0 :     FD_LOG_WARNING(( "NULL shgen" ));
      52           0 :     return NULL;
      53           0 :   }
      54             : 
      55           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shgen, fd_genesis_client_align() ) ) ) {
      56           0 :     FD_LOG_WARNING(( "misaligned shgen" ));
      57           0 :     return NULL;
      58           0 :   }
      59             : 
      60           0 :   fd_genesis_client_t * gen = (fd_genesis_client_t *)shgen;
      61             : 
      62           0 :   if( FD_UNLIKELY( gen->magic!=FD_GENESIS_CLIENT_MAGIC ) ) {
      63           0 :     FD_LOG_WARNING(( "bad magic" ));
      64           0 :     return NULL;
      65           0 :   }
      66             : 
      67           0 :   return gen;
      68           0 : }
      69             : 
      70             : void
      71             : fd_genesis_client_init( fd_genesis_client_t * client,
      72             :                         fd_ip4_port_t const * servers,
      73             :                         ulong                 servers_len,
      74           0 :                         int                   epoll_fd ) {
      75           0 :   FD_TEST( servers_len<=FD_TOPO_GOSSIP_ENTRYPOINTS_MAX );
      76           0 :   FD_TEST( epoll_fd!=-1 );
      77             : 
      78           0 :   client->epoll_fd = epoll_fd;
      79             : 
      80           0 :   ulong peer_cnt = 0UL;
      81             : 
      82           0 :   for( ulong i=0UL; i<servers_len; i++ ) {
      83           0 :     fd_ip4_port_t server = servers[ i ];
      84           0 :     server.port = fd_ushort_bswap( 8899 );  // TODO: SPECIFY IN CONFIG
      85             : 
      86           0 :     int sockfd = socket( AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0 );
      87           0 :     if( FD_UNLIKELY( -1==sockfd ) ) FD_LOG_ERR(( "socket() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
      88             : 
      89           0 :     struct sockaddr_in addr = {
      90           0 :       .sin_family = AF_INET,
      91           0 :       .sin_port   = server.port,
      92           0 :       .sin_addr   = { .s_addr = server.addr }
      93           0 :     };
      94             : 
      95           0 :     if( FD_UNLIKELY( -1==connect( sockfd, fd_type_pun( &addr ), sizeof(addr) ) && errno!=EINPROGRESS ) ) {
      96           0 :       if( FD_UNLIKELY( -1==close( sockfd ) ) ) FD_LOG_ERR(( "close() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
      97           0 :       continue;
      98           0 :     }
      99             : 
     100           0 :     struct epoll_event ev = { .events = EPOLLIN|EPOLLOUT, .data.fd = sockfd };
     101           0 :     if( FD_UNLIKELY( -1==epoll_ctl( epoll_fd, EPOLL_CTL_ADD, sockfd, &ev ) ) ) FD_LOG_ERR(( "epoll_ctl(ADD) failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     102             : 
     103           0 :     client->pollfds[ peer_cnt ] = (struct pollfd){
     104           0 :       .fd = sockfd,
     105           0 :       .events = POLLIN | POLLOUT,
     106           0 :       .revents = 0
     107           0 :     };
     108           0 :     client->peers[ peer_cnt ].addr = server;
     109           0 :     client->peers[ peer_cnt ].writing = 1;
     110           0 :     client->peers[ peer_cnt ].request_bytes_sent = 0UL;
     111           0 :     client->peers[ peer_cnt ].response_bytes_read = 0UL;
     112           0 :     peer_cnt++;
     113           0 :   }
     114             : 
     115           0 :   for( ulong i=peer_cnt; i<FD_TOPO_GOSSIP_ENTRYPOINTS_MAX; i++ ) client->pollfds[ i ].fd = -1;
     116             : 
     117           0 :   client->peer_cnt = peer_cnt;
     118           0 :   client->remaining_peer_cnt = peer_cnt;
     119           0 :   client->start_time_nanos = LONG_MAX;
     120           0 : }
     121             : 
     122             : static void
     123             : close_one( fd_genesis_client_t * client,
     124           0 :            ulong                 idx ) {
     125           0 :   if( FD_UNLIKELY( -1==epoll_ctl( client->epoll_fd, EPOLL_CTL_DEL, client->pollfds[ idx ].fd, NULL ) ) ) FD_LOG_ERR(( "epoll_ctl(DEL) failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     126             : 
     127           0 :   if( FD_UNLIKELY( -1==close( client->pollfds[ idx ].fd ) ) ) FD_LOG_ERR(( "close() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     128           0 :   client->pollfds[ idx ].fd = -1;
     129           0 :   client->remaining_peer_cnt--;
     130           0 : }
     131             : 
     132             : static void
     133           0 : close_all( fd_genesis_client_t * client ) {
     134           0 :   for( ulong i=0UL; i<client->peer_cnt; i++ ) {
     135           0 :     if( FD_UNLIKELY( -1==client->pollfds[ i ].fd ) ) continue;
     136           0 :     close_one( client, i );
     137           0 :   }
     138           0 : }
     139             : 
     140             : static void
     141             : write_conn( fd_genesis_client_t * client,
     142           0 :             ulong                 conn_idx ) {
     143           0 :   fd_genesis_client_peer_t * peer = &client->peers[ conn_idx ];
     144             : 
     145           0 :   if( FD_LIKELY( !peer->writing ) ) return;
     146             : 
     147           0 :   char request[ 1024UL ];
     148           0 :   ulong request_sz = 0UL;
     149           0 :   FD_TEST( fd_cstr_printf_check( request, sizeof(request), &request_sz, "GET /genesis.tar.bz2 HTTP/1.1\r\n"
     150           0 :                                                                       "Cache-Control: no-cache\r\n"
     151           0 :                                                                       "Connection: keep-alive\r\n"
     152           0 :                                                                       "Pragma: no-cache\r\n"
     153           0 :                                                                       "User-Agent: Firedancer\r\n"
     154           0 :                                                                       "Host: " FD_IP4_ADDR_FMT ":%hu\r\n\r\n",
     155           0 :                                                                       FD_IP4_ADDR_FMT_ARGS( peer->addr.addr ), fd_ushort_bswap( peer->addr.port ) ) );
     156           0 :   FD_TEST( request_sz > peer->request_bytes_sent );
     157             : 
     158           0 :   long written = sendto( client->pollfds[ conn_idx ].fd,
     159           0 :                          request+peer->request_bytes_sent,
     160           0 :                          request_sz-peer->request_bytes_sent,
     161           0 :                          MSG_NOSIGNAL,
     162           0 :                          NULL,
     163           0 :                          0 );
     164           0 :   if( FD_UNLIKELY( -1==written && errno==EAGAIN ) ) return; /* No data was written, continue. */
     165           0 :   else if( FD_UNLIKELY( -1==written ) ) {
     166           0 :     close_one( client, conn_idx );
     167           0 :     return;
     168           0 :   }
     169             : 
     170           0 :   peer->request_bytes_sent += (ulong)written;
     171           0 :   if( FD_UNLIKELY( peer->request_bytes_sent==request_sz ) ) {
     172           0 :     peer->writing = 0;
     173           0 :     peer->response_bytes_read = 0UL;
     174             :     /* A connected socket is always writable so drop EPOLLOUT or the
     175             :        waker refires on every rearm while we wait for the response */
     176           0 :     client->pollfds[ conn_idx ].events = POLLIN;
     177           0 :     struct epoll_event ev = { .events = EPOLLIN, .data.fd = client->pollfds[ conn_idx ].fd };
     178           0 :     if( FD_UNLIKELY( -1==epoll_ctl( client->epoll_fd, EPOLL_CTL_MOD, client->pollfds[ conn_idx ].fd, &ev ) ) ) FD_LOG_ERR(( "epoll_ctl(MOD) failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     179           0 :   }
     180           0 : }
     181             : 
     182             : static ulong
     183             : rpc_phr_content_length( struct phr_header * headers,
     184           0 :                         ulong               num_headers ) {
     185           0 :   for( ulong i=0UL; i<num_headers; i++ ) {
     186           0 :     if( FD_LIKELY( headers[i].name_len!=14UL ) ) continue;
     187           0 :     if( FD_LIKELY( strncasecmp( headers[i].name, "Content-Length", 14UL ) ) ) continue;
     188           0 :     ulong content_length = 0UL;
     189           0 :     if( FD_UNLIKELY( fd_http_parse_content_len( headers[i].value, (ulong)headers[i].value_len, &content_length ) ) ) return ULONG_MAX;
     190           0 :     if( FD_UNLIKELY( content_length>UINT_MAX ) ) return ULONG_MAX; /* prevent overflow */
     191           0 :     return content_length;
     192           0 :   }
     193           0 :   return ULONG_MAX;
     194           0 : }
     195             : 
     196             : static int
     197             : read_conn( fd_genesis_client_t * client,
     198             :            ulong                 conn_idx,
     199             :            uchar **              buffer,
     200           0 :            ulong *               buffer_sz ) {
     201           0 :   fd_genesis_client_peer_t * peer = &client->peers[ conn_idx ];
     202             : 
     203           0 :   if( FD_UNLIKELY( peer->writing ) ) return 1;
     204           0 :   long read = recvfrom( client->pollfds[ conn_idx ].fd,
     205           0 :                         peer->response+peer->response_bytes_read,
     206           0 :                         sizeof(peer->response)-peer->response_bytes_read,
     207           0 :                         0,
     208           0 :                         NULL,
     209           0 :                         NULL );
     210           0 :   if( FD_UNLIKELY( -1==read && (errno==EAGAIN || errno==EINTR) ) ) return 1;
     211           0 :   else if( FD_UNLIKELY( -1==read ) ) {
     212           0 :     close_one( client, conn_idx );
     213           0 :     return 1;
     214           0 :   }
     215           0 :   if( FD_UNLIKELY( !read ) ) {
     216           0 :     close_one( client, conn_idx );
     217           0 :     return 1;
     218           0 :   }
     219             : 
     220           0 :   peer->response_bytes_read += (ulong)read;
     221             : 
     222           0 :   int minor_version;
     223           0 :   int status;
     224           0 :   const char * message;
     225           0 :   ulong message_len;
     226           0 :   struct phr_header headers[ 32 ];
     227           0 :   ulong num_headers = 32UL;
     228           0 :   int len = phr_parse_response( (char*)peer->response, peer->response_bytes_read,
     229           0 :                                 &minor_version, &status, &message, &message_len,
     230           0 :                                 headers, &num_headers, 0L );
     231           0 :   if( FD_UNLIKELY( -1==len ) ) {
     232           0 :     close_one( client, conn_idx );
     233           0 :     return 1;
     234           0 :   } else if( FD_UNLIKELY( -2==len ) ) {
     235           0 :     return 1;
     236           0 :   }
     237             : 
     238           0 :   if( FD_UNLIKELY( status!=200 ) ) {
     239           0 :     close_one( client, conn_idx );
     240           0 :     return 1;
     241           0 :   }
     242             : 
     243           0 :   ulong content_length = rpc_phr_content_length( headers, num_headers );
     244           0 :   if( FD_UNLIKELY( content_length==ULONG_MAX ) ) {
     245           0 :     close_one( client, conn_idx );
     246           0 :     return 1;
     247           0 :   }
     248           0 :   if( FD_UNLIKELY( content_length+(ulong)len>sizeof(peer->response) ) ) {
     249           0 :     close_one( client, conn_idx );
     250           0 :     return 1;
     251           0 :   }
     252           0 :   if( FD_LIKELY( content_length+(ulong)len>peer->response_bytes_read ) ) {
     253           0 :     return 1;
     254           0 :   }
     255             : 
     256           0 :   *buffer_sz = content_length;
     257           0 :   *buffer    = peer->response + (ulong)len;
     258             : 
     259           0 :   uchar hash[ 32UL ] = {0};
     260           0 :   fd_sha256_hash( *buffer, *buffer_sz, hash );
     261             : 
     262           0 :   return 0;
     263           0 : }
     264             : 
     265             : int
     266             : fd_genesis_client_poll( fd_genesis_client_t * client,
     267             :                         long                  now,
     268             :                         fd_ip4_port_t *       peer,
     269             :                         uchar **              buffer,
     270             :                         ulong *               buffer_sz,
     271           0 :                         int *                 charge_busy ) {
     272           0 :   if( FD_UNLIKELY( !client->remaining_peer_cnt ) ) return -1;
     273           0 :   if( FD_UNLIKELY( LONG_MAX==client->start_time_nanos ) ) client->start_time_nanos = now;
     274           0 :   if( FD_UNLIKELY( now>fd_genesis_client_deadline_nanos( client ) ) ) {
     275           0 :     close_all( client );
     276           0 :     return -1;
     277           0 :   }
     278             : 
     279           0 :   int nfds = fd_syscall_poll( client->pollfds, (uint)client->peer_cnt, 0 );
     280           0 :   if( FD_UNLIKELY( 0==nfds ) ) return 1;
     281           0 :   else if( FD_UNLIKELY( -1==nfds && errno==EINTR ) ) return 1;
     282           0 :   else if( FD_UNLIKELY( -1==nfds ) ) FD_LOG_ERR(( "poll() failed (%i-%s)", errno, strerror( errno ) ));
     283             : 
     284           0 :   for( ulong i=0UL; i<FD_TOPO_GOSSIP_ENTRYPOINTS_MAX; i++ ) {
     285           0 :     if( FD_UNLIKELY( -1==client->pollfds[ i ].fd ) ) continue;
     286             : 
     287           0 :     if( FD_LIKELY( client->pollfds[ i ].revents & POLLOUT ) ) {
     288           0 :       if( FD_UNLIKELY( client->peers[ i ].writing ) ) *charge_busy = 1;
     289           0 :       write_conn( client, i );
     290           0 :     }
     291           0 :     if( FD_UNLIKELY( -1==client->pollfds[ i ].fd ) ) continue;
     292           0 :     if( FD_LIKELY( client->pollfds[ i ].revents & POLLIN ) ) {
     293           0 :       if( FD_UNLIKELY( !client->peers[ i ].writing ) ) *charge_busy = 1;
     294           0 :       if( FD_LIKELY( !read_conn( client, i, buffer, buffer_sz ) ) ) {
     295           0 :         close_all( client );
     296           0 :         *peer = client->peers[ i ].addr;
     297           0 :         return 0;
     298           0 :       }
     299           0 :     }
     300           0 :   }
     301             : 
     302           0 :   return 1;
     303           0 : }
     304             : 
     305             : struct pollfd const *
     306           0 : fd_genesis_client_get_pollfds( fd_genesis_client_t * client ) {
     307           0 :   return client->pollfds;
     308           0 : }
     309             : 
     310             : long
     311           0 : fd_genesis_client_deadline_nanos( fd_genesis_client_t const * client ) {
     312           0 :   if( FD_UNLIKELY( LONG_MAX==client->start_time_nanos ) ) return LONG_MAX;
     313           0 :   return client->start_time_nanos+20L*1000L*1000L*1000L;
     314           0 : }

Generated by: LCOV version 1.14