LCOV - code coverage report
Current view: top level - discof/rpc - fd_rpc_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 1218 0.0 %
Date: 2026-03-19 18:19:27 Functions: 0 316 0.0 %

          Line data    Source code
       1             : #include "../replay/fd_replay_tile.h"
       2             : #include "../genesis/fd_genesi_tile.h"
       3             : #include "../fd_accdb_topo.h"
       4             : 
       5             : #include "../../ballet/json/cJSON_alloc.h"
       6             : #include "../../ballet/base64/fd_base64.h"
       7             : #include "../../ballet/json/cJSON.h"
       8             : #include "../../disco/topo/fd_topo.h"
       9             : #include "../../disco/keyguard/fd_keyload.h"
      10             : #include "../../disco/keyguard/fd_keyswitch.h"
      11             : #include "../../flamenco/accdb/fd_accdb_sync.h"
      12             : #include "../../flamenco/features/fd_features.h"
      13             : #include "../../flamenco/runtime/sysvar/fd_sysvar_rent.h"
      14             : #include "../../flamenco/runtime/fd_runtime_const.h"
      15             : #include "../../flamenco/gossip/fd_gossip_message.h"
      16             : #include "../../flamenco/runtime/fd_genesis_parse.h"
      17             : #include "../../util/net/fd_ip4.h"
      18             : #include "../../waltz/http/fd_http_server.h"
      19             : #include "../../waltz/http/fd_http_server_private.h"
      20             : 
      21             : #include <stddef.h>
      22             : #include <sys/socket.h>
      23             : #include <math.h> /* floor, isfinite */
      24             : 
      25             : #if FD_HAS_ZSTD
      26             : #include <zstd.h>
      27             : #endif
      28             : 
      29             : #if FD_HAS_BZIP2
      30             : #include "../../util/archive/fd_tar.h"
      31             : #include <bzlib.h>
      32             : #endif
      33             : 
      34             : #include "generated/fd_rpc_tile_seccomp.h"
      35             : 
      36           0 : #define FD_RPC_AGAVE_API_VERSION "3.1.8"
      37             : 
      38           0 : #define FD_HTTP_SERVER_RPC_MAX_REQUEST_LEN 8192UL
      39             : 
      40           0 : #define IN_KIND_REPLAY      (0)
      41           0 : #define IN_KIND_GENESI      (1)
      42           0 : #define IN_KIND_GOSSIP_OUT  (2)
      43             : 
      44             : /* From bzip2 docs:
      45             :       To guarantee that the compressed data will fit in its buffer,
      46             :       allocate an output buffer of size 1% larger than the uncompressed
      47             :       data, plus six hundred extra bytes.
      48             : */
      49             : #define FD_RPC_TAR_SZ (FD_GENESIS_MAX_MESSAGE_SIZE + 4UL*512UL)
      50             : #define FD_RPC_TAR_BZ_SZ (FD_RPC_TAR_SZ + ((FD_RPC_TAR_SZ + 100UL - 1UL) / 100UL) + 600UL)
      51             : 
      52           0 : #define FD_RPC_BASE58_ENCODED_128_LEN (175UL) /* ceil(128*log58(256)) */
      53             : 
      54             : #define FD_RPC_COMMITMENT_PROCESSED (0)
      55             : #define FD_RPC_COMMITMENT_CONFIRMED (1)
      56             : #define FD_RPC_COMMITMENT_FINALIZED (2)
      57             : 
      58             : #define FD_RPC_ENCODING_BASE58      (0)
      59             : #define FD_RPC_ENCODING_BASE64      (1)
      60             : #define FD_RPC_ENCODING_BASE64_ZSTD (2)
      61             : #define FD_RPC_ENCODING_BINARY      (3)
      62             : #define FD_RPC_ENCODING_JSON_PARSED (4)
      63             : 
      64           0 : #define FD_RPC_HEALTH_STATUS_OK      (0)
      65           0 : #define FD_RPC_HEALTH_STATUS_BEHIND  (1)
      66           0 : #define FD_RPC_HEALTH_STATUS_UNKNOWN (2)
      67             : 
      68             : #define FD_RPC_METHOD_GET_ACCOUNT_INFO                       ( 0)
      69             : #define FD_RPC_METHOD_GET_BALANCE                            ( 1)
      70             : #define FD_RPC_METHOD_GET_BLOCK                              ( 2)
      71             : #define FD_RPC_METHOD_GET_BLOCK_COMMITMENT                   ( 3)
      72             : #define FD_RPC_METHOD_GET_BLOCK_HEIGHT                       ( 4)
      73             : #define FD_RPC_METHOD_GET_BLOCK_PRODUCTION                   ( 5)
      74             : #define FD_RPC_METHOD_GET_BLOCKS                             ( 6)
      75             : #define FD_RPC_METHOD_GET_BLOCKS_WITH_LIMIT                  ( 7)
      76             : #define FD_RPC_METHOD_GET_BLOCK_TIME                         ( 8)
      77             : #define FD_RPC_METHOD_GET_CLUSTER_NODES                      ( 9)
      78             : #define FD_RPC_METHOD_GET_EPOCH_INFO                         (10)
      79             : #define FD_RPC_METHOD_GET_EPOCH_SCHEDULE                     (11)
      80             : #define FD_RPC_METHOD_GET_FEE_FOR_MESSAGE                    (12)
      81             : #define FD_RPC_METHOD_GET_FIRST_AVAILABLE_BLOCK              (13)
      82             : #define FD_RPC_METHOD_GET_GENESIS_HASH                       (14)
      83             : #define FD_RPC_METHOD_GET_HEALTH                             (15)
      84             : #define FD_RPC_METHOD_GET_HIGHEST_SNAPSHOT_SLOT              (16)
      85             : #define FD_RPC_METHOD_GET_IDENTITY                           (17)
      86             : #define FD_RPC_METHOD_GET_INFLATION_GOVERNOR                 (18)
      87             : #define FD_RPC_METHOD_GET_INFLATION_RATE                     (19)
      88             : #define FD_RPC_METHOD_GET_INFLATION_REWARD                   (20)
      89             : #define FD_RPC_METHOD_GET_LARGEST_ACCOUNTS                   (21)
      90             : #define FD_RPC_METHOD_GET_LATEST_BLOCKHASH                   (22)
      91             : #define FD_RPC_METHOD_GET_LEADER_SCHEDULE                    (23)
      92             : #define FD_RPC_METHOD_GET_MAX_RETRANSMIT_SLOT                (24)
      93             : #define FD_RPC_METHOD_GET_MAX_SHRED_INSERT_SLOT              (25)
      94             : #define FD_RPC_METHOD_GET_MINIMUM_BALANCE_FOR_RENT_EXEMPTION (26)
      95             : #define FD_RPC_METHOD_GET_MULTIPLE_ACCOUNTS                  (27)
      96             : #define FD_RPC_METHOD_GET_PROGRAM_ACCOUNTS                   (28)
      97             : #define FD_RPC_METHOD_GET_RECENT_PERFORMANCE_SAMPLES         (29)
      98             : #define FD_RPC_METHOD_GET_RECENT_PRIORITIZATION_FEES         (30)
      99             : #define FD_RPC_METHOD_GET_SIGNATURES_FOR_ADDRESS             (31)
     100             : #define FD_RPC_METHOD_GET_SIGNATURE_STATUSES                 (32)
     101             : #define FD_RPC_METHOD_GET_SLOT                               (33)
     102             : #define FD_RPC_METHOD_GET_SLOT_LEADER                        (34)
     103             : #define FD_RPC_METHOD_GET_SLOT_LEADERS                       (35)
     104             : #define FD_RPC_METHOD_GET_STAKE_MINIMUM_DELEGATION           (36)
     105             : #define FD_RPC_METHOD_GET_SUPPLY                             (37)
     106             : #define FD_RPC_METHOD_GET_TOKEN_ACCOUNT_BALANCE              (38)
     107             : #define FD_RPC_METHOD_GET_TOKEN_ACCOUNTS_BY_DELEGATE         (39)
     108             : #define FD_RPC_METHOD_GET_TOKEN_ACCOUNTS_BY_OWNER            (40)
     109             : #define FD_RPC_METHOD_GET_TOKEN_LARGEST_ACCOUNTS             (41)
     110             : #define FD_RPC_METHOD_GET_TOKEN_SUPPLY                       (42)
     111             : #define FD_RPC_METHOD_GET_TRANSACTION                        (43)
     112             : #define FD_RPC_METHOD_GET_TRANSACTION_COUNT                  (44)
     113             : #define FD_RPC_METHOD_GET_VERSION                            (45)
     114             : #define FD_RPC_METHOD_GET_VOTE_ACCOUNTS                      (46)
     115             : #define FD_RPC_METHOD_IS_BLOCKHASH_VALID                     (47)
     116             : #define FD_RPC_METHOD_MINIMUM_LEDGER_SLOT                    (48)
     117             : #define FD_RPC_METHOD_REQUEST_AIRDROP                        (49)
     118             : #define FD_RPC_METHOD_SEND_TRANSACTION                       (50)
     119             : #define FD_RPC_METHOD_SIMULATE_TRANSACTION                   (51)
     120             : 
     121             : // Keep in sync with https://github.com/solana-labs/solana-web3.js/blob/master/src/errors.ts
     122             : // and https://github.com/anza-xyz/agave/blob/master/rpc-client-api/src/custom_error.rs
     123             : #define FD_RPC_ERROR_BLOCK_CLEANED_UP                            (-32001)
     124             : #define FD_RPC_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE          (-32002)
     125             : #define FD_RPC_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE  (-32003)
     126             : #define FD_RPC_ERROR_BLOCK_NOT_AVAILABLE                         (-32004)
     127             : #define FD_RPC_ERROR_NODE_UNHEALTHY                              (-32005)
     128             : #define FD_RPC_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE (-32006)
     129             : #define FD_RPC_ERROR_SLOT_SKIPPED                                (-32007)
     130             : #define FD_RPC_ERROR_NO_SNAPSHOT                                 (-32008)
     131             : #define FD_RPC_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED              (-32009)
     132             : #define FD_RPC_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX           (-32010)
     133             : #define FD_RPC_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE           (-32011)
     134             : #define FD_RPC_ROR                                               (-32012)
     135             : #define FD_RPC_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH          (-32013)
     136             : #define FD_RPC_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET              (-32014)
     137             : #define FD_RPC_ERROR_UNSUPPORTED_TRANSACTION_VERSION             (-32015)
     138             : #define FD_RPC_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED                (-32016)
     139             : #define FD_RPC_ERROR_EPOCH_REWARDS_PERIOD_ACTIVE                 (-32017)
     140             : #define FD_RPC_ERROR_SLOT_NOT_EPOCH_BOUNDARY                     (-32018)
     141             : #define FD_RPC_ERROR_LONG_TERM_STORAGE_UNREACHABLE               (-32019)
     142             : 
     143           0 : static void fd_rpc_cstr_cJSON_free( char ** p ) { cJSON_free( *p ); }
     144           0 : #define CSTR_JSON(__json, __out) __attribute__((cleanup(fd_rpc_cstr_cJSON_free))) char * __out = cJSON_PrintUnformatted( __json );
     145             : 
     146             : static fd_http_server_params_t
     147           0 : derive_http_params( fd_topo_tile_t const * tile ) {
     148           0 :   return (fd_http_server_params_t) {
     149           0 :     .max_connection_cnt    = tile->rpc.max_http_connections,
     150           0 :     .max_ws_connection_cnt = 0UL,
     151           0 :     .max_request_len       = FD_HTTP_SERVER_RPC_MAX_REQUEST_LEN,
     152           0 :     .max_ws_recv_frame_len = 0UL,
     153           0 :     .max_ws_send_frame_cnt = 0UL,
     154           0 :     .outgoing_buffer_sz    = tile->rpc.send_buffer_size_mb * (1UL<<20UL),
     155           0 :     .compress_websocket    = 0,
     156           0 :   };
     157           0 : }
     158             : 
     159             : struct fd_rpc_in {
     160             :   fd_wksp_t * mem;
     161             :   ulong       chunk0;
     162             :   ulong       wmark;
     163             :   ulong       mtu;
     164             : };
     165             : 
     166             : typedef struct fd_rpc_in fd_rpc_in_t;
     167             : 
     168             : struct fd_rpc_out {
     169             :   ulong       idx;
     170             :   fd_wksp_t * mem;
     171             :   ulong       chunk0;
     172             :   ulong       wmark;
     173             :   ulong       chunk;
     174             : };
     175             : 
     176             : typedef struct fd_rpc_out fd_rpc_out_t;
     177             : 
     178             : struct bank_info {
     179             :   ulong slot; /* default ULONG_MAX */
     180             :   ulong bank_idx;
     181             :   ulong epoch;
     182             :   ulong slot_in_epoch;
     183             :   ulong slots_per_epoch;
     184             : 
     185             :   ulong transaction_count;
     186             :   uchar block_hash[ 32 ];
     187             :   ulong block_height;
     188             : 
     189             :   struct {
     190             :     double initial;
     191             :     double terminal;
     192             :     double taper;
     193             :     double foundation;
     194             :     double foundation_term;
     195             :   } inflation;
     196             : 
     197             :   struct {
     198             :     ulong lamports_per_uint8_year;
     199             :     double exemption_threshold;
     200             :     uchar burn_percent;
     201             :   } rent;
     202             : };
     203             : 
     204             : typedef struct bank_info bank_info_t;
     205             : 
     206             : struct fd_rpc_cluster_node {
     207             :   int valid;
     208             :   fd_pubkey_t identity;
     209             :   fd_gossip_contact_info_t ci[ 1 ];
     210             : 
     211             :   struct { ulong prev, next; } dlist;
     212             : };
     213             : 
     214             : typedef struct fd_rpc_cluster_node fd_rpc_cluster_node_t;
     215             : 
     216             : #define DLIST_NAME  fd_rpc_cluster_node_dlist
     217             : #define DLIST_ELE_T fd_rpc_cluster_node_t
     218           0 : #define DLIST_PREV dlist.prev
     219           0 : #define DLIST_NEXT dlist.next
     220             : #include "../../util/tmpl/fd_dlist.c"
     221             : 
     222             : struct fd_rpc_tile {
     223             :   int delay_startup;
     224             :   fd_http_server_t * http;
     225             : 
     226             :   fd_rpc_cluster_node_dlist_t * cluster_nodes_dlist;
     227             :   fd_rpc_cluster_node_t cluster_nodes[ FD_CONTACT_INFO_TABLE_SIZE ];
     228             : 
     229             :   bank_info_t * banks;
     230             :   ulong         max_live_slots;
     231             : 
     232             :   ulong cluster_confirmed_slot;
     233             : 
     234             :   ulong processed_idx;
     235             :   ulong confirmed_idx;
     236             :   ulong finalized_idx;
     237             : 
     238             :   int has_genesis_hash;
     239             :   fd_hash_t genesis_hash[ 1 ];
     240             : 
     241             :   uchar genesis_tar[ FD_RPC_TAR_SZ ];
     242             :   uchar genesis_tar_bz[ FD_RPC_TAR_BZ_SZ ];
     243             :   ulong genesis_tar_bz_sz;
     244             : 
     245             : # if FD_HAS_BZIP2
     246             :   fd_alloc_t * bz2_alloc;
     247             : # endif
     248             : 
     249             :   long next_poll_deadline;
     250             : 
     251             :   char version_string[ 16UL ];
     252             : 
     253             :   fd_keyswitch_t * keyswitch;
     254             :   uchar identity_pubkey[ 32UL ];
     255             : 
     256             :   int in_kind[ 64UL ];
     257             :   fd_rpc_in_t in[ 64UL ];
     258             : 
     259             :   fd_rpc_out_t replay_out[1];
     260             : 
     261             :   fd_accdb_user_t accdb[1];
     262             : 
     263             : # if FD_HAS_ZSTD
     264             :   uchar compress_buf[ ZSTD_COMPRESSBOUND( FD_RUNTIME_ACC_SZ_MAX ) ];
     265             : # endif
     266             : };
     267             : 
     268             : typedef struct fd_rpc_tile fd_rpc_tile_t;
     269             : 
     270             : # if FD_HAS_BZIP2
     271             : static void *
     272             : bz2_malloc( void * opaque,
     273             :             int    items,
     274           0 :             int    size ) {
     275           0 :   fd_alloc_t * alloc = (fd_alloc_t *)opaque;
     276             : 
     277           0 :   void * result = fd_alloc_malloc( alloc, alignof(max_align_t), (ulong)(items*size) );
     278           0 :   if( FD_UNLIKELY( !result ) ) return NULL;
     279           0 :   return result;
     280           0 : }
     281             : 
     282             : static void
     283             : bz2_free( void * opaque,
     284           0 :           void * addr ) {
     285           0 :   fd_alloc_t * alloc = (fd_alloc_t *)opaque;
     286             : 
     287           0 :   if( FD_UNLIKELY( !addr ) ) return;
     288           0 :   fd_alloc_free( alloc, addr );
     289           0 : }
     290             : 
     291             : static inline ulong
     292             : fd_rpc_file_as_tarball( fd_rpc_tile_t * ctx,
     293             :                         char const *    filename_cstr,
     294             :                         uchar const *   data,
     295             :                         ulong           data_sz,
     296             :                         uchar *         scratch,
     297             :                         ulong           scratch_sz,
     298             :                         uchar *         out,
     299           0 :                         ulong           out_sz ) {
     300           0 :   ulong padding_sz = 2*512UL;
     301           0 :   if( FD_LIKELY( data_sz % 512UL ) ) padding_sz += 512UL - (data_sz % 512UL);
     302           0 :   FD_TEST( sizeof(fd_tar_meta_t)+data_sz+padding_sz <= scratch_sz );
     303             : 
     304           0 :   fd_tar_meta_init_file_default( (fd_tar_meta_t *)scratch, filename_cstr, data_sz, fd_log_wallclock() );
     305           0 :   fd_memcpy( scratch+sizeof(fd_tar_meta_t), data, data_sz );
     306           0 :   memset( scratch+sizeof(fd_tar_meta_t)+data_sz, 0, padding_sz );
     307             : 
     308             :   /* NOTE: Agave's genesis.tar also contains a `rocksdb` folder */
     309             : 
     310           0 :   ulong tar_sz = sizeof(fd_tar_meta_t)+data_sz+padding_sz;
     311           0 :   FD_TEST( tar_sz<=scratch_sz );
     312             : 
     313           0 :   bz_stream bzstrm = {0};
     314           0 :   bzstrm.bzalloc = bz2_malloc;
     315           0 :   bzstrm.bzfree  = bz2_free;
     316           0 :   bzstrm.opaque  = ctx->bz2_alloc;
     317           0 :   int bzerr = BZ2_bzCompressInit( &bzstrm, 1, 0, 0 );
     318           0 :   if( FD_UNLIKELY( BZ_OK!=bzerr ) ) FD_LOG_ERR(( "BZ2_bzCompressInit() failed (%d)", bzerr ));
     319             : 
     320           0 :   ulong tar_bz_sz = out_sz;
     321             : 
     322           0 :   bzstrm.next_in   = (char *)scratch;
     323           0 :   bzstrm.avail_in  = (uint)tar_sz;
     324           0 :   bzstrm.next_out  = (char *)out;
     325           0 :   bzstrm.avail_out = (uint)tar_bz_sz;
     326             : 
     327           0 :   for(;;) {
     328           0 :     bzerr = BZ2_bzCompress( &bzstrm, BZ_FINISH );
     329           0 :     if( FD_LIKELY( bzerr==BZ_STREAM_END ) ) break;
     330           0 :     if( FD_UNLIKELY( bzerr>=0 ) ) continue;
     331           0 :     FD_LOG_ERR(( "BZ2_bzCompress(_, BZ_FINISH) failed (%d)", bzerr ));
     332           0 :   }
     333             : 
     334           0 :   tar_bz_sz -= (ulong)bzstrm.avail_out;
     335             : 
     336           0 :   bzerr = BZ2_bzCompressEnd( &bzstrm );
     337           0 :   if( FD_UNLIKELY( BZ_OK!=bzerr ) ) FD_LOG_ERR(( "BZ2_bzCompressEnd() failed (%d)", bzerr ));
     338             : 
     339           0 :   return tar_bz_sz;
     340           0 : }
     341             : # endif
     342             : 
     343             : FD_FN_CONST static inline ulong
     344           0 : scratch_align( void ) {
     345           0 :   ulong a = alignof( fd_rpc_tile_t );
     346           0 :   a = fd_ulong_max( a, fd_http_server_align() );
     347           0 :   a = fd_ulong_max( a, fd_alloc_align() );
     348           0 :   a = fd_ulong_max( a, alignof(bank_info_t) );
     349           0 :   a = fd_ulong_max( a, fd_rpc_cluster_node_dlist_align() );
     350           0 :   return a;
     351           0 : }
     352             : 
     353             : FD_FN_PURE static inline ulong
     354           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
     355           0 :   ulong http_fp = fd_http_server_footprint( derive_http_params( tile ) );
     356           0 :   if( FD_UNLIKELY( !http_fp ) ) FD_LOG_ERR(( "Invalid [tiles.rpc] config parameters" ));
     357             : 
     358           0 :   ulong l = FD_LAYOUT_INIT;
     359           0 :   l = FD_LAYOUT_APPEND( l, alignof( fd_rpc_tile_t ), sizeof( fd_rpc_tile_t )                      );
     360           0 :   l = FD_LAYOUT_APPEND( l, fd_http_server_align(),   http_fp                                      );
     361           0 :   l = FD_LAYOUT_APPEND( l, fd_alloc_align(),         fd_alloc_footprint()                         );
     362           0 : #if FD_HAS_BZIP2
     363           0 :   l = FD_LAYOUT_APPEND( l, fd_alloc_align(),         fd_alloc_footprint()                         );
     364           0 : #endif
     365           0 :   l = FD_LAYOUT_APPEND( l, alignof(bank_info_t),     tile->rpc.max_live_slots*sizeof(bank_info_t) );
     366           0 :   l = FD_LAYOUT_APPEND( l, fd_rpc_cluster_node_dlist_align(), fd_rpc_cluster_node_dlist_footprint() );
     367           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
     368           0 : }
     369             : 
     370             : FD_FN_PURE static inline ulong
     371           0 : loose_footprint( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) {
     372           0 :   return 256UL * (1UL<<20UL); /* 256MiB of heap space for the cJSON allocator */
     373           0 : }
     374             : 
     375             : static inline void
     376           0 : during_housekeeping( fd_rpc_tile_t * ctx ) {
     377           0 :   if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->keyswitch )==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
     378           0 :     fd_memcpy( ctx->identity_pubkey, ctx->keyswitch->bytes, 32UL );
     379           0 :     fd_keyswitch_state( ctx->keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
     380           0 :   }
     381           0 : }
     382             : 
     383             : static void
     384             : before_credit( fd_rpc_tile_t *     ctx,
     385             :                fd_stem_context_t * stem,
     386           0 :                int *               charge_busy ) {
     387           0 :   (void)stem;
     388             : 
     389           0 :   long now = fd_tickcount();
     390           0 :   int replay_ready = ctx->confirmed_idx!=ULONG_MAX && ctx->processed_idx!=ULONG_MAX && ctx->finalized_idx!=ULONG_MAX;
     391           0 :   if( FD_UNLIKELY( (!ctx->delay_startup || replay_ready) && now>=ctx->next_poll_deadline ) ) {
     392           0 :     *charge_busy = fd_http_server_poll( ctx->http, 0 );
     393           0 :     ctx->next_poll_deadline = fd_tickcount() + (long)(fd_tempo_tick_per_ns( NULL )*128L*1000L);
     394           0 :   }
     395           0 : }
     396             : 
     397             : static int
     398             : before_frag( fd_rpc_tile_t *   ctx,
     399             :              ulong             in_idx,
     400             :              ulong             seq FD_PARAM_UNUSED,
     401           0 :              ulong             sig ) {
     402           0 :   if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_GOSSIP_OUT ) ) {
     403           0 :     return sig!=FD_GOSSIP_UPDATE_TAG_CONTACT_INFO &&
     404           0 :            sig!=FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE;
     405           0 :   }
     406             : 
     407           0 :   return 0;
     408           0 : }
     409             : 
     410             : static inline int
     411             : returnable_frag( fd_rpc_tile_t *     ctx,
     412             :                  ulong               in_idx,
     413             :                  ulong               seq FD_PARAM_UNUSED,
     414             :                  ulong               sig,
     415             :                  ulong               chunk,
     416             :                  ulong               sz FD_PARAM_UNUSED,
     417             :                  ulong               ctl FD_PARAM_UNUSED,
     418             :                  ulong               tsorig FD_PARAM_UNUSED,
     419             :                  ulong               tspub FD_PARAM_UNUSED,
     420           0 :                  fd_stem_context_t * stem ) {
     421             : 
     422           0 :   if( ctx->in_kind[ in_idx ]==IN_KIND_REPLAY ) {
     423           0 :     switch( sig ) {
     424           0 :       case REPLAY_SIG_SLOT_COMPLETED: {
     425           0 :         fd_replay_slot_completed_t const * slot_completed = fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk );
     426             : 
     427           0 :         bank_info_t * bank = &ctx->banks[ slot_completed->bank_idx ];
     428           0 :         bank->slot = slot_completed->slot;
     429           0 :         bank->epoch = slot_completed->epoch;
     430           0 :         bank->slot_in_epoch = slot_completed->slot_in_epoch;
     431           0 :         bank->slots_per_epoch = slot_completed->slots_per_epoch;
     432           0 :         bank->transaction_count = slot_completed->transaction_count;
     433           0 :         bank->block_height = slot_completed->block_height;
     434           0 :         fd_memcpy( bank->block_hash, slot_completed->block_hash.uc, 32 );
     435             : 
     436           0 :         bank->inflation.initial         = slot_completed->inflation.initial;
     437           0 :         bank->inflation.terminal        = slot_completed->inflation.terminal;
     438           0 :         bank->inflation.taper           = slot_completed->inflation.taper;
     439           0 :         bank->inflation.foundation      = slot_completed->inflation.foundation;
     440           0 :         bank->inflation.foundation_term = slot_completed->inflation.foundation_term;
     441             : 
     442           0 :         bank->rent.lamports_per_uint8_year = slot_completed->rent.lamports_per_uint8_year;
     443           0 :         bank->rent.exemption_threshold     = slot_completed->rent.exemption_threshold;
     444           0 :         bank->rent.burn_percent            = slot_completed->rent.burn_percent;
     445             : 
     446             :         /* In Agave, "processed" confirmation is the bank we've just
     447             :            voted for (handle_votable_bank), which is also guaranteed to
     448             :            have been replayed.
     449             : 
     450             :            Right now tower is not really built out to replicate this
     451             :            exactly, so we use the latest replayed slot, which is
     452             :            slightly more eager than Agave but shouldn't really affect
     453             :            end-users, since any use-cases that assume "processed" means
     454             :            "voted-for" would fail in Agave in cases where a cast vote
     455             :            does not land.
     456             : 
     457             :            tldr: This isn't strictly conformant with Agave, but doesn't
     458             :            need to be since Agave doesn't provide any guarantees anyways. */
     459           0 :         if( FD_LIKELY( ctx->processed_idx!=ULONG_MAX ) ) fd_stem_publish( stem, ctx->replay_out->idx, ctx->processed_idx, 0UL, 0UL, 0UL, 0UL, 0UL );
     460           0 :         ctx->processed_idx = slot_completed->bank_idx;
     461           0 :         break;
     462           0 :       }
     463           0 :       case REPLAY_SIG_OC_ADVANCED: {
     464           0 :         fd_replay_oc_advanced_t const * msg = fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk );
     465           0 :         if( FD_LIKELY( ctx->confirmed_idx!=ULONG_MAX ) ) fd_stem_publish( stem, ctx->replay_out->idx, ctx->confirmed_idx, 0UL, 0UL, 0UL, 0UL, 0UL );
     466           0 :         ctx->confirmed_idx = msg->bank_idx;
     467           0 :         ctx->cluster_confirmed_slot = msg->slot;
     468           0 :         break;
     469           0 :       }
     470           0 :       case REPLAY_SIG_ROOT_ADVANCED: {
     471           0 :         fd_replay_root_advanced_t const * msg = fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk );
     472           0 :         if( FD_LIKELY( ctx->finalized_idx!=ULONG_MAX ) ) fd_stem_publish( stem, ctx->replay_out->idx, ctx->finalized_idx, 0UL, 0UL, 0UL, 0UL, 0UL );
     473           0 :         ctx->finalized_idx = msg->bank_idx;
     474           0 :         break;
     475           0 :       }
     476           0 :       default: {
     477           0 :         break;
     478           0 :       }
     479           0 :     }
     480           0 :   } else if( ctx->in_kind[ in_idx ]==IN_KIND_GOSSIP_OUT ) {
     481           0 :     fd_gossip_update_message_t const * update = fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk );
     482           0 :     switch( update->tag ) {
     483           0 :       case FD_GOSSIP_UPDATE_TAG_CONTACT_INFO: {
     484           0 :         if( FD_UNLIKELY( update->contact_info->idx>=FD_CONTACT_INFO_TABLE_SIZE ) ) FD_LOG_ERR(( "unexpected contact_info_idx %lu >= %lu", update->contact_info->idx, FD_CONTACT_INFO_TABLE_SIZE ));
     485           0 :         fd_rpc_cluster_node_t * node = &ctx->cluster_nodes[ update->contact_info->idx ];
     486           0 :         if( FD_LIKELY( node->valid ) ) fd_rpc_cluster_node_dlist_idx_remove( ctx->cluster_nodes_dlist, update->contact_info->idx, ctx->cluster_nodes );
     487             : 
     488           0 :         node->valid = 1;
     489           0 :         node->identity = *(fd_pubkey_t *)update->origin;
     490           0 :         fd_memcpy( node->ci, update->contact_info->value, sizeof(fd_gossip_contact_info_t) );
     491             : 
     492           0 :         fd_rpc_cluster_node_dlist_idx_push_tail( ctx->cluster_nodes_dlist, update->contact_info->idx, ctx->cluster_nodes );
     493           0 :         break;
     494           0 :       }
     495           0 :       case FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE: {
     496           0 :         if( FD_UNLIKELY( update->contact_info_remove->idx>=FD_CONTACT_INFO_TABLE_SIZE ) ) FD_LOG_ERR(( "unexpected remove_contact_info_idx %lu >= %lu", update->contact_info_remove->idx, FD_CONTACT_INFO_TABLE_SIZE ));
     497           0 :         fd_rpc_cluster_node_t * node = &ctx->cluster_nodes[ update->contact_info_remove->idx ];
     498           0 :         FD_TEST( node->valid );
     499           0 :         node->valid = 0;
     500           0 :         fd_rpc_cluster_node_dlist_idx_remove( ctx->cluster_nodes_dlist, update->contact_info_remove->idx, ctx->cluster_nodes );
     501           0 :         break;
     502           0 :       }
     503           0 :       default: break;
     504           0 :     }
     505           0 :   } else if( ctx->in_kind[ in_idx ]==IN_KIND_GENESI ) {
     506           0 :     ctx->has_genesis_hash = 1;
     507           0 :     fd_genesis_meta_t const * genesis_meta = fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk );
     508           0 :     *ctx->genesis_hash = genesis_meta->genesis_hash;
     509             : 
     510           0 : #   if FD_HAS_BZIP2
     511           0 :     uchar const * blob    = (uchar const *)( genesis_meta+1 );
     512           0 :     ulong const   blob_sz = genesis_meta->blob_sz;
     513           0 :     FD_TEST( blob_sz<=FD_GENESIS_MAX_MESSAGE_SIZE );
     514             : 
     515           0 :     ctx->genesis_tar_bz_sz = fd_rpc_file_as_tarball(
     516           0 :       ctx,
     517           0 :       "genesis.bin",
     518           0 :       blob, blob_sz,
     519           0 :       ctx->genesis_tar, sizeof(ctx->genesis_tar),
     520           0 :       ctx->genesis_tar_bz, sizeof(ctx->genesis_tar_bz) );
     521           0 : #   endif
     522           0 :   }
     523             : 
     524           0 :   return 0;
     525           0 : }
     526             : 
     527             : /* Silence warnings due gcc not recognizing nan-infinity-disabled
     528             :    pragma, which is required by clang */
     529             : #pragma GCC diagnostic push
     530             : #pragma GCC diagnostic ignored "-Wpragmas"
     531             : #pragma GCC diagnostic ignored "-Wunknown-warning-option"
     532             : #pragma GCC diagnostic ignored "-Wnan-infinity-disabled"
     533             : 
     534             : static inline int
     535           0 : fd_rpc_cjson_is_integer( const cJSON * item ) {
     536           0 :   return cJSON_IsNumber(item)
     537           0 :       && isfinite(item->valuedouble)
     538           0 :       && floor(item->valuedouble) == item->valuedouble;
     539           0 : }
     540             : 
     541             : #pragma GCC diagnostic pop
     542             : 
     543             : static inline char const *
     544           0 : fd_rpc_cjson_type_to_cstr( cJSON const * elt ) {
     545           0 :   FD_TEST( elt );
     546           0 :   if( cJSON_IsString( elt ) ) return "string";
     547           0 :   if( cJSON_IsObject( elt ) ) return "map";
     548           0 :   if( cJSON_IsArray ( elt ) ) return "sequence";
     549           0 :   if( cJSON_IsBool  ( elt ) ) return "boolean";
     550           0 :   if( cJSON_IsNumber( elt ) && !fd_rpc_cjson_is_integer( elt ) ) return "floating point";
     551           0 :   if( cJSON_IsNumber( elt ) ) return "integer";
     552           0 :   if( cJSON_IsNull  ( elt ) ) return "null";
     553           0 :   CSTR_JSON( elt, elt_cstr );
     554           0 :   FD_LOG_ERR(( "unreachable %s", elt_cstr ));
     555           0 : }
     556             : 
     557           0 : #define STAGE_JSON(__ctx) (__extension__({ \
     558           0 :   fd_http_server_response_t __res = (fd_http_server_response_t){ .content_type = "application/json", .status = 200 }; \
     559           0 :   if( FD_UNLIKELY( fd_http_server_stage_body( __ctx->http, &__res ) ) ) { \
     560           0 :     __res.status = 500; \
     561           0 :     FD_LOG_WARNING(( "Failed to populate RPC response buffer" )); \
     562           0 :     FD_LOG_HEXDUMP_WARNING(( "start of message:\n%.*s", __ctx->http->oring+(__ctx->http->stage_off%__ctx->http->oring_sz), fd_ulong_min( 500UL, __ctx->http->oring_sz-(__ctx->http->stage_off%__ctx->http->oring_sz)-1UL ) )); \
     563           0 :     FD_LOG_HEXDUMP_WARNING(( "start of buffer:\n%.*s",  __ctx->http->oring,                                                fd_ulong_min( 500UL, __ctx->http->oring_sz ) )); \
     564           0 :   } \
     565           0 :   __res; }))
     566             : 
     567           0 : #define PRINTF_JSON(__ctx, ...) (__extension__({ \
     568           0 :   fd_http_server_printf( __ctx->http, __VA_ARGS__ ); \
     569           0 :   fd_http_server_response_t __res = STAGE_JSON( __ctx ); \
     570           0 :   __res; }))
     571             : 
     572             : 
     573             : static inline int
     574             : fd_rpc_validate_params( fd_rpc_tile_t *             ctx,
     575             :                         cJSON const *               id,
     576             :                         cJSON const *               params,
     577             :                         ulong                       min_cnt,
     578             :                         ulong                       max_cnt,
     579           0 :                         fd_http_server_response_t * res ) {
     580           0 :   FD_TEST( min_cnt <= max_cnt );
     581             :   /* Agave also includes a "data" field in some responses with the
     582             :      faulty params payload. Instead of printing raw JSON, they print the
     583             :      representation which we won't replicate.
     584             : 
     585             :      e.g. "data" might contain something like
     586             :       Array([String(\"\"), Object {}])
     587             : 
     588             :     instead, we just include the field with an empty string
     589             :   */
     590             : 
     591           0 :   ulong param_cnt;
     592           0 :   if( FD_UNLIKELY( !params ) ) param_cnt = 0UL;
     593           0 :   else if( FD_UNLIKELY( cJSON_IsNumber( params ) || cJSON_IsString( params ) || cJSON_IsBool( params ) ) ) {
     594           0 :     CSTR_JSON( id, id_cstr );
     595           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":%s}\n", id_cstr );
     596           0 :     return 0;
     597           0 :   }
     598           0 :   else if( FD_UNLIKELY( cJSON_IsObject( params ) && max_cnt==0UL ) ) {
     599           0 :     CSTR_JSON( id, id_cstr );
     600           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid parameters: No parameters were expected\",\"data\":\"\"},\"id\":%s}\n", id_cstr );
     601           0 :     return 0;
     602           0 :   }
     603           0 :   else if( FD_UNLIKELY( cJSON_IsObject( params ) && max_cnt>0UL ) ) {
     604           0 :     CSTR_JSON( id, id_cstr );
     605           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"`params` should be an array\"},\"id\":%s}\n", id_cstr );
     606           0 :     return 0;
     607           0 :   }
     608           0 :   else if( FD_UNLIKELY( cJSON_IsNull( params ) ) ) param_cnt = 0UL;
     609           0 :   else if( FD_UNLIKELY( cJSON_IsArray( params ) ) ) param_cnt = (ulong)cJSON_GetArraySize( params );
     610           0 :   else FD_LOG_ERR(("unreachable"));
     611             : 
     612           0 :   if( FD_UNLIKELY( param_cnt>0UL && max_cnt==0UL ) ) {
     613           0 :     CSTR_JSON( id, id_cstr );
     614           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid parameters: No parameters were expected\",\"data\":\"\"},\"id\":%s}\n", id_cstr );
     615           0 :     return 0;
     616           0 :   }
     617           0 :   if( FD_UNLIKELY( param_cnt<min_cnt ) ) {
     618           0 :     CSTR_JSON( id, id_cstr );
     619           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"`params` should have at least %lu argument(s)\"},\"id\":%s}\n", min_cnt, id_cstr );
     620           0 :     return 0;
     621           0 :   }
     622           0 :   if( param_cnt>max_cnt ) {
     623           0 :     CSTR_JSON( id, id_cstr );
     624           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid parameters: Expected from %lu to %lu parameters.\",\"data\":\"\\\"Got: %lu\\\"\"},\"id\":%s}\n", min_cnt, max_cnt, param_cnt, id_cstr );
     625           0 :     return 0;
     626           0 :   }
     627             : 
     628           0 :   return 1;
     629           0 : }
     630             : 
     631             : /* TODO: use optimized version of this from fd_base58_tmpl.c */
     632             : static const char base58_chars[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
     633             : 
     634             : static inline int
     635           0 : fd_rpc_cstr_contains_non_base58(const char *str) {
     636           0 :     for (; *str; str++) {
     637           0 :         if (!strchr(base58_chars, *str)) return 1;
     638           0 :     }
     639           0 :     return 0;
     640           0 : }
     641             : 
     642             : /* adapted from https://salsa.debian.org/debian/libbase58/-/blob/debian/master/base58.c */
     643             : static inline int
     644           0 : fd_rpc_base58_encode_128( char * b58, ulong * b58sz, const void *data, ulong binsz ) {
     645           0 :   FD_TEST( binsz <= 128UL );
     646             : 
     647           0 :   const uchar * bin = data;
     648           0 :   ulong carry;
     649           0 :   ulong i, j, high, zcount = 0;
     650           0 :   ulong size;
     651             : 
     652           0 :   while( zcount<binsz && !bin[ zcount ] ) zcount++;
     653             : 
     654           0 :   size = (binsz-zcount)*138/100+1; /* strict overestimate */
     655           0 :   size = fd_ulong_min( size, FD_RPC_BASE58_ENCODED_128_LEN ); /* theoretical max */
     656           0 :   uchar buf[ FD_RPC_BASE58_ENCODED_128_LEN ] = { 0 };
     657             : 
     658           0 :   for( i=zcount, high=size-1UL; i<binsz; i++, high=j ) {
     659           0 :     for( carry=bin[ i ], j=size-1UL; (j>high) || carry; j-- ) {
     660           0 :       carry += 256UL * buf[ j ];
     661           0 :       buf[ j ] = (uchar)(carry%58UL);
     662           0 :       carry /= 58UL;
     663           0 :       if( FD_UNLIKELY( !j ) ) break;
     664           0 :     }
     665           0 :   }
     666             : 
     667           0 :   for( j=0; j<size && !buf[ j ]; j++);
     668             : 
     669           0 :   if( *b58sz<zcount+size-j ) {
     670           0 :     *b58sz = zcount+size-j;
     671           0 :     return 0;
     672           0 :   }
     673             : 
     674           0 :   if (zcount) memset(b58, '1', zcount);
     675           0 :   for( i=zcount; j<size; i++, j++) b58[ i ] = base58_chars[ buf[ j ] ];
     676           0 :   *b58sz = i;
     677             : 
     678           0 :   return 1;
     679           0 : }
     680             : 
     681             : static inline int
     682             : fd_rpc_validate_config( fd_rpc_tile_t *             ctx,
     683             :                         cJSON const *               id,
     684             :                         cJSON const *               config,
     685             :                         char const *                config_rust_type,
     686             :                         int                         has_commitment,
     687             :                         int                         has_encoding,
     688             :                         int                         has_data_slice,
     689             :                         int                         has_min_context_slot,
     690             :                         ulong *                     bank_idx,
     691             :                         char const **               opt_encoding_cstr,
     692             :                         ulong *                     opt_slice_length,
     693             :                         ulong *                     opt_slice_offset,
     694           0 :                         fd_http_server_response_t * res ) {
     695             : 
     696           0 :   if( FD_UNLIKELY( config && (cJSON_IsNumber( config ) || cJSON_IsBool( config )) ) ) {
     697           0 :     CSTR_JSON( id, id_cstr ); CSTR_JSON( config, config_cstr );
     698           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s `%s`, expected %s.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( config ), config_cstr, config_rust_type, id_cstr );
     699           0 :     return 0;
     700           0 :   }
     701           0 :   if( FD_UNLIKELY( config && cJSON_IsString( config ) ) ) {
     702           0 :     CSTR_JSON( id, id_cstr );
     703           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s \\\"%s\\\", expected %s.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( config ), config->valuestring, config_rust_type, id_cstr );
     704           0 :     return 0;
     705           0 :   }
     706           0 :   if( FD_UNLIKELY( cJSON_IsArray( config ) ) ) {
     707           0 :     CSTR_JSON( id, id_cstr );
     708           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32065,\"message\":\"Firedancer Error: Positional config params not supported\"},\"id\":%s}\n", id_cstr );
     709           0 :     return 0;
     710           0 :   }
     711           0 :   if( FD_UNLIKELY( config && !(cJSON_IsNull( config ) || cJSON_IsObject( config )) ) ) {
     712           0 :     CSTR_JSON( id, id_cstr );
     713           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s, expected %s.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( config ), config_rust_type, id_cstr );
     714           0 :     return 0;
     715           0 :   }
     716             : 
     717           0 :   ulong _bank_idx = ULONG_MAX;
     718           0 :   cJSON const * commitment = NULL;
     719           0 :   if( FD_LIKELY( has_commitment ) ) {
     720           0 :     commitment = cJSON_GetObjectItemCaseSensitive( config, "commitment" );
     721           0 :     if( FD_UNLIKELY( !commitment || !cJSON_IsString( commitment ) ) ) _bank_idx = ctx->finalized_idx;
     722           0 :     else if( FD_LIKELY( !strcmp( commitment->valuestring, "processed" ) ) ) _bank_idx = ctx->processed_idx;
     723           0 :     else if( FD_LIKELY( !strcmp( commitment->valuestring, "confirmed" ) ) ) _bank_idx = ctx->confirmed_idx;
     724           0 :     else if( FD_LIKELY( !strcmp( commitment->valuestring, "finalized" ) ) ) _bank_idx = ctx->finalized_idx;
     725           0 :     else _bank_idx = ctx->finalized_idx;
     726           0 :   } else {
     727           0 :     _bank_idx = ctx->finalized_idx;
     728           0 :   }
     729           0 :   if( FD_UNLIKELY( _bank_idx==ULONG_MAX ) ) {
     730           0 :     CSTR_JSON( id, id_cstr );
     731           0 :     *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32065,\"message\":\"Firedancer Error: banks uninitialized\"},\"id\":%s}\n", id_cstr );
     732           0 :     return 0;
     733           0 :   }
     734           0 :   *bank_idx = _bank_idx;
     735             : 
     736           0 :   if( FD_LIKELY( has_encoding ) ) {
     737           0 :     cJSON const * encoding = cJSON_GetObjectItemCaseSensitive( config, "encoding" );
     738             : 
     739           0 :     if( FD_UNLIKELY( cJSON_IsNumber( encoding ) || cJSON_IsBool( encoding ) ) ) {
     740           0 :       CSTR_JSON( id, id_cstr );
     741           0 :       CSTR_JSON( encoding, encoding_cstr );
     742           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s `%s`, expected string or map.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( encoding ), encoding_cstr, id_cstr );
     743           0 :       return 0;
     744           0 :     }
     745           0 :     if( FD_UNLIKELY( cJSON_IsObject( encoding ) && !(encoding->child && encoding->child->next==NULL) ) ) {
     746           0 :       CSTR_JSON( id, id_cstr );
     747           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid value: map, expected map with a single key.\"},\"id\":%s}\n", id_cstr );
     748           0 :       return 0;
     749           0 :     }
     750           0 :     if( FD_UNLIKELY( encoding && !cJSON_IsString( encoding ) && !cJSON_IsNull( encoding ) && !cJSON_IsObject( encoding ) ) ) {
     751           0 :       CSTR_JSON( id, id_cstr );
     752           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s, expected string or map.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( encoding ), id_cstr );
     753           0 :       return 0;
     754           0 :     }
     755             : 
     756           0 :     char const * encoding_cstr;
     757           0 :     if( FD_UNLIKELY( cJSON_IsObject( encoding ) ) ) {
     758           0 :       if( cJSON_HasObjectItem( encoding, "binary" ) ) encoding_cstr = "binary";
     759           0 :       else if( cJSON_HasObjectItem( encoding, "base58" ) ) encoding_cstr = "base58";
     760           0 :       else if( cJSON_HasObjectItem( encoding, "base64" ) ) encoding_cstr = "base64";
     761           0 :       else if( cJSON_HasObjectItem( encoding, "base64+zstd" ) ) encoding_cstr = "base64+zstd";
     762           0 :       else if( cJSON_HasObjectItem( encoding, "jsonParsed" ) ) encoding_cstr = "jsonParsed";
     763           0 :       else {
     764           0 :         CSTR_JSON( id, id_cstr );
     765           0 :         *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: unknown variant `%s`, expected one of `binary`, `base58`, `base64`, `jsonParsed`, `base64+zstd`.\"},\"id\":%s}\n", encoding->child->string, id_cstr );
     766           0 :         return 0;
     767           0 :       }
     768           0 :     } else {
     769           0 :       encoding_cstr = encoding && cJSON_IsString( encoding ) ? encoding->valuestring : "binary";
     770           0 :     }
     771             : 
     772           0 :     if( FD_UNLIKELY( cJSON_IsObject( encoding ) && (cJSON_IsNumber( encoding->child ) || cJSON_IsBool( encoding->child )) ) ) {
     773           0 :       CSTR_JSON( id, id_cstr ); CSTR_JSON( encoding->child, child_cstr );
     774           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s `%s`, expected unit.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( encoding->child ), child_cstr, id_cstr );
     775           0 :       return 0;
     776           0 :     }
     777           0 :     if( FD_UNLIKELY( cJSON_IsObject( encoding ) && cJSON_IsString( encoding->child ) ) ) {
     778           0 :       CSTR_JSON( id, id_cstr );
     779           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s \\\"%s\\\", expected unit.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( encoding->child ), encoding->child->valuestring, id_cstr );
     780           0 :       return 0;
     781           0 :     }
     782           0 :     if( FD_UNLIKELY( cJSON_IsObject( encoding ) && !cJSON_IsNull( encoding->child ) ) ) {
     783           0 :       CSTR_JSON( id, id_cstr );
     784           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s, expected unit.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( encoding->child ), id_cstr );
     785           0 :       return 0;
     786           0 :     }
     787             : 
     788           0 :     if( 0==strcmp( encoding_cstr, "jsonParsed" ) ) {
     789           0 :       CSTR_JSON( id, id_cstr );
     790           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32065,\"message\":\"Firedancer Error: jsonParsed is unsupported\"},\"id\":%s}\n", id_cstr );
     791           0 :       return 0;
     792           0 :     } else if( 0!=strcmp( encoding_cstr, "binary" ) && 0!=strcmp( encoding_cstr, "base58" ) && 0!=strcmp( encoding_cstr, "base64" ) && 0!=strcmp( encoding_cstr, "base64+zstd" ) ) {
     793           0 :       CSTR_JSON( id, id_cstr );
     794           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: unknown variant `%s`, expected one of `binary`, `base58`, `base64`, `jsonParsed`, `base64+zstd`.\"},\"id\":%s}\n", encoding_cstr, id_cstr );
     795           0 :       return 0;
     796           0 :     }
     797             : 
     798           0 :     *opt_encoding_cstr = encoding_cstr;
     799           0 :   }
     800             : 
     801           0 :   if( FD_LIKELY( has_data_slice ) ) {
     802           0 :     const cJSON * dataSlice = cJSON_GetObjectItemCaseSensitive( config, "dataSlice" );
     803             : 
     804           0 :     if( FD_UNLIKELY( cJSON_IsNumber( dataSlice ) || cJSON_IsBool( dataSlice ) ) ) {
     805           0 :       CSTR_JSON( id, id_cstr ); CSTR_JSON( dataSlice, data_slice_cstr );
     806           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s `%s`, expected struct UiDataSliceConfig.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( dataSlice ), data_slice_cstr, id_cstr );
     807           0 :       return 0;
     808           0 :     }
     809           0 :     if( FD_UNLIKELY( cJSON_IsString( dataSlice ) ) ) {
     810           0 :       CSTR_JSON( id, id_cstr );
     811           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s \\\"%s\\\", expected struct UiDataSliceConfig.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( dataSlice ), dataSlice->valuestring, id_cstr );
     812           0 :       return 0;
     813           0 :     }
     814           0 :     if( FD_UNLIKELY( dataSlice && !cJSON_IsObject( dataSlice ) && !cJSON_IsNull( dataSlice ) && !cJSON_IsArray( dataSlice ) ) ) {
     815           0 :       CSTR_JSON( id, id_cstr );
     816           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s, expected struct UiDataSliceConfig.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( dataSlice ), id_cstr );
     817           0 :       return 0;
     818           0 :     }
     819             : 
     820           0 :     int has_offset = cJSON_IsObject( dataSlice ) && cJSON_HasObjectItem( dataSlice, "offset" );
     821           0 :     int has_length = cJSON_IsObject( dataSlice ) && cJSON_HasObjectItem( dataSlice, "length" );
     822             : 
     823           0 :     cJSON const * _length = NULL;
     824           0 :     cJSON const * _offset = NULL;
     825           0 :     if( cJSON_IsObject( dataSlice ) ) {
     826           0 :       _length = cJSON_GetObjectItemCaseSensitive( dataSlice, "length" );
     827           0 :       _offset = cJSON_GetObjectItemCaseSensitive( dataSlice, "offset" );
     828           0 :     } else if( FD_UNLIKELY( cJSON_IsArray( dataSlice ) ) ) {
     829           0 :       _offset = cJSON_GetArrayItem( dataSlice, 0 );
     830           0 :       _length = cJSON_GetArrayItem( dataSlice, 1 );
     831           0 :     }
     832             : 
     833           0 :     if( FD_UNLIKELY( cJSON_IsBool( _offset ) || (cJSON_IsNumber( _offset ) && !fd_rpc_cjson_is_integer( _offset )) ) ) {
     834           0 :       CSTR_JSON( id, id_cstr ); CSTR_JSON( _offset, offset_cstr );
     835           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s `%s`, expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _offset ), offset_cstr, id_cstr );
     836           0 :       return 0;
     837           0 :     }
     838           0 :     if( FD_UNLIKELY( cJSON_IsBool( _length ) || (cJSON_IsNumber( _length ) && !fd_rpc_cjson_is_integer( _length )) ) ) {
     839           0 :       CSTR_JSON( id, id_cstr ); CSTR_JSON( _length, length_cstr );
     840           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s `%s`, expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _length ), length_cstr, id_cstr );
     841           0 :       return 0;
     842           0 :     }
     843             : 
     844           0 :     if( FD_UNLIKELY( cJSON_IsNumber( _offset ) && _offset->valueint<0 ) ) {
     845           0 :       CSTR_JSON( id, id_cstr ); CSTR_JSON( _offset, offset_cstr );
     846           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid value: %s `%s`, expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _offset ), offset_cstr, id_cstr );
     847           0 :       return 0;
     848           0 :     }
     849           0 :     if( FD_UNLIKELY( cJSON_IsNumber( _length ) && _length->valueint<0 ) ) {
     850           0 :       CSTR_JSON( id, id_cstr ); CSTR_JSON( _length, length_cstr );
     851           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid value: %s `%s`, expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _length ), length_cstr, id_cstr );
     852           0 :       return 0;
     853           0 :     }
     854             : 
     855           0 :     if( FD_UNLIKELY( cJSON_IsString( _offset ) ) ) {
     856           0 :       CSTR_JSON( id, id_cstr );
     857           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s \\\"%s\\\", expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _offset ), _offset->valuestring, id_cstr );
     858           0 :       return 0;
     859           0 :     }
     860           0 :     if( FD_UNLIKELY( cJSON_IsString( _length ) ) ) {
     861           0 :       CSTR_JSON( id, id_cstr );
     862           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s \\\"%s\\\", expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _length ), _length->valuestring, id_cstr );
     863           0 :       return 0;
     864           0 :     }
     865             : 
     866           0 :     if( FD_UNLIKELY( _offset && !fd_rpc_cjson_is_integer( _offset ) ) ) {
     867           0 :       CSTR_JSON( id, id_cstr );
     868           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s, expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _offset ), id_cstr );
     869           0 :       return 0;
     870           0 :     }
     871           0 :     if( FD_UNLIKELY( _length && !fd_rpc_cjson_is_integer( _length ) ) ) {
     872           0 :       CSTR_JSON( id, id_cstr );
     873           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s, expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _length ), id_cstr );
     874           0 :       return 0;
     875           0 :     }
     876             : 
     877           0 :     if( FD_UNLIKELY( cJSON_IsObject( dataSlice ) && !has_offset ) ) {
     878           0 :       CSTR_JSON( id, id_cstr );
     879           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: missing field `offset`.\"},\"id\":%s}\n", id_cstr );
     880           0 :       return 0;
     881           0 :     }
     882           0 :     if( FD_UNLIKELY( cJSON_IsObject( dataSlice ) && !has_length ) ) {
     883           0 :       CSTR_JSON( id, id_cstr );
     884           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: missing field `length`.\"},\"id\":%s}\n", id_cstr );
     885           0 :       return 0;
     886           0 :     }
     887             : 
     888           0 :     if( FD_UNLIKELY( cJSON_IsArray( dataSlice ) && cJSON_GetArraySize( dataSlice )!=2 ) ) {
     889           0 :       CSTR_JSON( id, id_cstr );
     890           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid length %lu, expected struct UiDataSliceConfig with 2 elements.\"},\"id\":%s}\n", (ulong)cJSON_GetArraySize( dataSlice ), id_cstr );
     891           0 :       return 0;
     892           0 :     }
     893             : 
     894           0 :     if( dataSlice && !cJSON_IsNull( dataSlice ) ) {
     895           0 :       *opt_slice_offset = _offset ? _offset->valueulong : 0UL;
     896           0 :       *opt_slice_length = _length ? _length->valueulong : ULONG_MAX;
     897           0 :     } else {
     898           0 :       *opt_slice_offset = 0UL;
     899           0 :       *opt_slice_length = ULONG_MAX;
     900           0 :     }
     901           0 :   }
     902             : 
     903           0 :   if( FD_LIKELY( has_min_context_slot ) ) {
     904           0 :     ulong minContextSlot = 0UL;
     905           0 :     cJSON const * _minContextSlot = cJSON_GetObjectItemCaseSensitive( config, "minContextSlot" );
     906           0 :     if( FD_UNLIKELY( cJSON_IsBool( _minContextSlot ) || (cJSON_IsNumber( _minContextSlot ) && !fd_rpc_cjson_is_integer( _minContextSlot )) ) ) {
     907           0 :       CSTR_JSON( id, id_cstr ); CSTR_JSON( _minContextSlot, min_context_slot_cstr );
     908           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s `%s`, expected u64.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _minContextSlot ), min_context_slot_cstr, id_cstr );
     909           0 :       return 0;
     910           0 :     }
     911             : 
     912           0 :     if( FD_UNLIKELY( cJSON_IsNumber( _minContextSlot ) && _minContextSlot->valueint<0 ) ) {
     913           0 :       CSTR_JSON( id, id_cstr ); CSTR_JSON( _minContextSlot, min_context_slot_cstr );
     914           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid value: %s `%s`, expected u64.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _minContextSlot ), min_context_slot_cstr, id_cstr );
     915           0 :       return 0;
     916           0 :     }
     917             : 
     918           0 :     if( FD_UNLIKELY( cJSON_IsString( _minContextSlot ) ) ) {
     919           0 :       CSTR_JSON( id, id_cstr );
     920           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s \\\"%s\\\", expected u64.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _minContextSlot ), _minContextSlot->valuestring, id_cstr );
     921           0 :       return 0;
     922           0 :     }
     923             : 
     924           0 :     if( FD_UNLIKELY( _minContextSlot && !cJSON_IsNull( _minContextSlot ) && !fd_rpc_cjson_is_integer( _minContextSlot ) ) ) {
     925           0 :       CSTR_JSON( id, id_cstr );
     926           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s, expected u64.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( _minContextSlot ), id_cstr );
     927           0 :       return 0;
     928           0 :     }
     929             : 
     930           0 :     minContextSlot = _minContextSlot && fd_rpc_cjson_is_integer( _minContextSlot ) ? _minContextSlot->valueulong : 0UL;
     931             : 
     932           0 :     if( _bank_idx!=ULONG_MAX && ctx->banks[ _bank_idx ].slot<minContextSlot ) {
     933           0 :       CSTR_JSON( id, id_cstr );
     934           0 :       *res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":%d,\"message\":\"Minimum context slot has not been reached\",\"data\":{\"contextSlot\":%lu}},\"id\":%s}\n", FD_RPC_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED, ctx->banks[ _bank_idx ].slot, id_cstr );
     935           0 :       return 0;
     936           0 :     }
     937           0 :   }
     938             : 
     939           0 :   return 1;
     940           0 : }
     941             : 
     942             : static int
     943             : fd_rpc_validate_address( fd_rpc_tile_t *             ctx,
     944             :                          cJSON const *               id,
     945             :                          cJSON const *               address_in,
     946             :                          fd_pubkey_t *               address_out,
     947           0 :                          fd_http_server_response_t * response ) {
     948           0 :   FD_TEST( address_in );
     949           0 :   if( FD_UNLIKELY( cJSON_IsNumber( address_in ) || cJSON_IsBool( address_in ) ) ) {
     950           0 :     CSTR_JSON( id, id_cstr ); CSTR_JSON( address_in, address_in_cstr );
     951           0 :     *response = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s `%s`, expected a string.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( address_in ), address_in_cstr, id_cstr );
     952           0 :     return 0;
     953           0 :   }
     954           0 :   if( FD_UNLIKELY( !cJSON_IsString( address_in ) ) ) {
     955           0 :     CSTR_JSON( id, id_cstr );
     956           0 :     *response = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s, expected a string.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( address_in ), id_cstr );
     957           0 :     return 0;
     958           0 :   }
     959           0 :   int invalid_char = fd_rpc_cstr_contains_non_base58( address_in->valuestring );
     960           0 :   if( FD_UNLIKELY( invalid_char ) ) {
     961           0 :     CSTR_JSON( id, id_cstr );
     962           0 :     *response = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid param: Invalid\"},\"id\":%s}\n", id_cstr );
     963           0 :     return 0;
     964           0 :   }
     965           0 :   int valid = !!fd_base58_decode_32( address_in->valuestring, address_out->uc );
     966           0 :   if( FD_UNLIKELY( !valid ) ) {
     967           0 :     CSTR_JSON( id, id_cstr );
     968           0 :     *response = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid param: WrongSize\"},\"id\":%s}\n", id_cstr );
     969           0 :     return 0;
     970           0 :   }
     971             : 
     972           0 :   return 1;
     973           0 : }
     974             : 
     975             : #define UNIMPLEMENTED(X)                               \
     976             : static fd_http_server_response_t                       \
     977             : X( fd_rpc_tile_t * ctx,                                \
     978             :    cJSON const *   id,                                 \
     979           0 :    cJSON const *   params ) {                          \
     980           0 :   (void)ctx; (void)id; (void)params;                   \
     981           0 :   return (fd_http_server_response_t){ .status = 501 }; \
     982           0 : }
     983             : 
     984             : UNIMPLEMENTED(getBlock)
     985             : UNIMPLEMENTED(getBlockCommitment)
     986             : 
     987             : static fd_http_server_response_t
     988             : getAccountInfo( fd_rpc_tile_t * ctx,
     989             :                 cJSON const *   id,
     990           0 :                 cJSON const *   params ) {
     991           0 :   fd_http_server_response_t response;
     992           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 1, 2, &response ) ) ) return response;
     993             : 
     994           0 :   fd_pubkey_t address;
     995           0 :   cJSON const * acct_pubkey = cJSON_GetArrayItem( params, 0 );
     996           0 :   if( FD_UNLIKELY( !fd_rpc_validate_address( ctx, id, acct_pubkey, &address, &response ) ) ) return response;
     997             : 
     998           0 :   ulong bank_idx = ULONG_MAX;
     999           0 :   char const * encoding_cstr = NULL;
    1000           0 :   ulong slice_length = ULONG_MAX;
    1001           0 :   ulong slice_offset = 0;
    1002           0 :   cJSON const * config = cJSON_GetArrayItem( params, 1 );
    1003           0 :   int config_valid = fd_rpc_validate_config( ctx, id, config, "struct RpcAccountInfoConfig",
    1004           0 :                                              1, /* has_commitment */
    1005           0 :                                              1, /* has_encoding */
    1006           0 :                                              1, /* has_data_slice */
    1007           0 :                                              1, /* has_min_context_slot */
    1008           0 :                                              &bank_idx,
    1009           0 :                                              &encoding_cstr,
    1010           0 :                                              &slice_length,
    1011           0 :                                              &slice_offset,
    1012           0 :                                              &response );
    1013           0 :   if( FD_UNLIKELY( !config_valid ) ) return response;
    1014             : 
    1015           0 :   bank_info_t * info = &ctx->banks[ bank_idx ];
    1016           0 :   fd_funk_txn_xid_t xid = { .ul={ info->slot, bank_idx } };
    1017           0 :   fd_accdb_ro_t ro[1];
    1018           0 :   if( FD_UNLIKELY( !fd_accdb_open_ro( ctx->accdb, ro, &xid, address.uc ) ) ) {
    1019           0 :     CSTR_JSON( id, id_cstr );
    1020           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"slot\":%lu},\"value\":null},\"id\":%s}\n", info->slot, id_cstr );
    1021           0 :   }
    1022             : 
    1023           0 :   ulong const data_sz   = fd_accdb_ref_data_sz( ro );
    1024           0 :   uchar const * out     = (uchar const *)fd_accdb_ref_data_const( ro )+fd_ulong_if(slice_offset<data_sz, slice_offset, 0UL );
    1025           0 :   ulong         snip_sz = fd_ulong_min( fd_ulong_if( slice_offset<data_sz, data_sz-slice_offset, 0UL ), slice_length );
    1026           0 :   ulong         out_sz  = snip_sz;
    1027             : 
    1028           0 :   int is_binary = !strncmp( encoding_cstr, "binary", strlen("binary") );
    1029           0 :   int is_base58 = !strncmp( encoding_cstr, "base58", strlen("base58") );
    1030           0 :   int is_zstd   = !strncmp( encoding_cstr, "base64+zstd", strlen("base64+zstd") );
    1031           0 :   if( FD_UNLIKELY( (is_binary || is_base58) && snip_sz>128UL ) ) {
    1032           0 :     fd_accdb_close_ro( ctx->accdb, ro );
    1033           0 :     CSTR_JSON( id, id_cstr );
    1034           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Encoded binary (base 58) data should be less than {MAX_BASE58_BYTES} bytes, please use Base64 encoding.\"},\"id\":%s}\n", id_cstr );
    1035           0 :   }
    1036             : 
    1037           0 : # if FD_HAS_ZSTD
    1038           0 :   if( is_zstd ) {
    1039           0 :     ulong zstd_res = ZSTD_compress( ctx->compress_buf, sizeof(ctx->compress_buf), out, snip_sz, 0 );
    1040           0 :     if( ZSTD_isError( zstd_res ) ) {
    1041           0 :       fd_accdb_close_ro( ctx->accdb, ro );
    1042           0 :       CSTR_JSON( id, id_cstr );
    1043           0 :       return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32065,\"message\":\"Firedancer Error: zstandard compression failed (%s)\"},\"id\":%s}\n", ZSTD_getErrorName( zstd_res ), id_cstr );
    1044           0 :     }
    1045           0 :     out = ctx->compress_buf;
    1046           0 :     out_sz     = (ulong)zstd_res;
    1047           0 :   }
    1048             : # else
    1049             :   if( is_zstd ) {
    1050             :     fd_accdb_close_ro( ctx->accdb, ro );
    1051             :     CSTR_JSON( id, id_cstr );
    1052             :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32065,\"message\":\"Firedancer Error: zstandard is disabled\"},\"id\":%s}\n", id_cstr );
    1053             :   }
    1054             : # endif
    1055             : 
    1056           0 :   FD_BASE58_ENCODE_32_BYTES( fd_accdb_ref_owner( ro ), owner_b58 );
    1057           0 :   CSTR_JSON( id, id_cstr );
    1058           0 :   fd_http_server_printf( ctx->http,
    1059           0 :       "{\"jsonrpc\":\"2.0\",\"id\":%s,\"result\":{\"context\":{\"apiVersion\":\"%s\",\"slot\":%lu},\"value\":{"
    1060           0 :       "\"executable\":%s,"
    1061           0 :       "\"lamports\":%lu,"
    1062           0 :       "\"owner\":\"%s\","
    1063           0 :       "\"rentEpoch\":18446744073709551615,"
    1064           0 :       "\"space\":%lu,"
    1065           0 :       "\"data\":",
    1066           0 :       id_cstr,
    1067           0 :       FD_RPC_AGAVE_API_VERSION,
    1068           0 :       info->slot,
    1069           0 :       fd_accdb_ref_exec_bit( ro ) ? "true" : "false",
    1070           0 :       fd_accdb_ref_lamports( ro ),
    1071           0 :       owner_b58,
    1072           0 :       data_sz );
    1073             : 
    1074           0 :   ulong encoded_sz = fd_ulong_if( is_base58 || is_binary, FD_RPC_BASE58_ENCODED_128_LEN, FD_BASE64_ENC_SZ( out_sz ) );
    1075           0 :   if( FD_UNLIKELY( is_binary ) ) {
    1076           0 :     fd_http_server_printf( ctx->http, "\"" );
    1077           0 :   } else {
    1078           0 :     fd_http_server_printf( ctx->http, "[\"" );
    1079           0 :   }
    1080             : 
    1081           0 :   uchar * encoded = fd_http_server_append_start( ctx->http, encoded_sz );;
    1082           0 :   if( FD_UNLIKELY( !encoded ) ) {
    1083           0 :     fd_accdb_close_ro( ctx->accdb, ro );
    1084           0 :     CSTR_JSON( id, id_cstr );
    1085           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32065,\"message\":\"Firedancer Error: large accounts unsupported\"},\"id\":%s}\n", id_cstr );
    1086           0 :   }
    1087             : 
    1088           0 :   if( FD_UNLIKELY( is_base58 || is_binary ) ) {
    1089           0 :     if( FD_UNLIKELY( !fd_rpc_base58_encode_128( (char *)encoded, &encoded_sz, out, out_sz ) ) ) {
    1090           0 :       fd_http_server_unstage( ctx->http );
    1091           0 :       fd_accdb_close_ro( ctx->accdb, ro );
    1092           0 :       FD_LOG_WARNING(( "base58 encode failed out_sz=%lu", out_sz ));
    1093           0 :       return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32065,\"message\":\"Firedancer Error: base58 encode failed\"},\"id\":%s}\n", id_cstr );
    1094           0 :     }
    1095           0 :   } else {
    1096           0 :     encoded_sz = fd_base64_encode( (char *)encoded, out, out_sz );
    1097           0 :   }
    1098             : 
    1099           0 :   fd_accdb_close_ro( ctx->accdb, ro );
    1100             : 
    1101           0 :   fd_http_server_append_end( ctx->http, encoded_sz );
    1102             : 
    1103           0 :   if( FD_UNLIKELY( is_binary ) ) fd_http_server_printf( ctx->http, "\"}}}\n" );
    1104           0 :   else                           fd_http_server_printf( ctx->http, "\",\"%s\"]}}}\n", encoding_cstr );
    1105             : 
    1106           0 :   return STAGE_JSON( ctx );
    1107           0 : }
    1108             : 
    1109             : static fd_http_server_response_t
    1110             : getBalance( fd_rpc_tile_t * ctx,
    1111             :             cJSON const *   id,
    1112           0 :             cJSON const *   params ) {
    1113           0 :   fd_http_server_response_t response;
    1114           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 1, 2, &response ) ) ) return response;
    1115             : 
    1116           0 :   fd_pubkey_t address;
    1117           0 :   cJSON const * acct_pubkey = cJSON_GetArrayItem( params, 0 );
    1118           0 :   if( FD_UNLIKELY( !fd_rpc_validate_address( ctx, id, acct_pubkey, &address, &response ) ) ) return response;
    1119             : 
    1120           0 :   ulong bank_idx = ULONG_MAX;
    1121           0 :   cJSON const * config = cJSON_GetArrayItem( params, 1 );
    1122           0 :   int config_valid = fd_rpc_validate_config( ctx, id, config, "struct RpcContextConfig",
    1123           0 :                                              1, /* has_commitment */
    1124           0 :                                              0, /* has_encoding */
    1125           0 :                                              0, /* has_data_slice */
    1126           0 :                                              1, /* has_min_context_slot */
    1127           0 :                                              &bank_idx,
    1128           0 :                                              NULL,
    1129           0 :                                              NULL,
    1130           0 :                                              NULL,
    1131           0 :                                              &response );
    1132           0 :   if( FD_UNLIKELY( !config_valid ) ) return response;
    1133             : 
    1134           0 :   ulong balance = 0UL;
    1135           0 :   fd_funk_txn_xid_t xid = { .ul={ ctx->banks[ bank_idx ].slot, bank_idx } };
    1136           0 :   fd_accdb_ro_t ro[ 1 ];
    1137           0 :   if( FD_UNLIKELY( fd_accdb_open_ro( ctx->accdb, ro, &xid, address.uc ) ) ) {
    1138           0 :     balance = fd_accdb_ref_lamports( ro );
    1139           0 :     fd_accdb_close_ro( ctx->accdb, ro );
    1140           0 :   }
    1141             : 
    1142           0 :   CSTR_JSON( id, id_cstr );
    1143           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"%s\",\"slot\":%lu},\"value\":%lu},\"id\":%s}\n", FD_RPC_AGAVE_API_VERSION, ctx->banks[ bank_idx ].slot, balance, id_cstr );
    1144           0 : }
    1145             : 
    1146             : static fd_http_server_response_t
    1147             : getBlockHeight( fd_rpc_tile_t * ctx,
    1148             :                 cJSON const *   id,
    1149           0 :                 cJSON const *   params ) {
    1150           0 :   fd_http_server_response_t response;
    1151           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 1, &response ) ) ) return response;
    1152             : 
    1153           0 :   ulong bank_idx = ULONG_MAX;
    1154           0 :   cJSON const * config = cJSON_GetArrayItem( params, 0 );
    1155           0 :   int config_valid = fd_rpc_validate_config( ctx, id, config, "struct RpcContextConfig",
    1156           0 :                                              1, /* has_commitment */
    1157           0 :                                              0, /* has_encoding */
    1158           0 :                                              0, /* has_data_slice */
    1159           0 :                                              1, /* has_min_context_slot */
    1160           0 :                                              &bank_idx,
    1161           0 :                                              NULL,
    1162           0 :                                              NULL,
    1163           0 :                                              NULL,
    1164           0 :                                              &response );
    1165           0 :   if( FD_UNLIKELY( !config_valid ) ) return response;
    1166             : 
    1167           0 :   CSTR_JSON( id, id_cstr );
    1168           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":%lu,\"id\":%s}\n", ctx->banks[ bank_idx ].block_height, id_cstr );
    1169           0 : }
    1170             : 
    1171             : UNIMPLEMENTED(getBlockProduction) // TODO: Used by solana-exporter
    1172             : UNIMPLEMENTED(getBlocks)
    1173             : UNIMPLEMENTED(getBlocksWithLimit)
    1174             : UNIMPLEMENTED(getBlockTime)
    1175             : 
    1176             : static fd_http_server_response_t
    1177             : getClusterNodes( fd_rpc_tile_t * ctx,
    1178             :                  cJSON const *   id,
    1179           0 :                  cJSON const *   params ) {
    1180           0 :   fd_http_server_response_t response;
    1181           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 0, &response ) ) ) return response;
    1182             : 
    1183           0 :   fd_http_server_printf( ctx->http, "{\"jsonrpc\":\"2.0\",\"result\":[" );
    1184             : 
    1185           0 :   for( fd_rpc_cluster_node_dlist_iter_t iter = fd_rpc_cluster_node_dlist_iter_rev_init( ctx->cluster_nodes_dlist, ctx->cluster_nodes );
    1186           0 :        !fd_rpc_cluster_node_dlist_iter_done( iter, ctx->cluster_nodes_dlist, ctx->cluster_nodes );
    1187           0 :        iter = fd_rpc_cluster_node_dlist_iter_rev_next( iter, ctx->cluster_nodes_dlist, ctx->cluster_nodes ) ) {
    1188           0 :     fd_rpc_cluster_node_t * ele = fd_rpc_cluster_node_dlist_iter_ele( iter, ctx->cluster_nodes_dlist, ctx->cluster_nodes );
    1189           0 :     FD_BASE58_ENCODE_32_BYTES( ele->identity.uc, identity_cstr );
    1190           0 :     int is_last = fd_rpc_cluster_node_dlist_iter_done( fd_rpc_cluster_node_dlist_iter_rev_next( iter, ctx->cluster_nodes_dlist, ctx->cluster_nodes ), ctx->cluster_nodes_dlist, ctx->cluster_nodes );
    1191             : 
    1192           0 :     fd_http_server_printf( ctx->http, "{\"featureSet\":%u,", ele->ci->version.feature_set );
    1193             : 
    1194           0 :     for( ulong i=0UL; i<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; i++ ) {
    1195           0 :       char const * name;
    1196           0 :       switch( i ) {
    1197           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP:            name = "gossip"; break;
    1198           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR_QUIC: name = NULL; break;
    1199           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_RPC:               name = "rpc"; break;
    1200           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_RPC_PUBSUB:        name = "pubsub"; break;
    1201           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR:      name = "serveRepair"; break;
    1202           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU:               name = "tpu"; break;
    1203           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS:      name = "tpuForwards"; break;
    1204           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS_QUIC: name = "tpuForwardsQuic"; break;
    1205           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_QUIC:          name = "tpuQuic"; break;
    1206           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE:          name = "tpuVote"; break;
    1207           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_TVU:               name = "tvu"; break;
    1208           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_TVU_QUIC:          name = NULL; break;
    1209           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE_QUIC:     name = NULL; break;
    1210           0 :         case FD_GOSSIP_CONTACT_INFO_SOCKET_ALPENGLOW:         name = NULL; break;
    1211           0 :         default: FD_LOG_ERR(( "unreachable "));
    1212           0 :       }
    1213           0 :       if( FD_UNLIKELY( !name ) ) continue;
    1214             : 
    1215           0 :       uint ip4 = ele->ci->sockets[ i ].is_ipv6 ? 0U : ele->ci->sockets[ i ].ip4;
    1216           0 :       if( FD_LIKELY( !!ip4 || !!ele->ci->sockets[ i ].port ) ) fd_http_server_printf( ctx->http, "\"%s\":\"" FD_IP4_ADDR_FMT ":%hu\",", name, FD_IP4_ADDR_FMT_ARGS( ip4 ), fd_ushort_bswap( ele->ci->sockets[ i ].port ) );
    1217           0 :       else                                                     fd_http_server_printf( ctx->http, "\"%s\":null,", name );
    1218           0 :     }
    1219           0 :     fd_http_server_printf( ctx->http, "\"pubkey\":\"%s\",", identity_cstr );
    1220           0 :     fd_http_server_printf( ctx->http, "\"shredVersion\":%u,", ele->ci->shred_version );
    1221             : 
    1222           0 :     char version[ 64UL ];
    1223           0 :     FD_TEST( fd_gossip_version_cstr( ele->ci->version.major, ele->ci->version.minor, ele->ci->version.patch, version, sizeof( version ) ) );
    1224           0 :     fd_http_server_printf( ctx->http, "\"version\":\"%s\"", version );
    1225             : 
    1226           0 :     if( FD_UNLIKELY( is_last ) ) fd_http_server_printf( ctx->http, "}" );
    1227           0 :     else                         fd_http_server_printf( ctx->http, "}," );
    1228           0 :   }
    1229             : 
    1230           0 :   CSTR_JSON( id, id_cstr );
    1231           0 :   fd_http_server_printf( ctx->http, "],\"id\":%s}\n", id_cstr );
    1232           0 :   return STAGE_JSON( ctx );
    1233           0 : }
    1234             : 
    1235             : static fd_http_server_response_t
    1236             : getEpochInfo( fd_rpc_tile_t * ctx,
    1237             :               cJSON const *   id,
    1238           0 :               cJSON const *   params ) {
    1239           0 :   fd_http_server_response_t response;
    1240           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 1, &response ) ) ) return response;
    1241             : 
    1242           0 :   ulong bank_idx = ULONG_MAX;
    1243           0 :   cJSON const * config = cJSON_GetArrayItem( params, 0 );
    1244           0 :   int config_valid = fd_rpc_validate_config( ctx, id, config, "struct RpcContextConfig",
    1245           0 :                                               1, /* has_commitment */
    1246           0 :                                               0, /* has_encoding */
    1247           0 :                                               0, /* has_data_slice */
    1248           0 :                                               1, /* has_min_context_slot */
    1249           0 :                                               &bank_idx,
    1250           0 :                                               NULL,
    1251           0 :                                               NULL,
    1252           0 :                                               NULL,
    1253           0 :                                               &response );
    1254           0 :   if( FD_UNLIKELY( !config_valid ) ) return response;
    1255             : 
    1256           0 :   CSTR_JSON( id, id_cstr );
    1257           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":{\"absoluteSlot\":%lu,\"blockHeight\":%lu,\"epoch\":%lu,\"slotIndex\":%lu,\"slotsInEpoch\":%lu,\"transactionCount\":%lu},\"id\":%s}\n", ctx->banks[ bank_idx ].slot, ctx->banks[ bank_idx ].block_height, ctx->banks[ bank_idx ].epoch, ctx->banks[ bank_idx ].slot_in_epoch, ctx->banks[ bank_idx ].slots_per_epoch, ctx->banks[ bank_idx ].transaction_count, id_cstr );
    1258           0 : }
    1259             : 
    1260             : UNIMPLEMENTED(getEpochSchedule)
    1261             : UNIMPLEMENTED(getFeeForMessage)
    1262             : UNIMPLEMENTED(getFirstAvailableBlock) // TODO: Used by solana-exporter
    1263             : 
    1264             : /* Get the genesis hash of the cluster.  Firedancer deviates slightly
    1265             :    from Agave here, as the genesis hash is not always known when RPC
    1266             :    is first booted, it may need to be determined asynchronously in the
    1267             :    background, fetched from a peer node.  If the genesis hash is not yet
    1268             :    known, we return an error indicating no snapshot is available. */
    1269             : 
    1270             : static fd_http_server_response_t
    1271             : getGenesisHash( fd_rpc_tile_t * ctx,
    1272             :                 cJSON const *   id,
    1273           0 :                 cJSON const *   params ) {
    1274           0 :   fd_http_server_response_t response;
    1275           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 0, &response ) ) ) return response;
    1276             : 
    1277           0 :   if( FD_UNLIKELY( !ctx->has_genesis_hash ) ) {
    1278           0 :     CSTR_JSON( id, id_cstr );
    1279           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":%d,\"message\":\"Firedancer Error: No genesis hash\"},\"id\":%s}\n", FD_RPC_ERROR_NO_SNAPSHOT, id_cstr );
    1280           0 :   }
    1281             : 
    1282           0 :   FD_BASE58_ENCODE_32_BYTES( ctx->genesis_hash->uc, genesis_hash_b58 );
    1283           0 :   CSTR_JSON( id, id_cstr );
    1284           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":\"%s\",\"id\":%s}\n", genesis_hash_b58, id_cstr );
    1285           0 : }
    1286             : 
    1287             : /* Determines if the node is healthy.  Agave defines this as follows,
    1288             : 
    1289             :     - On boot, nodes must go through the entire snapshot slot database
    1290             :       and hash everything, and once it's done verify the hash matches.
    1291             :       While this is ongoing, the node is unhealthy with a "slotsBehind"
    1292             :       value of null.
    1293             : 
    1294             :     - On boot, if the cluster is restarting and we are currently waiting
    1295             :       for a supermajority of stake to join gossip to proceed with
    1296             :       booting, the node is forcibly marked as healthy, to, per Agave,
    1297             : 
    1298             :        > prevent load balancers from removing the node from their list
    1299             :        > of candidates during a manual restart
    1300             : 
    1301             :     - In addition, once booted, there is a period where we do not yet
    1302             :       know the cluster confirmed slot, because we have not yet observed
    1303             :       any (or enough) votes arrive from peers in the cluster.  During
    1304             :       this period the node is unhealthy with a "slotsBehind" value of
    1305             :       null.
    1306             : 
    1307             :     - Finally, once the cluster confirmed slot is known, which is the
    1308             :       highest optimistically confirmed slot observed from both gossip,
    1309             :       and votes procesed in blocks, it is compared to our own
    1310             :       optimistically confirmed slot, which is just the highest slot down
    1311             :       the cluster confirmed fork that we have finished replaying
    1312             :       locally.  The difference between these two slots is compared, and
    1313             :       if it is less than or equal to 128, the node is healthy, otherwise
    1314             :       it is unhealthy with a "slotsBehind" value equal to the
    1315             :       difference.
    1316             : 
    1317             :    Firedancer currently only implements the final two checks, and does
    1318             :    not forcibly mark the node as healthy while waiting for a
    1319             :    supermajority, nor does it mark a node as unhealthy while hashing the
    1320             :    snapshot database on boot.  Firedancer hashes snapshots so quickly
    1321             :    that the node will die on boot if the hash is not valid. */
    1322             : 
    1323             : static inline int
    1324           0 : _getHealth( fd_rpc_tile_t * ctx ) {
    1325             :   /* fd_http_server_listen is not called until after RPC has initialized banks */
    1326           0 :   if( FD_UNLIKELY( ctx->confirmed_idx==ULONG_MAX ) ) return FD_RPC_HEALTH_STATUS_UNKNOWN;
    1327           0 :   if( FD_UNLIKELY( ctx->cluster_confirmed_slot==ULONG_MAX ) ) return FD_RPC_HEALTH_STATUS_UNKNOWN;
    1328             : 
    1329           0 :   ulong slots_behind = fd_ulong_sat_sub( ctx->cluster_confirmed_slot, ctx->banks[ ctx->confirmed_idx ].slot );
    1330           0 :   if( FD_LIKELY( slots_behind<=128UL ) ) return FD_RPC_HEALTH_STATUS_OK;
    1331           0 :   else                                   return FD_RPC_HEALTH_STATUS_BEHIND;
    1332           0 : }
    1333             : 
    1334             : static fd_http_server_response_t
    1335             : getHealth( fd_rpc_tile_t * ctx,
    1336             :            cJSON const *   id,
    1337           0 :            cJSON const *   params ) {
    1338           0 :   fd_http_server_response_t response;
    1339           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 0, &response ) ) ) return response;
    1340             : 
    1341             :   // TODO: We should probably implement the same waiting_for_supermajority
    1342             :   // logic to conform with Agave here.
    1343             : 
    1344           0 :   int health_status = _getHealth( ctx );
    1345             : 
    1346           0 :   CSTR_JSON( id, id_cstr );
    1347           0 :   switch( health_status ) {
    1348           0 :     case FD_RPC_HEALTH_STATUS_UNKNOWN: return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":%d,\"message\":\"Node is unhealthy\",\"data\":{\"slotsBehind\":null}},\"id\":%s}\n", FD_RPC_ERROR_NODE_UNHEALTHY, id_cstr );
    1349           0 :     case FD_RPC_HEALTH_STATUS_BEHIND:  return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":%d,\"message\":\"Node is unhealthy\",\"data\":{\"slotsBehind\":%lu}},\"id\":%s}\n", FD_RPC_ERROR_NODE_UNHEALTHY, fd_ulong_sat_sub( ctx->cluster_confirmed_slot, ctx->banks[ ctx->confirmed_idx ].slot ), id_cstr );
    1350           0 :     case FD_RPC_HEALTH_STATUS_OK:      return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":\"ok\",\"id\":%s}\n", id_cstr );
    1351           0 :     default: FD_LOG_ERR(( "unknown health status" ));
    1352           0 :   }
    1353           0 : }
    1354             : 
    1355             : UNIMPLEMENTED(getHighestSnapshotSlot)
    1356             : 
    1357             : static fd_http_server_response_t
    1358             : getIdentity( fd_rpc_tile_t * ctx,
    1359             :              cJSON const *   id,
    1360           0 :              cJSON const *   params ) {
    1361           0 :   fd_http_server_response_t response;
    1362           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 0, &response ) ) ) return response;
    1363             : 
    1364           0 :   FD_BASE58_ENCODE_32_BYTES( ctx->identity_pubkey, identity_pubkey_b58 );
    1365           0 :   CSTR_JSON( id, id_cstr );
    1366           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":{\"identity\":\"%s\"},\"id\":%s}\n", identity_pubkey_b58, id_cstr );
    1367           0 : }
    1368             : 
    1369             : static fd_http_server_response_t
    1370             : getInflationGovernor( fd_rpc_tile_t * ctx,
    1371             :                      cJSON const *   id,
    1372           0 :                      cJSON const *   params ) {
    1373           0 :   fd_http_server_response_t response;
    1374           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 1, &response ) ) ) return response;
    1375             : 
    1376           0 :   ulong bank_idx = ULONG_MAX;
    1377           0 :   cJSON const * config = cJSON_GetArrayItem( params, 0 );
    1378           0 :   int config_valid = fd_rpc_validate_config( ctx, id, config, "struct CommitmentConfig",
    1379           0 :                                              1, /* has_commitment */
    1380           0 :                                              0, /* has_encoding */
    1381           0 :                                              0, /* has_data_slice */
    1382           0 :                                              0, /* has_min_context_slot */
    1383           0 :                                              &bank_idx,
    1384           0 :                                              NULL,
    1385           0 :                                              NULL,
    1386           0 :                                              NULL,
    1387           0 :                                              &response );
    1388           0 :   if( FD_UNLIKELY( !config_valid ) ) return response;
    1389             : 
    1390           0 :   bank_info_t const * bank = &ctx->banks[ bank_idx ];
    1391           0 :   CSTR_JSON( id, id_cstr );
    1392           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":{\"foundation\":%g%s,\"foundationTerm\":%g%s,\"initial\":%g%s,\"taper\":%g%s,\"terminal\":%g%s},\"id\":%s}\n",
    1393           0 :                            bank->inflation.foundation, bank->inflation.foundation==0 ? ".0" : "",
    1394           0 :                            bank->inflation.foundation_term, bank->inflation.foundation_term==0 ? ".0" : "",
    1395           0 :                            bank->inflation.initial, bank->inflation.initial==0 ? ".0" : "",
    1396           0 :                            bank->inflation.taper, bank->inflation.taper==0 ? ".0" : "",
    1397           0 :                            bank->inflation.terminal, bank->inflation.terminal==0 ? ".0" : "",
    1398           0 :                            id_cstr );
    1399           0 : }
    1400             : 
    1401             : UNIMPLEMENTED(getInflationRate)
    1402             : UNIMPLEMENTED(getInflationReward) // TODO: Used by solana-exporter
    1403             : UNIMPLEMENTED(getLargestAccounts)
    1404             : 
    1405             : static fd_http_server_response_t
    1406             : getLatestBlockhash( fd_rpc_tile_t * ctx,
    1407             :                     cJSON const *   id,
    1408           0 :                     cJSON const *   params ) {
    1409           0 :   if( FD_UNLIKELY( ctx->processed_idx==ULONG_MAX ) ) {
    1410           0 :     CSTR_JSON( id, id_cstr );
    1411           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32065,\"message\":\"Firedancer Error: banks uninitialized\"},\"id\":%s}\n", id_cstr );
    1412           0 :   }
    1413             : 
    1414           0 :   fd_http_server_response_t response;
    1415           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 1, &response ) ) ) return response;
    1416             : 
    1417           0 :   ulong bank_idx = ULONG_MAX;
    1418           0 :   cJSON const * config = cJSON_GetArrayItem( params, 0 );
    1419           0 :   int config_valid = fd_rpc_validate_config( ctx, id, config, "struct CommitmentConfig",
    1420           0 :                                              1, /* has_commitment */
    1421           0 :                                              0, /* has_encoding */
    1422           0 :                                              0, /* has_data_slice */
    1423           0 :                                              1, /* has_min_context_slot */
    1424           0 :                                              &bank_idx,
    1425           0 :                                              NULL,
    1426           0 :                                              NULL,
    1427           0 :                                              NULL,
    1428           0 :                                              &response );
    1429           0 :   if( FD_UNLIKELY( !config_valid ) ) return response;
    1430             : 
    1431           0 :   bank_info_t * bank = &ctx->banks[ bank_idx ];
    1432           0 :   FD_BASE58_ENCODE_32_BYTES( bank->block_hash, block_hash_b58 );
    1433             : 
    1434           0 :   ulong age = ctx->banks[ ctx->processed_idx ].block_height - bank->block_height;
    1435           0 :   FD_TEST( bank->block_height <= ctx->banks[ ctx->processed_idx ].block_height );
    1436           0 :   FD_TEST( bank->block_height + 150UL >= age );
    1437           0 :   CSTR_JSON( id, id_cstr );
    1438           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"slot\":%lu,\"apiVersion\":\"%s\"},\"value\":{\"blockhash\":\"%s\",\"lastValidBlockHeight\":%lu}},\"id\":%s}\n", bank->slot, FD_RPC_AGAVE_API_VERSION, block_hash_b58, bank->block_height + 150UL - age, id_cstr );
    1439           0 : }
    1440             : 
    1441             : UNIMPLEMENTED(getLeaderSchedule) // TODO: Used by solana-exporter
    1442             : UNIMPLEMENTED(getMaxRetransmitSlot)
    1443             : UNIMPLEMENTED(getMaxShredInsertSlot)
    1444             : 
    1445             : static fd_http_server_response_t
    1446             : getMinimumBalanceForRentExemption( fd_rpc_tile_t * ctx,
    1447             :                                    cJSON const *   id,
    1448           0 :                                    cJSON const *   params ) {
    1449           0 :   fd_http_server_response_t response;
    1450           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 1, 2, &response ) ) ) return response;
    1451             : 
    1452           0 :   ulong bank_idx = ULONG_MAX;
    1453           0 :   cJSON const * config = cJSON_GetArrayItem( params, 1 );
    1454           0 :   int config_valid = fd_rpc_validate_config( ctx, id, config, "struct CommitmentConfig",
    1455           0 :                                              1, /* has_commitment */
    1456           0 :                                              0, /* has_encoding */
    1457           0 :                                              0, /* has_data_slice */
    1458           0 :                                              0, /* has_min_context_slot */
    1459           0 :                                              &bank_idx,
    1460           0 :                                              NULL,
    1461           0 :                                              NULL,
    1462           0 :                                              NULL,
    1463           0 :                                              &response );
    1464           0 :   if( FD_UNLIKELY( !config_valid ) ) return response;
    1465             : 
    1466           0 :   cJSON const * acct_sz = cJSON_GetArrayItem( params, 0 );
    1467             : 
    1468           0 :   if( FD_UNLIKELY( cJSON_IsBool( acct_sz ) || (cJSON_IsNumber( acct_sz ) && !fd_rpc_cjson_is_integer( acct_sz )) ) ) {
    1469           0 :     CSTR_JSON( id, id_cstr );
    1470           0 :     CSTR_JSON( acct_sz, acct_sz_cstr );
    1471           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s `%s`, expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( acct_sz ), acct_sz_cstr, id_cstr );
    1472           0 :   }
    1473           0 :   if( FD_UNLIKELY( cJSON_IsNumber( acct_sz ) && acct_sz->valueint<0 ) ) {
    1474           0 :     CSTR_JSON( id, id_cstr );
    1475           0 :     CSTR_JSON( acct_sz, acct_sz_cstr );
    1476           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid value: %s `%s`, expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( acct_sz ), acct_sz_cstr, id_cstr );
    1477           0 :   }
    1478           0 :   if( FD_UNLIKELY( cJSON_IsString( acct_sz ) ) ) {
    1479           0 :     CSTR_JSON( id, id_cstr );
    1480           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s \\\"%s\\\", expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( acct_sz ), acct_sz->valuestring, id_cstr );
    1481           0 :   }
    1482           0 :   if( FD_UNLIKELY( acct_sz && !fd_rpc_cjson_is_integer( acct_sz ) ) ) {
    1483           0 :     CSTR_JSON( id, id_cstr );
    1484           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: invalid type: %s, expected usize.\"},\"id\":%s}\n", fd_rpc_cjson_type_to_cstr( acct_sz ), id_cstr );
    1485           0 :   }
    1486             : 
    1487           0 :   bank_info_t const * bank = &ctx->banks[ bank_idx ];
    1488             : 
    1489           0 :   fd_rent_t rent = {
    1490           0 :     .lamports_per_uint8_year = bank->rent.lamports_per_uint8_year,
    1491           0 :     .exemption_threshold = bank->rent.exemption_threshold,
    1492           0 :     .burn_percent = bank->rent.burn_percent,
    1493           0 :   };
    1494           0 :   ulong minimum = fd_rent_exempt_minimum_balance( &rent, acct_sz->valueulong );
    1495           0 :   CSTR_JSON( id, id_cstr );
    1496           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":%lu,\"id\":%s}\n", minimum, id_cstr );
    1497           0 : }
    1498             : 
    1499             : UNIMPLEMENTED(getMultipleAccounts)
    1500             : UNIMPLEMENTED(getProgramAccounts)
    1501             : UNIMPLEMENTED(getRecentPerformanceSamples)
    1502             : UNIMPLEMENTED(getRecentPrioritizationFees)
    1503             : UNIMPLEMENTED(getSignaturesForAddress)
    1504             : UNIMPLEMENTED(getSignatureStatuses)
    1505             : 
    1506             : static fd_http_server_response_t
    1507             : getSlot( fd_rpc_tile_t * ctx,
    1508             :          cJSON const *   id,
    1509           0 :          cJSON const *   params ) {
    1510           0 :   fd_http_server_response_t response;
    1511           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 1, &response ) ) ) return response;
    1512             : 
    1513           0 :   ulong bank_idx = ULONG_MAX;
    1514           0 :   cJSON const * config = cJSON_GetArrayItem( params, 0 );
    1515           0 :   int config_valid = fd_rpc_validate_config( ctx, id, config, "struct CommitmentConfig",
    1516           0 :                                              1, /* has_commitment */
    1517           0 :                                              0, /* has_encoding */
    1518           0 :                                              0, /* has_data_slice */
    1519           0 :                                              1, /* has_min_context_slot */
    1520           0 :                                              &bank_idx,
    1521           0 :                                              NULL,
    1522           0 :                                              NULL,
    1523           0 :                                              NULL,
    1524           0 :                                              &response );
    1525           0 :   if( FD_UNLIKELY( !config_valid ) ) return response;
    1526             : 
    1527           0 :   bank_info_t * bank = &ctx->banks[ bank_idx ];
    1528           0 :   CSTR_JSON( id, id_cstr );
    1529           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":%lu,\"id\":%s}\n", bank->slot, id_cstr );
    1530           0 : }
    1531             : 
    1532             : UNIMPLEMENTED(getSlotLeader)
    1533             : UNIMPLEMENTED(getSlotLeaders)
    1534             : UNIMPLEMENTED(getStakeMinimumDelegation)
    1535             : UNIMPLEMENTED(getSupply)
    1536             : UNIMPLEMENTED(getTokenAccountBalance)
    1537             : UNIMPLEMENTED(getTokenAccountsByDelegate)
    1538             : UNIMPLEMENTED(getTokenAccountsByOwner)
    1539             : UNIMPLEMENTED(getTokenLargestAccounts)
    1540             : UNIMPLEMENTED(getTokenSupply)
    1541             : UNIMPLEMENTED(getTransaction)
    1542             : 
    1543             : static fd_http_server_response_t
    1544             : getTransactionCount( fd_rpc_tile_t * ctx,
    1545             :                      cJSON const *   id,
    1546           0 :                      cJSON const *   params ) {
    1547           0 :   fd_http_server_response_t response;
    1548           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 1, &response ) ) ) return response;
    1549             : 
    1550           0 :   ulong bank_idx = ULONG_MAX;
    1551           0 :   cJSON const * config = cJSON_GetArrayItem( params, 0 );
    1552           0 :   int config_valid = fd_rpc_validate_config( ctx, id, config, "struct CommitmentConfig",
    1553           0 :                                              1, /* has_commitment */
    1554           0 :                                              0, /* has_encoding */
    1555           0 :                                              0, /* has_data_slice */
    1556           0 :                                              1, /* has_min_context_slot */
    1557           0 :                                              &bank_idx,
    1558           0 :                                              NULL,
    1559           0 :                                              NULL,
    1560           0 :                                              NULL,
    1561           0 :                                              &response );
    1562           0 :   if( FD_UNLIKELY( !config_valid ) ) return response;
    1563             : 
    1564           0 :   bank_info_t * bank = &ctx->banks[ bank_idx ];
    1565           0 :   CSTR_JSON( id, id_cstr );
    1566           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":%lu,\"id\":%s}\n", bank->transaction_count, id_cstr );
    1567           0 : }
    1568             : 
    1569             : static fd_http_server_response_t
    1570             : getVersion( fd_rpc_tile_t * ctx,
    1571             :             cJSON const *   id,
    1572           0 :             cJSON const *   params ) {
    1573           0 :   fd_http_server_response_t response;
    1574           0 :   if( FD_UNLIKELY( !fd_rpc_validate_params( ctx, id, params, 0, 0, &response ) ) ) return response;
    1575             : 
    1576           0 :   CSTR_JSON( id, id_cstr );
    1577           0 :   return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"result\":{\"solana-core\":\"%s\",\"feature-set\":%u},\"id\":%s}\n", ctx->version_string, FD_FEATURE_SET_ID, id_cstr );
    1578           0 : }
    1579             : 
    1580             : UNIMPLEMENTED(getVoteAccounts) // TODO: Used by solana-exporter
    1581             : UNIMPLEMENTED(isBlockhashValid)
    1582             : UNIMPLEMENTED(minimumLedgerSlot) // TODO: Used by solana-exporter
    1583             : UNIMPLEMENTED(requestAirdrop)
    1584             : UNIMPLEMENTED(sendTransaction)
    1585             : UNIMPLEMENTED(simulateTransaction)
    1586             : 
    1587             : static fd_http_server_response_t
    1588           0 : rpc_http_request( fd_http_server_request_t const * request ) {
    1589           0 :   fd_rpc_tile_t * ctx = (fd_rpc_tile_t *)request->ctx;
    1590             : 
    1591           0 :   if( FD_UNLIKELY( request->method==FD_HTTP_SERVER_METHOD_GET && !strcmp( request->path, "/health" ) ) ) {
    1592           0 :     int health_status = _getHealth( ctx );
    1593             : 
    1594           0 :     switch( health_status ) {
    1595           0 :       case FD_RPC_HEALTH_STATUS_UNKNOWN: return PRINTF_JSON( ctx, "unknown" );
    1596           0 :       case FD_RPC_HEALTH_STATUS_BEHIND:  return PRINTF_JSON( ctx, "behind" );
    1597           0 :       case FD_RPC_HEALTH_STATUS_OK:      return PRINTF_JSON( ctx, "ok" );
    1598           0 :       default: FD_LOG_ERR(( "unknown health status" ));
    1599           0 :     }
    1600           0 :   }
    1601             : 
    1602           0 :   if( FD_UNLIKELY( request->method==FD_HTTP_SERVER_METHOD_GET && !strcmp( request->path, "/genesis.tar.bz2" ) ) ) {
    1603           0 :     if( FD_UNLIKELY( ctx->genesis_tar_bz_sz==ULONG_MAX ) ) return (fd_http_server_response_t){ .status = 404 };
    1604             : 
    1605           0 :     fd_http_server_response_t response = (fd_http_server_response_t){ .status = 200 };
    1606           0 :     fd_http_server_memcpy( ctx->http, ctx->genesis_tar_bz, ctx->genesis_tar_bz_sz );
    1607           0 :     FD_TEST( !fd_http_server_stage_body( ctx->http, &response ) );
    1608           0 :     return response;
    1609           0 :   }
    1610             : 
    1611           0 :   if( FD_UNLIKELY( request->method==FD_HTTP_SERVER_METHOD_GET ) ) {
    1612           0 :     return (fd_http_server_response_t){ .status = 404 };
    1613           0 :   }
    1614             : 
    1615           0 :   if( FD_UNLIKELY( request->method!=FD_HTTP_SERVER_METHOD_POST ) ) {
    1616           0 :     return (fd_http_server_response_t){ .status = 405 };
    1617           0 :   }
    1618             : 
    1619           0 :   const char * parse_end;
    1620           0 :   cJSON * json = cJSON_ParseWithLengthOpts( (char *)request->post.body, request->post.body_len, &parse_end, 0 );
    1621             : 
    1622           0 :   if( FD_UNLIKELY( cJSON_IsArray( json ) && cJSON_GetArraySize( json )==0UL ) ) {
    1623             :     /* A bug in Agave ¯\_(ツ)_/¯ */
    1624           0 :     cJSON_Delete( json );
    1625           0 :     return (fd_http_server_response_t){ .content_type = "application/json", .status = 200 };
    1626           0 :   }
    1627             : 
    1628           0 :   if( FD_UNLIKELY( !json || !cJSON_IsObject( json ) ) ) {
    1629           0 :     cJSON_Delete( json );
    1630           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32700,\"message\":\"Parse error\"},\"id\":null}\n" );
    1631           0 :   }
    1632           0 :   const cJSON * id = cJSON_GetObjectItemCaseSensitive( json, "id" );
    1633             : 
    1634           0 :   cJSON * item = json->child;
    1635           0 :   while( item ) {
    1636           0 :     if( FD_UNLIKELY( strcmp( item->string, "jsonrpc" ) && strcmp( item->string, "id" ) && strcmp( item->string, "method" ) && strcmp( item->string, "params" ) ) ) {
    1637           0 :       fd_http_server_response_t res;
    1638           0 :       if( FD_LIKELY( id ) ) {
    1639           0 :         CSTR_JSON( id, id_cstr );
    1640           0 :         res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":%s}\n", id_cstr );
    1641           0 :       } else {
    1642           0 :         res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":null}\n" );
    1643           0 :       }
    1644           0 :       cJSON_Delete( json );
    1645           0 :       return res;
    1646           0 :     }
    1647           0 :     item = item->next;
    1648           0 :   }
    1649             : 
    1650           0 :   if( FD_UNLIKELY( cJSON_HasObjectItem( json, "method") && !cJSON_HasObjectItem( json, "id") ) ) {
    1651             :     /* A bug in Agave ¯\_(ツ)_/¯ */
    1652           0 :     cJSON_Delete( json );
    1653           0 :     return (fd_http_server_response_t){ .content_type = "application/json", .status = 200 };
    1654           0 :   }
    1655             : 
    1656           0 :   const cJSON * jsonrpc = cJSON_GetObjectItemCaseSensitive( json, "jsonrpc" );
    1657           0 :   if( FD_UNLIKELY( !cJSON_HasObjectItem( json, "jsonrpc" ) && cJSON_HasObjectItem( json, "method" ) ) ) {
    1658           0 :     fd_http_server_response_t res;
    1659           0 :     if( FD_LIKELY( id ) ) {
    1660           0 :       CSTR_JSON( id, id_cstr );
    1661           0 :       res = PRINTF_JSON( ctx, "{\"error\":{\"code\":-32600,\"message\":\"Unsupported JSON-RPC protocol version\"},\"id\":%s}\n", id_cstr );
    1662           0 :     } else {
    1663           0 :       res = PRINTF_JSON( ctx, "{\"error\":{\"code\":-32600,\"message\":\"Unsupported JSON-RPC protocol version\"},\"id\":null}\n" );;
    1664           0 :     }
    1665           0 :     cJSON_Delete( json );
    1666           0 :     return res;
    1667           0 :   }
    1668             : 
    1669           0 :   if( FD_UNLIKELY( cJSON_IsObject( json ) && (!cJSON_HasObjectItem( json, "jsonrpc" ) || !cJSON_HasObjectItem( json, "method" )) ) ) {
    1670           0 :     fd_http_server_response_t res;
    1671           0 :     if( FD_LIKELY( id ) ) {
    1672           0 :       CSTR_JSON( id, id_cstr );
    1673           0 :       res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":%s}\n", id_cstr );
    1674           0 :     } else {
    1675           0 :       res = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":null}\n" );
    1676           0 :     }
    1677           0 :     cJSON_Delete( json );
    1678           0 :     return res;
    1679           0 :   }
    1680             : 
    1681           0 :   if( FD_UNLIKELY( !(id && fd_rpc_cjson_is_integer( id ) && id->valueint >= 0) && !cJSON_IsString( id ) && !cJSON_IsNull( id ) ) ) {
    1682           0 :     cJSON_Delete( json );
    1683           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32700,\"message\":\"Parse error\"},\"id\":null}\n" );
    1684           0 :   }
    1685             : 
    1686           0 :   if( FD_UNLIKELY( !cJSON_HasObjectItem( json, "jsonrpc" ) || cJSON_IsNull( jsonrpc ) ) ) {
    1687           0 :     CSTR_JSON( id, id_cstr );
    1688           0 :     cJSON_Delete( json );
    1689           0 :     return PRINTF_JSON( ctx, "{\"error\":{\"code\":-32600,\"message\":\"Unsupported JSON-RPC protocol version\"},\"id\":%s}\n", id_cstr );
    1690           0 :   }
    1691             : 
    1692           0 :   if( FD_UNLIKELY( !cJSON_IsString( jsonrpc ) || strcmp( jsonrpc->valuestring, "2.0" ) ) ) {
    1693           0 :     CSTR_JSON( id, id_cstr );
    1694           0 :     cJSON_Delete( json );
    1695           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":%s}\n", id_cstr );
    1696           0 :   }
    1697             : 
    1698           0 :   const cJSON * params = cJSON_GetObjectItemCaseSensitive( json, "params" );
    1699           0 :   fd_http_server_response_t response;
    1700             : 
    1701           0 :   const cJSON * _method = cJSON_GetObjectItemCaseSensitive( json, "method" );
    1702           0 :   if( FD_UNLIKELY( !cJSON_IsString( _method ) || _method->valuestring==NULL ) ) {
    1703           0 :     CSTR_JSON( id, id_cstr );
    1704           0 :     cJSON_Delete( json );
    1705           0 :     return PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":%s}\n", id_cstr );
    1706           0 :   }
    1707             : 
    1708           0 :   if( FD_LIKELY(      !strcmp( _method->valuestring, "getAccountInfo"                    ) ) ) response = getAccountInfo( ctx, id, params );
    1709           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getBalance"                        ) ) ) response = getBalance( ctx, id, params );
    1710           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getBlock"                          ) ) ) response = getBlock( ctx, id, params );
    1711           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getBlockCommitment"                ) ) ) response = getBlockCommitment( ctx, id, params );
    1712           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getBlockHeight"                    ) ) ) response = getBlockHeight( ctx, id, params );
    1713           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getBlockProduction"                ) ) ) response = getBlockProduction( ctx, id, params );
    1714           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getBlocks"                         ) ) ) response = getBlocks( ctx, id, params );
    1715           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getBlocksWithLimit"                ) ) ) response = getBlocksWithLimit( ctx, id, params );
    1716           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getBlockTime"                      ) ) ) response = getBlockTime( ctx, id, params );
    1717           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getClusterNodes"                   ) ) ) response = getClusterNodes( ctx, id, params );
    1718           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getEpochInfo"                      ) ) ) response = getEpochInfo( ctx, id, params );
    1719           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getEpochSchedule"                  ) ) ) response = getEpochSchedule( ctx, id, params );
    1720           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getFeeForMessage"                  ) ) ) response = getFeeForMessage( ctx, id, params );
    1721           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getFirstAvailableBlock"            ) ) ) response = getFirstAvailableBlock( ctx, id, params );
    1722           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getGenesisHash"                    ) ) ) response = getGenesisHash( ctx, id, params );
    1723           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getHealth"                         ) ) ) response = getHealth( ctx, id, params );
    1724           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getHighestSnapshotSlot"            ) ) ) response = getHighestSnapshotSlot( ctx, id, params );
    1725           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getIdentity"                       ) ) ) response = getIdentity( ctx, id, params );
    1726           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getInflationGovernor"              ) ) ) response = getInflationGovernor( ctx, id, params );
    1727           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getInflationRate"                  ) ) ) response = getInflationRate( ctx, id, params );
    1728           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getInflationReward"                ) ) ) response = getInflationReward( ctx, id, params );
    1729           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getLargestAccounts"                ) ) ) response = getLargestAccounts( ctx, id, params );
    1730           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getLatestBlockhash"                ) ) ) response = getLatestBlockhash( ctx, id, params );
    1731           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getLeaderSchedule"                 ) ) ) response = getLeaderSchedule( ctx, id, params );
    1732           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getMaxRetransmitSlot"              ) ) ) response = getMaxRetransmitSlot( ctx, id, params );
    1733           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getMaxShredInsertSlot"             ) ) ) response = getMaxShredInsertSlot( ctx, id, params );
    1734           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getMinimumBalanceForRentExemption" ) ) ) response = getMinimumBalanceForRentExemption( ctx, id, params );
    1735           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getMultipleAccounts"               ) ) ) response = getMultipleAccounts( ctx, id, params );
    1736           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getProgramAccounts"                ) ) ) response = getProgramAccounts( ctx, id, params );
    1737           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getRecentPerformanceSamples"       ) ) ) response = getRecentPerformanceSamples( ctx, id, params );
    1738           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getRecentPrioritizationFees"       ) ) ) response = getRecentPrioritizationFees( ctx, id, params );
    1739           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getSignaturesForAddress"           ) ) ) response = getSignaturesForAddress( ctx, id, params );
    1740           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getSignatureStatuses"              ) ) ) response = getSignatureStatuses( ctx, id, params );
    1741           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getSlot"                           ) ) ) response = getSlot( ctx, id, params );
    1742           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getSlotLeader"                     ) ) ) response = getSlotLeader( ctx, id, params );
    1743           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getSlotLeaders"                    ) ) ) response = getSlotLeaders( ctx, id, params );
    1744           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getStakeMinimumDelegation"         ) ) ) response = getStakeMinimumDelegation( ctx, id, params );
    1745           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getSupply"                         ) ) ) response = getSupply( ctx, id, params );
    1746           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getTokenAccountBalance"            ) ) ) response = getTokenAccountBalance( ctx, id, params );
    1747           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getTokenAccountsByDelegate"        ) ) ) response = getTokenAccountsByDelegate( ctx, id, params );
    1748           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getTokenAccountsByOwner"           ) ) ) response = getTokenAccountsByOwner( ctx, id, params );
    1749           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getTokenLargestAccounts"           ) ) ) response = getTokenLargestAccounts( ctx, id, params );
    1750           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getTokenSupply"                    ) ) ) response = getTokenSupply( ctx, id, params );
    1751           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getTransaction"                    ) ) ) response = getTransaction( ctx, id, params );
    1752           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getTransactionCount"               ) ) ) response = getTransactionCount( ctx, id, params );
    1753           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getVersion"                        ) ) ) response = getVersion( ctx, id, params );
    1754           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "getVoteAccounts"                   ) ) ) response = getVoteAccounts( ctx, id, params );
    1755           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "isBlockhashValid"                  ) ) ) response = isBlockhashValid( ctx, id, params );
    1756           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "minimumLedgerSlot"                 ) ) ) response = minimumLedgerSlot( ctx, id, params );
    1757           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "requestAirdrop"                    ) ) ) response = requestAirdrop( ctx, id, params );
    1758           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "sendTransaction"                   ) ) ) response = sendTransaction( ctx, id, params );
    1759           0 :   else if( FD_LIKELY( !strcmp( _method->valuestring, "simulateTransaction"               ) ) ) response = simulateTransaction( ctx, id, params );
    1760           0 :   else {
    1761           0 :     CSTR_JSON( id, id_cstr );
    1762           0 :     response = PRINTF_JSON( ctx, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"Method not found\"},\"id\":%s}\n", id_cstr );
    1763           0 :   }
    1764             : 
    1765           0 :   cJSON_Delete( json );
    1766           0 :   return response;
    1767           0 : }
    1768             : 
    1769             : static void
    1770             : privileged_init( fd_topo_t *      topo,
    1771           0 :                  fd_topo_tile_t * tile ) {
    1772           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
    1773             : 
    1774           0 :   fd_http_server_params_t http_params = derive_http_params( tile );
    1775             : 
    1776           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
    1777           0 :   fd_rpc_tile_t * ctx      = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_rpc_tile_t ), sizeof( fd_rpc_tile_t ) );
    1778           0 :   fd_http_server_t * _http = FD_SCRATCH_ALLOC_APPEND( l, fd_http_server_align(),   fd_http_server_footprint( http_params ) );
    1779             : 
    1780           0 :   if( FD_UNLIKELY( !strcmp( tile->rpc.identity_key_path, "" ) ) )
    1781           0 :     FD_LOG_ERR(( "identity_key_path not set" ));
    1782             : 
    1783           0 :   const uchar * identity_key = fd_keyload_load( tile->rpc.identity_key_path, /* pubkey only: */ 1 );
    1784           0 :   fd_memcpy( ctx->identity_pubkey, identity_key, 32UL );
    1785             : 
    1786           0 :   fd_http_server_callbacks_t callbacks = {
    1787           0 :     .request = rpc_http_request,
    1788           0 :   };
    1789           0 :   ctx->http = fd_http_server_join( fd_http_server_new( _http, http_params, callbacks, ctx ) );
    1790           0 :   fd_http_server_listen( ctx->http, tile->rpc.listen_addr, tile->rpc.listen_port );
    1791           0 :   FD_LOG_NOTICE(( "rpc server listening at http://" FD_IP4_ADDR_FMT ":%u", FD_IP4_ADDR_FMT_ARGS( tile->rpc.listen_addr ), tile->rpc.listen_port ));
    1792           0 : }
    1793             : 
    1794             : extern char const fdctl_version_string[];
    1795             : 
    1796             : static inline fd_rpc_out_t
    1797             : out1( fd_topo_t const *      topo,
    1798             :       fd_topo_tile_t const * tile,
    1799           0 :       char const *           name ) {
    1800           0 :   ulong idx = ULONG_MAX;
    1801             : 
    1802           0 :   for( ulong i=0UL; i<tile->out_cnt; i++ ) {
    1803           0 :     fd_topo_link_t const * link = &topo->links[ tile->out_link_id[ i ] ];
    1804           0 :     if( !strcmp( link->name, name ) ) {
    1805           0 :       if( FD_UNLIKELY( idx!=ULONG_MAX ) ) FD_LOG_ERR(( "tile %s:%lu had multiple output links named %s but expected one", tile->name, tile->kind_id, name ));
    1806           0 :       idx = i;
    1807           0 :     }
    1808           0 :   }
    1809             : 
    1810           0 :   if( FD_UNLIKELY( idx==ULONG_MAX ) ) return (fd_rpc_out_t){ .idx = ULONG_MAX, .mem = NULL, .chunk0 = 0, .wmark = 0, .chunk = 0 };
    1811             : 
    1812             : 
    1813           0 :   ulong mtu = topo->links[ tile->out_link_id[ idx ] ].mtu;
    1814           0 :   if( FD_UNLIKELY( mtu==0UL ) ) return (fd_rpc_out_t){ .idx = idx, .mem = NULL, .chunk0 = ULONG_MAX, .wmark = ULONG_MAX, .chunk = ULONG_MAX };
    1815             : 
    1816           0 :   void * mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ idx ] ].dcache_obj_id ].wksp_id ].wksp;
    1817           0 :   ulong chunk0 = fd_dcache_compact_chunk0( mem, topo->links[ tile->out_link_id[ idx ] ].dcache );
    1818           0 :   ulong wmark  = fd_dcache_compact_wmark ( mem, topo->links[ tile->out_link_id[ idx ] ].dcache, topo->links[ tile->out_link_id[ idx ] ].mtu );
    1819             : 
    1820           0 :   return (fd_rpc_out_t){ .idx = idx, .mem = mem, .chunk0 = chunk0, .wmark = wmark, .chunk = chunk0 };
    1821           0 : }
    1822             : 
    1823             : static void
    1824             : unprivileged_init( fd_topo_t *      topo,
    1825           0 :                    fd_topo_tile_t * tile ) {
    1826           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
    1827             : 
    1828           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
    1829           0 :   fd_rpc_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_rpc_tile_t ), sizeof( fd_rpc_tile_t )                                );
    1830           0 :                         FD_SCRATCH_ALLOC_APPEND( l, fd_http_server_align(),   fd_http_server_footprint( derive_http_params( tile ) ) );
    1831           0 :   void * _alloc       = FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(),         fd_alloc_footprint()                                   );
    1832           0 : #if FD_HAS_BZIP2
    1833           0 :   void * _bz2_alloc   = FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(),         fd_alloc_footprint()                                   );
    1834           0 : #endif
    1835           0 :   void * _banks       = FD_SCRATCH_ALLOC_APPEND( l, alignof(bank_info_t),     tile->rpc.max_live_slots*sizeof(bank_info_t)           );
    1836           0 :   void * _nodes_dlist = FD_SCRATCH_ALLOC_APPEND( l, fd_rpc_cluster_node_dlist_align(), fd_rpc_cluster_node_dlist_footprint() );
    1837             : 
    1838           0 :   fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( _alloc, 1UL ), 1UL );
    1839           0 :   FD_TEST( alloc );
    1840           0 :   cJSON_alloc_install( alloc );
    1841             : 
    1842           0 :   ctx->delay_startup = tile->rpc.delay_startup;
    1843           0 :   ctx->keyswitch = fd_keyswitch_join( fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id ) );
    1844           0 :   FD_TEST( ctx->keyswitch );
    1845             : 
    1846           0 :   for( ulong i=0UL; i<FD_CONTACT_INFO_TABLE_SIZE; i++ ) ctx->cluster_nodes[ i ].valid = 0;
    1847             : 
    1848           0 : # if FD_HAS_BZIP2
    1849           0 :   ctx->bz2_alloc = fd_alloc_join( fd_alloc_new( _bz2_alloc, 1UL ), 1UL );
    1850           0 :   FD_TEST( ctx->bz2_alloc );
    1851           0 : # endif
    1852             : 
    1853           0 :   ctx->next_poll_deadline = fd_tickcount();
    1854             : 
    1855           0 :   ctx->cluster_confirmed_slot = ULONG_MAX;
    1856           0 :   ctx->genesis_tar_bz_sz = ULONG_MAX;
    1857             : 
    1858           0 :   ctx->processed_idx = ULONG_MAX;
    1859           0 :   ctx->confirmed_idx = ULONG_MAX;
    1860           0 :   ctx->finalized_idx = ULONG_MAX;
    1861             : 
    1862           0 :   ctx->cluster_nodes_dlist = fd_rpc_cluster_node_dlist_join( fd_rpc_cluster_node_dlist_new( _nodes_dlist ) );
    1863           0 :   ctx->banks = _banks;
    1864           0 :   ctx->max_live_slots = tile->rpc.max_live_slots;
    1865           0 :   for( ulong i=0UL; i<ctx->max_live_slots; i++ ) ctx->banks[ i ].slot = ULONG_MAX;
    1866             : 
    1867           0 :   FD_TEST( fd_cstr_printf_check( ctx->version_string, sizeof( ctx->version_string ), NULL, "%s", fdctl_version_string ) );
    1868             : 
    1869           0 :   FD_TEST( tile->in_cnt<=sizeof( ctx->in )/sizeof( ctx->in[ 0 ] ) );
    1870           0 :   for( ulong i=0; i<tile->in_cnt; i++ ) {
    1871           0 :     fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
    1872           0 :     fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
    1873             : 
    1874           0 :     ctx->in[ i ].mem    = link_wksp->wksp;
    1875           0 :     ctx->in[ i ].chunk0 = fd_dcache_compact_chunk0( ctx->in[ i ].mem, link->dcache );
    1876           0 :     ctx->in[ i ].wmark  = fd_dcache_compact_wmark ( ctx->in[ i ].mem, link->dcache, link->mtu );
    1877           0 :     ctx->in[ i ].mtu    = link->mtu;
    1878             : 
    1879           0 :     if     ( FD_LIKELY( !strcmp( link->name, "replay_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_REPLAY;
    1880           0 :     else if( FD_LIKELY( !strcmp( link->name, "genesi_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_GENESI;
    1881           0 :     else if( FD_LIKELY( !strcmp( link->name, "gossip_out" ) ) ) ctx->in_kind[ i ] = IN_KIND_GOSSIP_OUT;
    1882           0 :     else FD_LOG_ERR(( "unexpected link name %s", link->name ));
    1883           0 :   }
    1884             : 
    1885           0 :   *ctx->replay_out = out1( topo, tile, "rpc_replay" ); FD_TEST( ctx->replay_out->idx!=ULONG_MAX );
    1886             : 
    1887           0 :   fd_accdb_init_from_topo( ctx->accdb, topo, tile, tile->rpc.accdb_max_depth );
    1888             : 
    1889           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
    1890           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
    1891           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
    1892           0 : }
    1893             : 
    1894             : static ulong
    1895             : populate_allowed_seccomp( fd_topo_t const *      topo,
    1896             :                           fd_topo_tile_t const * tile,
    1897             :                           ulong                  out_cnt,
    1898           0 :                           struct sock_filter *   out ) {
    1899           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
    1900           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
    1901           0 :   fd_rpc_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_rpc_tile_t ), sizeof( fd_rpc_tile_t ) );
    1902             : 
    1903           0 :   populate_sock_filter_policy_fd_rpc_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)fd_http_server_fd( ctx->http ) );
    1904           0 :   return sock_filter_policy_fd_rpc_tile_instr_cnt;
    1905           0 : }
    1906             : 
    1907             : static ulong
    1908             : populate_allowed_fds( fd_topo_t const *      topo,
    1909             :                       fd_topo_tile_t const * tile,
    1910             :                       ulong                  out_fds_cnt,
    1911           0 :                       int *                  out_fds ) {
    1912           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
    1913           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
    1914           0 :   fd_rpc_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_rpc_tile_t ), sizeof( fd_rpc_tile_t ) );
    1915             : 
    1916           0 :   if( FD_UNLIKELY( out_fds_cnt<3UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
    1917             : 
    1918           0 :   ulong out_cnt = 0UL;
    1919           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
    1920           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
    1921           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
    1922           0 :   out_fds[ out_cnt++ ] = fd_http_server_fd( ctx->http ); /* rpc listen socket */
    1923           0 :   return out_cnt;
    1924           0 : }
    1925             : 
    1926             : static ulong
    1927             : rlimit_file_cnt( fd_topo_t const *      topo FD_PARAM_UNUSED,
    1928           0 :                  fd_topo_tile_t const * tile ) {
    1929             :   /* pipefd, socket, stderr, logfile, and one spare for new accept() connections */
    1930           0 :   ulong base = 5UL;
    1931           0 :   return base+tile->rpc.max_http_connections;
    1932           0 : }
    1933             : 
    1934           0 : #define STEM_BURST (1UL)
    1935             : 
    1936             : /* The default STEM_LAZY is based on cr_max, which is the minimum depth
    1937             :    across all output links that have at least one reliable consumer.
    1938             :    RPC has one tiny output link used to release banks, with a
    1939             :    significantly slower line rate than assumed in the formula for the
    1940             :    default STEM_LAZY value.
    1941             : 
    1942             :    Instead, lazy just needs to be frequent enough to relinquish credits
    1943             :    to upstream producers faster than they are exhausted. 384us is a
    1944             :    reasonable default used in many other non-critical tiles. */
    1945           0 : #define STEM_LAZY  (128L*3000L)
    1946             : 
    1947           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_rpc_tile_t
    1948           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_rpc_tile_t)
    1949             : 
    1950           0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
    1951           0 : #define STEM_CALLBACK_BEFORE_CREDIT       before_credit
    1952           0 : #define STEM_CALLBACK_BEFORE_FRAG         before_frag
    1953           0 : #define STEM_CALLBACK_RETURNABLE_FRAG     returnable_frag
    1954             : 
    1955             : #include "../../disco/stem/fd_stem.c"
    1956             : 
    1957             : #ifndef FD_TILE_TEST
    1958             : fd_topo_run_tile_t fd_tile_rpc = {
    1959             :   .name                     = "rpc",
    1960             :   .rlimit_file_cnt_fn       = rlimit_file_cnt,
    1961             :   .populate_allowed_seccomp = populate_allowed_seccomp,
    1962             :   .populate_allowed_fds     = populate_allowed_fds,
    1963             :   .scratch_align            = scratch_align,
    1964             :   .scratch_footprint        = scratch_footprint,
    1965             :   .loose_footprint          = loose_footprint,
    1966             :   .privileged_init          = privileged_init,
    1967             :   .unprivileged_init        = unprivileged_init,
    1968             :   .run                      = stem_run,
    1969             : };
    1970             : #endif

Generated by: LCOV version 1.14