diff --git a/config/extra/with-rpcserver.mk b/config/extra/with-rpcserver.mk new file mode 100644 index 0000000000..3a25227280 --- /dev/null +++ b/config/extra/with-rpcserver.mk @@ -0,0 +1 @@ +FD_WITH_RPCSERVER:=1 diff --git a/src/app/rpcserver/Local.mk b/src/app/rpcserver/Local.mk new file mode 100644 index 0000000000..55a0a50d12 --- /dev/null +++ b/src/app/rpcserver/Local.mk @@ -0,0 +1,10 @@ +ifdef FD_WITH_RPCSERVER + +LDFLAGS+=-Wl,--push-state,-Bstatic -lmicrohttpd -Wl,--pop-state -lgmp -lcrypto + +$(call make-bin,fd_rpcserver,main fd_block_to_json fd_methods fd_rpc_service fd_webserver json_lex keywords,fd_flamenco fd_ballet fd_reedsol fd_disco fd_funk fd_shred fd_tango fd_choreo fd_waltz fd_util, $(SECP256K1_LIBS)) + +$(call make-unit-test,test_rpc_keywords,test_keywords keywords,fd_util) +$(call fuzz-test,fuzz_json_lex,fuzz_json_lex,fd_util) + +endif diff --git a/src/app/rpcserver/deps.sh b/src/app/rpcserver/deps.sh new file mode 100755 index 0000000000..433264bb20 --- /dev/null +++ b/src/app/rpcserver/deps.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Install prefix +cd ../../.. +PREFIX="$(pwd)/opt" + +checkout_gnuweb () { + # Skip if dir already exists + if [[ -d ./opt/gnuweb/"$1" ]]; then + echo "[~] Skipping $1 fetch as \"$(pwd)/opt/gnuweb/$1\" already exists" + else + echo "[+] Cloning $1 from $2/$3.tar.gz" + curl -o - -L "$2/$3.tar.gz" | gunzip | tar xf - -C ./opt/gnuweb + mv ./opt/gnuweb/$3 ./opt/gnuweb/$1 + echo + fi +} + +fetch () { + mkdir -pv ./opt/gnuweb + checkout_gnuweb libmicrohttpd https://ftp.gnu.org/gnu/libmicrohttpd/ "libmicrohttpd-0.9.77" +} + +install_libmicrohttpd () { + cd ./opt/gnuweb/libmicrohttpd/ + ./configure \ + --prefix="$PREFIX" \ + --disable-https \ + --disable-curl \ + --disable-dauth \ + --with-pic + make -j + make install + + echo "[+] Successfully installed libmicrohttpd" +} + +install () { + CC="$(command -v gcc)" + cc="$CC" + export CC + export cc + + ( install_libmicrohttpd ) + + echo "[~] Done!" +} + +ACTION=0 +while [[ $# -gt 0 ]]; do + case $1 in + fetch) + shift + fetch + ACTION=1 + ;; + install) + shift + install + ACTION=1 + ;; + *) + echo "Unknown command: $1" >&2 + exit 1 + ;; + esac +done + +if [[ $ACTION == 0 ]]; then + fetch + install +fi diff --git a/src/app/rpcserver/fd_block_to_json.c b/src/app/rpcserver/fd_block_to_json.c new file mode 100644 index 0000000000..2781cd2a7f --- /dev/null +++ b/src/app/rpcserver/fd_block_to_json.c @@ -0,0 +1,513 @@ +#include +#include +#include "../../util/fd_util.h" +#include "../../flamenco/nanopb/pb_decode.h" +#include "fd_webserver.h" +#include "../../ballet/txn/fd_txn.h" +#include "../../ballet/block/fd_microblock.h" +#include "../../ballet/base58/fd_base58.h" +#include "../../flamenco/types/fd_types.h" +#include "../../flamenco/types/fd_solana_block.pb.h" +#include "../../flamenco/runtime/fd_blockstore.h" +#include "fd_block_to_json.h" + +#define EMIT_SIMPLE(_str_) fd_textstream_append(ts, _str_, sizeof(_str_)-1) + +void fd_tokenbalance_to_json( fd_textstream_t * ts, struct _fd_solblock_TokenBalance * b ) { + fd_textstream_sprintf(ts, "{\"accountIndex\":%u,\"mint\":\"%s\",\"owner\":\"%s\",\"programId\":\"%s\",\"uiTokenAmount\":{", + b->account_index, b->mint, b->owner, b->program_id); + fd_textstream_sprintf(ts, "\"amount\":\"%s\",", b->ui_token_amount.amount); + int dec; + if (b->ui_token_amount.has_decimals) { + fd_textstream_sprintf(ts, "\"decimals\":%u,", b->ui_token_amount.decimals); + dec = (int)b->ui_token_amount.decimals; + } else + dec = 0; + if (b->ui_token_amount.has_ui_amount) + fd_textstream_sprintf(ts, "\"uiAmount\":%.*f,", dec, b->ui_token_amount.ui_amount); + fd_textstream_sprintf(ts, "\"uiAmountString\":\"%s\"}}", b->ui_token_amount.ui_amount_string); +} + +void fd_error_to_json( fd_textstream_t * ts, + const uchar* bytes, + ulong size ) { + /* I worked this out by brute force examination of actual cases */ + + const uchar* orig_bytes = bytes; + ulong orig_size = size; + +#define INSTRUCTION_ERROR 8 + if (size < sizeof(uint) || *(const uint*)bytes != INSTRUCTION_ERROR) /* Always the same? */ + goto dump_as_hex; + bytes += sizeof(uint); + size -= sizeof(uint); + + if (size < 1) + goto dump_as_hex; + uint index = *(bytes++); /* Instruction index */ + size--; + + if (size < sizeof(uint)) + goto dump_as_hex; + uint cnum = *(const uint*)bytes; + bytes += sizeof(uint); + size -= sizeof(uint); + + switch (cnum) { + case 25: { /* "Custom" */ + if (size < sizeof(uint)) + goto dump_as_hex; + uint code = *(const uint*)bytes; /* Custom code? */ + fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,{\"Custom\":%u}]}", index, code); + return; + } + + case 0: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"GenericError\"]}", index); return; + case 1: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"InvalidArgument\"]}", index); return; + case 2: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"InvalidInstructionData\"]}", index); return; + case 3: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"InvalidAccountData\"]}", index); return; + case 4: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"AccountDataTooSmall\"]}", index); return; + case 5: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"InsufficientFunds\"]}", index); return; + case 6: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"IncorrectProgramId\"]}", index); return; + case 7: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"MissingRequiredSignature\"]}", index); return; + case 8: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"AccountAlreadyInitialized\"]}", index); return; + case 9: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"UninitializedAccount\"]}", index); return; + case 10: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"UnbalancedInstruction\"]}", index); return; + case 11: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ModifiedProgramId\"]}", index); return; + case 12: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ExternalAccountLamportSpend\"]}", index); return; + case 13: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ExternalAccountDataModified\"]}", index); return; + case 14: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ReadonlyLamportChange\"]}", index); return; + case 15: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ReadonlyDataModified\"]}", index); return; + case 16: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"DuplicateAccountIndex\"]}", index); return; + case 17: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ExecutableModified\"]}", index); return; + case 18: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"RentEpochModified\"]}", index); return; + case 19: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"NotEnoughAccountKeys\"]}", index); return; + case 20: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"AccountDataSizeChanged\"]}", index); return; + case 21: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"AccountNotExecutable\"]}", index); return; + case 22: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"AccountBorrowFailed\"]}", index); return; + case 23: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"AccountBorrowOutstanding\"]}", index); return; + case 24: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"DuplicateAccountOutOfSync\"]}", index); return; + case 26: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"InvalidError\"]}", index); return; + case 27: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ExecutableDataModified\"]}", index); return; + case 28: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ExecutableLamportChange\"]}", index); return; + case 29: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ExecutableAccountNotRentExempt\"]}", index); return; + case 30: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"UnsupportedProgramId\"]}", index); return; + case 31: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"CallDepth\"]}", index); return; + case 32: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"MissingAccount\"]}", index); return; + case 33: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ReentrancyNotAllowed\"]}", index); return; + case 34: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"MaxSeedLengthExceeded\"]}", index); return; + case 35: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"InvalidSeeds\"]}", index); return; + case 36: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"InvalidRealloc\"]}", index); return; + case 37: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ComputationalBudgetExceeded\"]}", index); return; + case 38: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"PrivilegeEscalation\"]}", index); return; + case 39: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ProgramEnvironmentSetupFailure\"]}", index); return; + case 40: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ProgramFailedToComplete\"]}", index); return; + case 41: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ProgramFailedToCompile\"]}", index); return; + case 42: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"Immutable\"]}", index); return; + case 43: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"IncorrectAuthority\"]}", index); return; + case 44: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"BorshIoError(String::new())\"]}", index); return; + case 45: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"AccountNotRentExempt\"]}", index); return; + case 46: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"InvalidAccountOwner\"]}", index); return; + case 47: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"ArithmeticOverflow\"]}", index); return; + case 48: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"UnsupportedSysvar\"]}", index); return; + case 49: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"IllegalOwner\"]}", index); return; + case 50: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"MaxAccountsDataSizeExceeded\"]}", index); return; + case 51: fd_textstream_sprintf(ts, "{\"InstructionError\":[%u,\"MaxAccountsExceeded\"]}", index); return; + } + + dump_as_hex: + EMIT_SIMPLE("\""); + fd_textstream_encode_hex(ts, orig_bytes, orig_size); + EMIT_SIMPLE("\""); +} + +void fd_inner_instructions_to_json( fd_textstream_t * ts, + struct _fd_solblock_InnerInstructions * insts ) { + fd_textstream_sprintf(ts, "{\"index\":%u,\"instructions\":[", insts->index); + for ( pb_size_t i = 0; i < insts->instructions_count; ++i ) { + struct _fd_solblock_InnerInstruction * inst = insts->instructions + i; + fd_textstream_sprintf(ts, "%s{\"data\":\"", (i == 0 ? "" : ",")); + fd_textstream_encode_base58(ts, inst->data->bytes, inst->data->size); + fd_textstream_sprintf(ts, "\",\"programIdIndex:\":%u}", inst->program_id_index); + } + EMIT_SIMPLE("]}"); +} + +int fd_txn_meta_to_json( fd_textstream_t * ts, + const void * meta_raw, + ulong meta_raw_sz ) { + fd_solblock_TransactionStatusMeta txn_status = {0}; + pb_istream_t stream = pb_istream_from_buffer( meta_raw, meta_raw_sz ); + if( FD_UNLIKELY( !pb_decode( &stream, fd_solblock_TransactionStatusMeta_fields, &txn_status ) ) ) { + FD_LOG_ERR(( "failed to decode txn status: %s", PB_GET_ERROR( &stream ) )); + } + + EMIT_SIMPLE("\"meta\":{"); + if (txn_status.has_compute_units_consumed) + fd_textstream_sprintf(ts, "\"computeUnitsConsumed\":%lu,", txn_status.compute_units_consumed); + EMIT_SIMPLE("\"err\":"); + if (txn_status.has_err) + fd_error_to_json(ts, txn_status.err.err->bytes, txn_status.err.err->size); + else + EMIT_SIMPLE("null"); + fd_textstream_sprintf(ts, ",\"fee\":%lu,\"innerInstructions\":[", txn_status.fee); + if (!txn_status.inner_instructions_none) { + for (pb_size_t i = 0; i < txn_status.inner_instructions_count; ++i) { + if ( i > 0 ) EMIT_SIMPLE(","); + fd_inner_instructions_to_json(ts, txn_status.inner_instructions + i); + } + } + EMIT_SIMPLE("],\"loadedAddresses\":{\"readonly\":["); + for (pb_size_t i = 0; i < txn_status.loaded_readonly_addresses_count; ++i) { + pb_bytes_array_t * ba = txn_status.loaded_readonly_addresses[i]; + if (ba->size == 32) { + char buf32[FD_BASE58_ENCODED_32_SZ]; + fd_base58_encode_32(ba->bytes, NULL, buf32); + fd_textstream_sprintf(ts, "%s\"%s\"", (i == 0 ? "" : ","), buf32); + } else + fd_textstream_sprintf(ts, "%s\"\"", (i == 0 ? "" : ",")); + } + EMIT_SIMPLE("],\"writable\":["); + for (pb_size_t i = 0; i < txn_status.loaded_writable_addresses_count; ++i) { + pb_bytes_array_t * ba = txn_status.loaded_writable_addresses[i]; + if (ba->size == 32) { + char buf32[FD_BASE58_ENCODED_32_SZ]; + fd_base58_encode_32(ba->bytes, NULL, buf32); + fd_textstream_sprintf(ts, "%s\"%s\"", (i == 0 ? "" : ","), buf32); + } else + fd_textstream_sprintf(ts, "%s\"\"", (i == 0 ? "" : ",")); + } + EMIT_SIMPLE("]},\"logMessages\":["); + for (pb_size_t i = 0; i < txn_status.log_messages_count; ++i) + fd_textstream_sprintf(ts, "%s\"%s\"", (i == 0 ? "" : ","), txn_status.log_messages[i]); + EMIT_SIMPLE("],\"postBalances\":["); + for (pb_size_t i = 0; i < txn_status.post_balances_count; ++i) + fd_textstream_sprintf(ts, "%s%lu", (i == 0 ? "" : ","), txn_status.post_balances[i]); + EMIT_SIMPLE("],\"postTokenBalances\":["); + for (pb_size_t i = 0; i < txn_status.post_token_balances_count; ++i) { + if (i > 0) EMIT_SIMPLE(","); + fd_tokenbalance_to_json(ts, txn_status.post_token_balances + i); + } + EMIT_SIMPLE("],\"preBalances\":["); + for (pb_size_t i = 0; i < txn_status.pre_balances_count; ++i) + fd_textstream_sprintf(ts, "%s%lu", (i == 0 ? "" : ","), txn_status.pre_balances[i]); + EMIT_SIMPLE("],\"preTokenBalances\":["); + for (pb_size_t i = 0; i < txn_status.pre_token_balances_count; ++i) { + if (i > 0) EMIT_SIMPLE(","); + fd_tokenbalance_to_json(ts, txn_status.pre_token_balances + i); + } + EMIT_SIMPLE("],\"rewards\":["); + EMIT_SIMPLE("],\"status\":{\"Ok\":null}},"); + + pb_release( fd_solblock_TransactionStatusMeta_fields, &txn_status ); + + return 0; +} + +int fd_txn_to_json_full( fd_textstream_t * ts, + fd_txn_t* txn, + const uchar* raw, + fd_rpc_encoding_t encoding, + long maxvers, + int rewards ) { + (void)encoding; + (void)maxvers; + (void)rewards; + + EMIT_SIMPLE("\"transaction\":{\"message\":{\"accountKeys\":["); + + ushort acct_cnt = txn->acct_addr_cnt; + const fd_pubkey_t * accts = (const fd_pubkey_t *)(raw + txn->acct_addr_off); + char buf32[FD_BASE58_ENCODED_32_SZ]; + for (ushort idx = 0; idx < acct_cnt; idx++) { + fd_base58_encode_32(accts[idx].uc, NULL, buf32); + fd_textstream_sprintf(ts, "%s\"%s\"", (idx == 0 ? "" : ","), buf32); + } + + fd_textstream_sprintf(ts, "],\"header\":{\"numReadonlySignedAccounts\":%u,\"numReadonlyUnsignedAccounts\":%u,\"numRequiredSignatures\":%u},\"instructions\":[", + (uint)txn->readonly_signed_cnt, (uint)txn->readonly_unsigned_cnt, (uint)txn->signature_cnt); + + ushort instr_cnt = txn->instr_cnt; + for (ushort idx = 0; idx < instr_cnt; idx++) { + fd_textstream_sprintf(ts, "%s{\"accounts\":[", (idx == 0 ? "" : ",")); + + fd_txn_instr_t * instr = &txn->instr[idx]; + const uchar * instr_acc_idxs = raw + instr->acct_off; + for (ushort j = 0; j < instr->acct_cnt; j++) + fd_textstream_sprintf(ts, "%s%u", (j == 0 ? "" : ","), (uint)instr_acc_idxs[j]); + + EMIT_SIMPLE("],\"data\":\""); + fd_textstream_encode_base58(ts, raw + instr->data_off, instr->data_sz); + + fd_textstream_sprintf(ts, "\",\"programIdIndex\":%u,\"stackHeight\":null}", (uint)instr->program_id); + } + + const fd_hash_t * recent = (const fd_hash_t *)(raw + txn->recent_blockhash_off); + fd_base58_encode_32(recent->uc, NULL, buf32); + fd_textstream_sprintf(ts, "],\"recentBlockhash\":\"%s\"},\"signatures\":[", buf32); + + fd_ed25519_sig_t const * sigs = (fd_ed25519_sig_t const *)(raw + txn->signature_off); + for ( uchar j = 0; j < txn->signature_cnt; j++ ) { + char buf64[FD_BASE58_ENCODED_64_SZ]; + fd_base58_encode_64((const uchar*)&sigs[j], NULL, buf64); + fd_textstream_sprintf(ts, "%s\"%s\"", (j == 0 ? "" : ","), buf64); + } + + const char* vers; + switch (txn->transaction_version) { + case FD_TXN_VLEGACY: vers = "\"legacy\""; break; + case FD_TXN_V0: vers = "0"; break; + default: vers = "\"?\""; break; + } + fd_textstream_sprintf(ts, "]},\"version\":%s", vers); + + return 0; +} +int fd_txn_to_json_accts( fd_textstream_t * ts, + fd_txn_t* txn, + const uchar* raw, + fd_rpc_encoding_t encoding, + long maxvers, + int rewards ) { + (void)encoding; + (void)maxvers; + (void)rewards; + + EMIT_SIMPLE("\"transaction\":{\"accountKeys\":["); + + ushort acct_cnt = txn->acct_addr_cnt; + const fd_pubkey_t * accts = (const fd_pubkey_t *)(raw + txn->acct_addr_off); + char buf32[FD_BASE58_ENCODED_32_SZ]; + for (ushort idx = 0; idx < acct_cnt; idx++) { + fd_base58_encode_32(accts[idx].uc, NULL, buf32); + bool signer = (idx < txn->signature_cnt); + bool writable = ((idx < txn->signature_cnt - txn->readonly_signed_cnt) || + ((idx >= txn->signature_cnt) && (idx < acct_cnt - txn->readonly_unsigned_cnt))); + fd_textstream_sprintf(ts, "%s{\"pubkey\":\"%s\",\"signer\":%s,\"source\":\"transaction\",\"writable\":%s}", + (idx == 0 ? "" : ","), buf32, (signer ? "true" : "false"), (writable ? "true" : "false")); + } + + fd_textstream_sprintf(ts, "],\"signatures\":["); + fd_ed25519_sig_t const * sigs = (fd_ed25519_sig_t const *)(raw + txn->signature_off); + for ( uchar j = 0; j < txn->signature_cnt; j++ ) { + char buf64[FD_BASE58_ENCODED_64_SZ]; + fd_base58_encode_64((const uchar*)&sigs[j], NULL, buf64); + fd_textstream_sprintf(ts, "%s\"%s\"", (j == 0 ? "" : ","), buf64); + } + EMIT_SIMPLE("]}"); + + return 0; +} + +int fd_txn_to_json( fd_textstream_t * ts, + fd_txn_t* txn, + const uchar* raw, + fd_rpc_encoding_t encoding, + long maxvers, + enum fd_block_detail detail, + int rewards ) { + if( detail == FD_BLOCK_DETAIL_FULL ) + return fd_txn_to_json_full( ts, txn, raw, encoding, maxvers, rewards ); + else if( detail == FD_BLOCK_DETAIL_ACCTS ) + return fd_txn_to_json_accts( ts, txn, raw, encoding, maxvers, rewards ); + FD_LOG_ERR(("unsupported detail parameter")); + return -1; +} + +int fd_block_to_json( fd_textstream_t * ts, + long call_id, + fd_block_t * blk, + const uchar * blk_data, + ulong blk_sz, + fd_slot_meta_t * meta, + fd_rpc_encoding_t encoding, + long maxvers, + enum fd_block_detail detail, + int rewards) { + EMIT_SIMPLE("{\"jsonrpc\":\"2.0\",\"result\":{"); + + if ( meta ) { + fd_textstream_sprintf(ts, "\"blockHeight\":%lu,\"blockTime\":%ld,\"parentSlot\":%lu", + blk->height, blk->ts/(long)1e9, meta->parent_slot); + } + + if( detail == FD_BLOCK_DETAIL_NONE ) { + fd_textstream_sprintf(ts, "},\"id\":%lu}", call_id); + return 0; + } + + EMIT_SIMPLE(","); + + if( detail == FD_BLOCK_DETAIL_SIGS ) { + EMIT_SIMPLE("\"signatures\":["); + + int first_sig = 1; + ulong blockoff = 0; + while (blockoff < blk_sz) { + if ( blockoff + sizeof(ulong) > blk_sz ) + FD_LOG_ERR(("premature end of block")); + ulong mcount = *(const ulong *)(blk_data + blockoff); + blockoff += sizeof(ulong); + + /* Loop across microblocks */ + for (ulong mblk = 0; mblk < mcount; ++mblk) { + if ( blockoff + sizeof(fd_microblock_hdr_t) > blk_sz ) + FD_LOG_ERR(("premature end of block")); + fd_microblock_hdr_t * hdr = (fd_microblock_hdr_t *)((const uchar *)blk_data + blockoff); + blockoff += sizeof(fd_microblock_hdr_t); + + /* Loop across transactions */ + for ( ulong txn_idx = 0; txn_idx < hdr->txn_cnt; txn_idx++ ) { + uchar txn_out[FD_TXN_MAX_SZ]; + ulong pay_sz = 0; + const uchar* raw = (const uchar *)blk_data + blockoff; + ulong txn_sz = fd_txn_parse_core(raw, fd_ulong_min(blk_sz - blockoff, FD_TXN_MTU), txn_out, NULL, &pay_sz, 0); + if ( txn_sz == 0 || txn_sz > FD_TXN_MAX_SZ ) + FD_LOG_ERR( ( "failed to parse transaction %lu in microblock %lu", + txn_idx, + mblk ) ); + fd_txn_t * txn = (fd_txn_t *)txn_out; + + /* Loop across signatures */ + fd_ed25519_sig_t const * sigs = (fd_ed25519_sig_t const *)(raw + txn->signature_off); + for ( uchar j = 0; j < txn->signature_cnt; j++ ) { + char buf64[FD_BASE58_ENCODED_64_SZ]; + fd_base58_encode_64((const uchar*)&sigs[j], NULL, buf64); + fd_textstream_sprintf(ts, "%s\"%s\"", (first_sig ? "" : ","), buf64); + first_sig = 0; + } + + blockoff += pay_sz; + } + } + } + if ( blockoff != blk_sz ) + FD_LOG_ERR(("garbage at end of block")); + + fd_textstream_sprintf(ts, "]},\"id\":%lu}", call_id); + return 0; + } + + EMIT_SIMPLE("\"transactions\":["); + + int first_txn = 1; + ulong blockoff = 0; + while (blockoff < blk_sz) { + if ( blockoff + sizeof(ulong) > blk_sz ) + FD_LOG_ERR(("premature end of block")); + ulong mcount = *(const ulong *)(blk_data + blockoff); + blockoff += sizeof(ulong); + + /* Loop across microblocks */ + for (ulong mblk = 0; mblk < mcount; ++mblk) { + if ( blockoff + sizeof(fd_microblock_hdr_t) > blk_sz ) + FD_LOG_ERR(("premature end of block")); + fd_microblock_hdr_t * hdr = (fd_microblock_hdr_t *)((const uchar *)blk_data + blockoff); + blockoff += sizeof(fd_microblock_hdr_t); + + /* Loop across transactions */ + for ( ulong txn_idx = 0; txn_idx < hdr->txn_cnt; txn_idx++ ) { + uchar txn_out[FD_TXN_MAX_SZ]; + ulong pay_sz = 0; + const uchar* raw = (const uchar *)blk_data + blockoff; + ulong txn_sz = fd_txn_parse_core(raw, fd_ulong_min(blk_sz - blockoff, FD_TXN_MTU), txn_out, NULL, &pay_sz, 0); + if ( txn_sz == 0 || txn_sz > FD_TXN_MAX_SZ ) + FD_LOG_ERR( ( "failed to parse transaction %lu in microblock %lu", + txn_idx, + mblk ) ); + + if (first_txn) { + first_txn = 0; + EMIT_SIMPLE("{"); + } else + EMIT_SIMPLE(",{"); + + int r = fd_txn_to_json( ts, (fd_txn_t *)txn_out, raw, encoding, maxvers, detail, rewards ); + if ( r ) { + return r; + } + + EMIT_SIMPLE("}"); + + blockoff += pay_sz; + } + } + } + if ( blockoff != blk_sz ) + FD_LOG_ERR(("garbage at end of block")); + + fd_textstream_sprintf(ts, "]},\"id\":%lu}", call_id); + + return 0; +} + +const char* +fd_account_to_json( fd_textstream_t * ts, + fd_pubkey_t acct, + fd_rpc_encoding_t enc, + uchar const * val, + ulong val_sz, + long off, + long len ) { + fd_textstream_sprintf(ts, "{\"data\":[\""); + + fd_account_meta_t * metadata = (fd_account_meta_t *)val; + if (val_sz < sizeof(fd_account_meta_t) && val_sz < metadata->hlen) { + return "failed to load account data"; + } + val = (uchar*)val + metadata->hlen; + val_sz = val_sz - metadata->hlen; + if (val_sz > metadata->dlen) + val_sz = metadata->dlen; + + if (len != FD_LONG_UNSET && off != FD_LONG_UNSET) { + if (enc == FD_ENC_JSON) { + return "cannot use jsonParsed encoding with slice"; + } + if (off < 0 || (ulong)off >= val_sz) { + val = NULL; + val_sz = 0; + } else { + val = (uchar*)val + (ulong)off; + val_sz = val_sz - (ulong)off; + } + if (len < 0) { + val = NULL; + val_sz = 0; + } else if ((ulong)len < val_sz) + val_sz = (ulong)len; + } + + const char* encstr; + switch (enc) { + case FD_ENC_BASE58: + if (fd_textstream_encode_base58(ts, val, val_sz)) { + return "failed to encode data in base58"; + } + encstr = "base58"; + break; + case FD_ENC_BASE64: + if (fd_textstream_encode_base64(ts, val, val_sz)) { + return "failed to encode data in base64"; + } + encstr = "base64"; + break; + default: + return "unsupported encoding"; + } + + char owner[50]; + fd_base58_encode_32((uchar*)metadata->info.owner, 0, owner); + char addr[50]; + fd_base58_encode_32(acct.uc, 0, addr); + fd_textstream_sprintf(ts, "\",\"%s\"],\"executable\":%s,\"lamports\":%lu,\"owner\":\"%s\",\"address\":\"%s\",\"rentEpoch\":%lu,\"space\":%lu}", + encstr, + (metadata->info.executable ? "true" : "false"), + metadata->info.lamports, + owner, + addr, + metadata->info.rent_epoch, + val_sz); + + return NULL; +} diff --git a/src/app/rpcserver/fd_block_to_json.h b/src/app/rpcserver/fd_block_to_json.h new file mode 100644 index 0000000000..0f716075b5 --- /dev/null +++ b/src/app/rpcserver/fd_block_to_json.h @@ -0,0 +1,38 @@ +typedef enum { + FD_ENC_BASE58, FD_ENC_BASE64, FD_ENC_BASE64_ZSTD, FD_ENC_JSON, FD_ENC_JSON_PARSED +} fd_rpc_encoding_t; + +enum fd_block_detail { FD_BLOCK_DETAIL_FULL, FD_BLOCK_DETAIL_ACCTS, FD_BLOCK_DETAIL_SIGS, FD_BLOCK_DETAIL_NONE }; + +int fd_txn_meta_to_json( fd_textstream_t * ts, + const void * meta_raw, + ulong meta_raw_sz ); + +int fd_txn_to_json( fd_textstream_t * ts, + fd_txn_t* txn, + const uchar* raw, + fd_rpc_encoding_t encoding, + long maxvers, + enum fd_block_detail detail, + int rewards ); + +int fd_block_to_json( fd_textstream_t * ts, + long call_id, + fd_block_t * blk, + const uchar * blk_data, + ulong blk_sz, + fd_slot_meta_t * meta, + fd_rpc_encoding_t encoding, + long maxvers, + enum fd_block_detail detail, + int rewards); + +#define FD_LONG_UNSET (1L << 63L) + +const char* fd_account_to_json( fd_textstream_t * ts, + fd_pubkey_t acct, + fd_rpc_encoding_t enc, + uchar const * val, + ulong val_sz, + long off, + long len ); diff --git a/src/app/rpcserver/fd_methods.c b/src/app/rpcserver/fd_methods.c new file mode 100644 index 0000000000..d21fcd35e2 --- /dev/null +++ b/src/app/rpcserver/fd_methods.c @@ -0,0 +1,301 @@ +#include +#include +#include +#include "fd_methods.h" +#include "fd_webserver.h" +#include "../../util/fd_util.h" + +// Read the next json lexical token and report any error to the client +#define NEXT_TOKEN \ + do { \ + prevpos = lex->pos; \ + prevtoken = lex->last_tok; \ + token = json_lex_next_token(lex); \ + if (token == JSON_TOKEN_ERROR) return 0; \ + } while (0) + +#define UNNEXT_TOKEN \ + lex->pos = prevpos; \ + lex->last_tok = prevtoken; + +// Report a json parsing syntax error +#define SYNTAX_ERROR(format, ...) \ + do { \ + json_lex_sprintf(lex, format, __VA_ARGS__); \ + return 0; \ + } while (0) + +// Parse a generic json value. The values argument is used for storing +// leaf values for later access. path describes the path through the +// json syntax tree to this value. +int +json_values_parse(json_lex_state_t* lex, struct json_values* values, struct json_path* path) { + ulong prevpos; + long token; + long prevtoken; + + // Prepare to update the path to include a new element + if (path->len == JSON_MAX_PATH) + SYNTAX_ERROR("json value is too nested at position %lu", lex->pos); + uint* path_last = &path->elems[path->len ++]; + + NEXT_TOKEN; + switch (token) { + case JSON_TOKEN_LBRACE: // Start a new json object + do { + NEXT_TOKEN; + if (token == JSON_TOKEN_RBRACE) + break; + if (token != JSON_TOKEN_STRING) + SYNTAX_ERROR("expected string key at position %lu", prevpos); + // Translate the key string to a known keyword ID. We only allow + // a predetermined set of keys. + ulong key_sz; + const char* key = json_lex_get_text(lex, &key_sz); + long keyid = fd_webserver_json_keyword(key, key_sz); + if (keyid == KEYW_UNKNOWN) + SYNTAX_ERROR("unrecognized string key at position %lu", prevpos); + // Append to the path + *path_last = ((JSON_TOKEN_LBRACE<<16) | (uint)keyid); + + NEXT_TOKEN; + if (token != JSON_TOKEN_COLON) + SYNTAX_ERROR("expected colon at position %lu", prevpos); + + // Recursively parse the inner value + if (!json_values_parse(lex, values, path)) + return 0; + + NEXT_TOKEN; + if (token == JSON_TOKEN_RBRACE) + break; + if (token != JSON_TOKEN_COMMA) + SYNTAX_ERROR("expected comma at position %lu", prevpos); + } while(1); + break; + + case JSON_TOKEN_LBRACKET: { // Start an array + uint i = 0; + do { + // Append to the path + *path_last = ((JSON_TOKEN_LBRACKET<<16) | i); + // Recursively parse the array element + if (!json_values_parse(lex, values, path)) + return 0; + + NEXT_TOKEN; + if (token == JSON_TOKEN_RBRACKET) + break; + if (token != JSON_TOKEN_COMMA) + SYNTAX_ERROR("expected comma at position %lu", prevpos); + + ++i; + } while(1); + break; + } + + case JSON_TOKEN_STRING: { + // Append to the path + *path_last = (JSON_TOKEN_STRING<<16); + // Store the leaf value in values, indexed by the current path + ulong str_sz; + const char* str = json_lex_get_text(lex, &str_sz); + json_add_value(values, path, str, str_sz); + break; + } + + case JSON_TOKEN_INTEGER: { + // Append to the path + *path_last = (JSON_TOKEN_INTEGER<<16); + // Store the leaf value in values, indexed by the current path + long val = json_lex_as_int(lex); + json_add_value(values, path, &val, sizeof(val)); + break; + } + + case JSON_TOKEN_FLOAT: { + // Append to the path + *path_last = (JSON_TOKEN_FLOAT<<16); + // Store the leaf value in values, indexed by the current path + double val = json_lex_as_float(lex); + json_add_value(values, path, &val, sizeof(val)); + break; + } + + case JSON_TOKEN_BOOL: + // Append to the path + *path_last = (JSON_TOKEN_BOOL<<16); + // Store the leaf value in values, indexed by the current path + json_add_value(values, path, &lex->last_bool, sizeof(lex->last_bool)); + break; + + case JSON_TOKEN_NULL: + // Append to the path + *path_last = (JSON_TOKEN_NULL<<16); + // Store the leaf value in values, indexed by the current path + json_add_value(values, path, NULL, 0); + break; + + case JSON_TOKEN_RBRACKET: + if (prevtoken == JSON_TOKEN_LBRACKET) { + /* Empty array */ + UNNEXT_TOKEN; + break; + } + SYNTAX_ERROR("unexpected ']' at position %lu", prevpos); + break; + + case JSON_TOKEN_RBRACE: + if (prevtoken == JSON_TOKEN_LBRACE) { + /* Empty object */ + UNNEXT_TOKEN; + break; + } + SYNTAX_ERROR("unexpected '}' at position %lu", prevpos); + break; + + default: + SYNTAX_ERROR("expected json value at position %lu", prevpos); + } + + path->len --; + return 1; +} + +// Initialize a json_values +void json_values_new(struct json_values* values) { + values->num_values = 0; + values->buf = values->buf_init; + values->buf_sz = 0; + values->buf_alloc = sizeof(values->buf_init); +} + +// Destroy a json_values +void json_values_delete(struct json_values* values) { + if (values->buf != values->buf_init) + free(values->buf); +} + +// Add a parsed value to a json_values +void json_add_value(struct json_values* values, struct json_path* path, const void* data, ulong data_sz) { + if (values->num_values == JSON_MAX_PATHS) { + // Ignore when we have too many values. In the actual requests + // that we expect to handle, the number of values is modest. + return; + } + + // Get the new buffer size after we add the new data (plus null terminator) + ulong new_buf_sz = values->buf_sz + data_sz + 1; + new_buf_sz = ((new_buf_sz + 7UL) & ~7UL); // 8-byte align + if (new_buf_sz > values->buf_alloc) { + // Grow the allocation + do { + values->buf_alloc <<= 1; + } while (new_buf_sz > values->buf_alloc); + char* newbuf = (char*)malloc(values->buf_alloc); + fd_memcpy(newbuf, values->buf, values->buf_sz); + if (values->buf != values->buf_init) + free(values->buf); + values->buf = newbuf; + } + + // Add a new value to the table + uint i = values->num_values++; + struct json_path* path2 = &values->values[i].path; + uint len = path2->len = path->len; + for (uint j = 0; j < len; ++j) + path2->elems[j] = path->elems[j]; + // Copy out the data + ulong off = values->values[i].data_offset = values->buf_sz; + values->values[i].data_sz = data_sz; + fd_memcpy(values->buf + off, data, data_sz); + values->buf[off + data_sz] = '\0'; + values->buf_sz = new_buf_sz; +} + +// Retrieve a value at a given path. A NULL is returned if the path +// isn't found +const void* json_get_value(struct json_values* values, const uint* path_elems, uint path_sz, ulong* data_sz) { + // Loop through the values + for (uint i = 0; i < values->num_values; ++i) { + // Compare paths + struct json_path* path = &values->values[i].path; + if (path->len == path_sz) { + for (uint j = 0; ; ++j) { + if (j == path_sz) { + *data_sz = values->values[i].data_sz; + return values->buf + values->values[i].data_offset; + } + if (path->elems[j] != path_elems[j]) + break; + } + } + } + // Not found + *data_sz = 0; + return NULL; +} + +const void* json_get_value_multi(struct json_values* values, const uint* path_elems, uint path_sz, ulong* data_sz, uint * pos) { + // Loop through the values + for (uint i = *pos; i < values->num_values; ++i) { + // Compare paths + struct json_path* path = &values->values[i].path; + if (path->len == path_sz) { + for (uint j = 0; ; ++j) { + if (j == path_sz) { + *data_sz = values->values[i].data_sz; + *pos = j+1; + return values->buf + values->values[i].data_offset; + } + if (path->elems[j] != path_elems[j]) + break; + } + } + } + // Not found + *data_sz = 0; + *pos = values->num_values; + return NULL; +} + +// Dump the values and paths to stdout +void json_values_printout(struct json_values* values) { + for (uint i = 0; i < values->num_values; ++i) { + struct json_path* path = &values->values[i].path; + const char* data = values->buf + values->values[i].data_offset; + ulong data_sz = values->values[i].data_sz; + for (uint j = 0; j < path->len; ++j) { + uint e = path->elems[j]; + switch (e >> 16U) { + case JSON_TOKEN_LBRACE: + printf(" (object|%s)", un_fd_webserver_json_keyword(e & 0xffffUL)); + break; + case JSON_TOKEN_LBRACKET: + printf(" (array|%lu)", e & 0xffffUL); + break; + case JSON_TOKEN_STRING: + printf(" STRING \""); + fwrite(data, 1, data_sz, stdout); + printf("\""); + break; + case JSON_TOKEN_INTEGER: + assert(data_sz == sizeof(long)); + printf(" INT %ld", *(long*)data); + break; + case JSON_TOKEN_FLOAT: + assert(data_sz == sizeof(double)); + printf(" FLOAT %g", *(double*)data); + break; + case JSON_TOKEN_BOOL: + assert(data_sz == sizeof(int)); + printf(" BOOL %d", *(int*)data); + break; + case JSON_TOKEN_NULL: + printf(" NULL"); + break; + } + } + printf("\n"); + } +} diff --git a/src/app/rpcserver/fd_methods.h b/src/app/rpcserver/fd_methods.h new file mode 100644 index 0000000000..d61c10075a --- /dev/null +++ b/src/app/rpcserver/fd_methods.h @@ -0,0 +1,68 @@ +#ifndef HEADER_fd_src_tango_webserver_fd_methods_h +#define HEADER_fd_src_tango_webserver_fd_methods_h + +#include "json_lex.h" + +// Data structure describing a "path" to a value in json data. This is +// basically a path through the syntax tree. A path element can be one +// of the following: +// object member: (JSON_TOKEN_LBRACE<<16) | keyword_id +// array member: (JSON_TOKEN_LBRACKET<<16) | index +// string value: (JSON_TOKEN_STRING<<16) +// int value: (JSON_TOKEN_INTEGER<<16) +// float value: (JSON_TOKEN_FLOAT<<16) +// boolean value: (JSON_TOKEN_BOOL<<16) +// null value: (JSON_TOKEN_NULL<<16) +// keyword ids are generated by json_keyword(...) in keywords.h +#define JSON_MAX_PATH 8 +struct json_path { + uint len; + uint elems[JSON_MAX_PATH]; +}; + +// Represents the result of parsing a json data structure. Each leaf +// value (string, number, boolean, etc) gets an entry in the values +// list. The complete paths to those values are provided. This +// structure is optimized for quickly finding values at predetermined +// paths. It is compact and efficient. +#define JSON_MAX_PATHS 32 +struct json_values { + // Number of leaf values + uint num_values; + struct { + // Path to data + struct json_path path; + // Offset and size of data value in buffer + ulong data_offset; + ulong data_sz; + } values[JSON_MAX_PATHS]; + // Dynamic buffer containing all data + char* buf; + ulong buf_sz; + ulong buf_alloc; + char buf_init[2048]; +}; + +// Initialize a json_values +void json_values_new(struct json_values* values); + +// Destroy a json_values +void json_values_delete(struct json_values* values); + +// Add a parsed value to a json_values +void json_add_value(struct json_values* values, struct json_path* path, const void* data, ulong data_sz); + +// Retrieve a value at a given path. A NULL is returned if the path +// isn't found +const void* json_get_value(struct json_values* values, const uint* path, uint path_sz, ulong* data_sz); + +// Version that allows iterative retrieval. *pos should be initialized to zero. +const void* json_get_value_multi(struct json_values* values, const uint* path, uint path_sz, ulong* data_sz, uint * pos); + +// Dump the values and paths to stdout +void json_values_printout(struct json_values* values); + +// Parse a block of json. Returns 1 on success. +int json_values_parse(json_lex_state_t* lex, struct json_values* values, struct json_path* path); + +#endif /* HEADER_fd_src_tango_webserver_fd_methods_h */ diff --git a/src/app/rpcserver/fd_rpc_service.c b/src/app/rpcserver/fd_rpc_service.c new file mode 100644 index 0000000000..c6281190b8 --- /dev/null +++ b/src/app/rpcserver/fd_rpc_service.c @@ -0,0 +1,2038 @@ +#include "fd_rpc_service.h" +#include +#include "fd_methods.h" +#include "fd_webserver.h" +#include "../../flamenco/types/fd_types.h" +#include "../../flamenco/types/fd_solana_block.pb.h" +#include "../../flamenco/runtime/fd_runtime.h" +#include "../../flamenco/runtime/fd_acc_mgr.h" +#include "../../flamenco/runtime/sysvar/fd_sysvar_rent.h" +#include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h" +#include "../../ballet/base58/fd_base58.h" +#include "keywords.h" + +#define API_VERSION "1.17.6" + +#define CRLF "\r\n" +#define MATCH_STRING(_text_,_text_sz_,_str_) (_text_sz_ == sizeof(_str_)-1 && memcmp(_text_, _str_, sizeof(_str_)-1) == 0) + +struct fd_ws_subscription { + fd_websocket_ctx_t * socket; + long meth_id; + long call_id; + ulong subsc_id; + union { + struct { + fd_pubkey_t acct; + fd_rpc_encoding_t enc; + long off; + long len; + } acct_subscribe; + }; +}; + +#define FD_WS_MAX_SUBS 1024 + +struct fd_rpc_global_ctx { + fd_readwrite_lock_t lock; + fd_webserver_t ws; + fd_funk_t * funk; + fd_blockstore_t * blockstore; + struct fd_ws_subscription sub_list[FD_WS_MAX_SUBS]; + ulong sub_cnt; + ulong last_subsc_id; + fd_epoch_bank_t * epoch_bank; + ulong epoch_bank_epoch; +}; +typedef struct fd_rpc_global_ctx fd_rpc_global_ctx_t; + +struct fd_rpc_ctx { + long call_id; + fd_rpc_global_ctx_t * global; +}; + +static void * +read_account( fd_rpc_ctx_t * ctx, fd_pubkey_t * acct, fd_valloc_t valloc, ulong * result_len ) { + fd_funk_rec_key_t recid = fd_acc_funk_key(acct); + fd_funk_t * funk = ctx->global->funk; + return fd_funk_rec_query_safe(funk, &recid, valloc, result_len); +} + +static void * +read_account_with_xid( fd_rpc_ctx_t * ctx, fd_pubkey_t * acct, fd_funk_txn_xid_t * xid, fd_valloc_t valloc, ulong * result_len ) { + fd_funk_rec_key_t recid = fd_acc_funk_key(acct); + fd_funk_t * funk = ctx->global->funk; + return fd_funk_rec_query_xid_safe(funk, &recid, xid, valloc, result_len); +} + +/* LEAVES THE LOCK IN READ MODE */ +fd_epoch_bank_t * +read_epoch_bank( fd_rpc_ctx_t * ctx, fd_valloc_t valloc, ulong * smr ) { + fd_rpc_global_ctx_t * glob = ctx->global; + + for(;;) { + fd_readwrite_start_read( &glob->lock ); + *smr = glob->blockstore->smr; + + if( glob->epoch_bank != NULL && + glob->epoch_bank_epoch == fd_slot_to_epoch(&glob->epoch_bank->epoch_schedule, *smr, NULL) ) { + /* Leave lock held */ + return glob->epoch_bank; + } + + fd_readwrite_end_read( &glob->lock ); + fd_readwrite_start_write( &glob->lock ); + + if( glob->epoch_bank != NULL ) { + fd_bincode_destroy_ctx_t binctx; + binctx.valloc = fd_libc_alloc_virtual(); + fd_epoch_bank_destroy( glob->epoch_bank, &binctx ); + free( glob->epoch_bank ); + glob->epoch_bank = NULL; + } + + fd_funk_rec_key_t recid = fd_runtime_epoch_bank_key(); + ulong vallen; + fd_funk_t * funk = ctx->global->funk; + void * val = fd_funk_rec_query_safe(funk, &recid, valloc, &vallen); + if( val == NULL ) { + FD_LOG_WARNING(( "failed to decode epoch_bank" )); + fd_readwrite_end_write( &glob->lock ); + return NULL; + } + fd_epoch_bank_t * epoch_bank = malloc( fd_epoch_bank_footprint() ); + fd_epoch_bank_new( epoch_bank ); + fd_bincode_decode_ctx_t binctx; + binctx.data = val; + binctx.dataend = (uchar*)val + vallen; + binctx.valloc = fd_libc_alloc_virtual(); + if( fd_epoch_bank_decode( epoch_bank, &binctx )!=FD_BINCODE_SUCCESS ) { + FD_LOG_WARNING(( "failed to decode epoch_bank" )); + fd_valloc_free( valloc, val ); + free( epoch_bank ); + fd_readwrite_end_write( &glob->lock ); + return NULL; + } + fd_valloc_free( valloc, val ); + + glob->epoch_bank = epoch_bank; + glob->epoch_bank_epoch = fd_slot_to_epoch(&epoch_bank->epoch_schedule, *smr, NULL); + fd_readwrite_end_write( &glob->lock ); + } +} + +fd_slot_bank_t * +read_slot_bank( fd_rpc_ctx_t * ctx, fd_valloc_t valloc ) { + fd_funk_rec_key_t recid = fd_runtime_slot_bank_key(); + ulong vallen; + fd_funk_t * funk = ctx->global->funk; + void * val = fd_funk_rec_query_safe(funk, &recid, valloc, &vallen); + if( val == NULL ) { + FD_LOG_WARNING(( "failed to decode slot_bank" )); + return NULL; + } + fd_slot_bank_t * slot_bank = fd_valloc_malloc( valloc, fd_slot_bank_align(), fd_slot_bank_footprint() ); + fd_slot_bank_new( slot_bank ); + fd_bincode_decode_ctx_t binctx; + binctx.data = val; + binctx.dataend = (uchar*)val + vallen; + binctx.valloc = valloc; + if( fd_slot_bank_decode( slot_bank, &binctx )!=FD_BINCODE_SUCCESS ) { + FD_LOG_WARNING(( "failed to decode slot_bank" )); + fd_valloc_free( valloc, val ); + return NULL; + } + fd_valloc_free( valloc, val ); + return slot_bank; +} + +static void fd_method_cleanup( uchar ** smem ) { + fd_scratch_detach( NULL ); + free( *smem ); +} + +/* Setup scratch space */ +#define FD_METHOD_SCRATCH_BEGIN( SMAX ) do { \ + uchar * smem = aligned_alloc( FD_SCRATCH_SMEM_ALIGN, \ + fd_ulong_align_up( fd_scratch_smem_footprint( SMAX ), FD_SCRATCH_SMEM_ALIGN ) ); \ + ulong fmem[4U]; \ + fd_scratch_attach( smem, fmem, SMAX, 4U ); \ + fd_scratch_push(); \ + uchar * __fd_scratch_guard_ ## __LINE__ \ + __attribute__((cleanup(fd_method_cleanup))) = smem; \ + do + +#define FD_METHOD_SCRATCH_END while(0); } while(0) + +// Implementation of the "getAccountInfo" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 1, "method": "getAccountInfo", "params": [ "21bVZhkqPJRVYDG3YpYtzHLMvkc7sa4KB7fMwGekTquG", { "encoding": "base64" } ] }' + +static int +method_getAccountInfo(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + FD_METHOD_SCRATCH_BEGIN( 11<<20 ) { + // Path to argument + static const uint PATH[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 0, + (JSON_TOKEN_STRING<<16) + }; + ulong arg_sz = 0; + const void* arg = json_get_value(values, PATH, 3, &arg_sz); + if (arg == NULL) { + fd_web_replier_error(replier, "getAccountInfo requires a string as first parameter"); + return 0; + } + + fd_textstream_t * ts = fd_web_replier_textstream(replier); + + fd_pubkey_t acct; + fd_base58_decode_32((const char *)arg, acct.uc); + ulong val_sz; + void * val = read_account(ctx, &acct, fd_scratch_virtual(), &val_sz); + fd_blockstore_t * blockstore = ctx->global->blockstore; + if (val == NULL) { + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"" API_VERSION "\",\"slot\":%lu},\"value\":null},\"id\":%lu}" CRLF, + blockstore->smr, ctx->call_id); + fd_web_replier_done(replier); + return 0; + } + + static const uint PATH2[4] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_ENCODING, + (JSON_TOKEN_STRING<<16) + }; + ulong enc_str_sz = 0; + const void* enc_str = json_get_value(values, PATH2, 4, &enc_str_sz); + fd_rpc_encoding_t enc; + if (enc_str == NULL || MATCH_STRING(enc_str, enc_str_sz, "base58")) + enc = FD_ENC_BASE58; + else if (MATCH_STRING(enc_str, enc_str_sz, "base64")) + enc = FD_ENC_BASE64; + else if (MATCH_STRING(enc_str, enc_str_sz, "base64+zstd")) + enc = FD_ENC_BASE64_ZSTD; + else if (MATCH_STRING(enc_str, enc_str_sz, "jsonParsed")) + enc = FD_ENC_JSON; + else { + fd_web_replier_error(replier, "invalid data encoding %s", (const char*)enc_str); + return 0; + } + + static const uint PATH3[5] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_DATASLICE, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_LENGTH, + (JSON_TOKEN_INTEGER<<16) + }; + static const uint PATH4[5] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_DATASLICE, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_OFFSET, + (JSON_TOKEN_INTEGER<<16) + }; + ulong len_sz = 0; + const void* len_ptr = json_get_value(values, PATH3, 5, &len_sz); + ulong off_sz = 0; + const void* off_ptr = json_get_value(values, PATH4, 5, &off_sz); + long off = (off_ptr ? *(long *)off_ptr : FD_LONG_UNSET); + long len = (len_ptr ? *(long *)len_ptr : FD_LONG_UNSET); + + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"" API_VERSION "\",\"slot\":%lu},\"value\":", + blockstore->smr); + const char * err = fd_account_to_json( ts, acct, enc, val, val_sz, off, len ); + if( err ) { + fd_web_replier_error(replier, "%s", err); + return 0; + } + fd_textstream_sprintf(ts, "},\"id\":%lu}" CRLF, ctx->call_id); + + fd_web_replier_done(replier); + + } FD_METHOD_SCRATCH_END; + + return 0; +} + +// Implementation of the "getBalance" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 1, "method": "getBalance", "params": [ "6s5gDyLyfNXP6WHUEn4YSMQJVcGETpKze7FCPeg9wxYT" ] }' + +static int +method_getBalance(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + FD_METHOD_SCRATCH_BEGIN( 11<<20 ) { + // Path to argument + static const uint PATH[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 0, + (JSON_TOKEN_STRING<<16) + }; + ulong arg_sz = 0; + const void* arg = json_get_value(values, PATH, 3, &arg_sz); + if (arg == NULL) { + fd_web_replier_error(replier, "getBalance requires a string as first parameter"); + return 0; + } + fd_pubkey_t acct; + fd_base58_decode_32((const char *)arg, acct.uc); + ulong val_sz; + void * val = read_account(ctx, &acct, fd_scratch_virtual(), &val_sz); + if (val == NULL) { + fd_web_replier_error(replier, "failed to load account data for %s", (const char*)arg); + return 0; + } + fd_account_meta_t * metadata = (fd_account_meta_t *)val; + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_blockstore_t * blockstore = ctx->global->blockstore; + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"" API_VERSION "\",\"slot\":%lu},\"value\":%lu},\"id\":%lu}" CRLF, + blockstore->smr, metadata->info.lamports, ctx->call_id); + fd_web_replier_done(replier); + } FD_METHOD_SCRATCH_END; + return 0; +} + +// Implementation of the "getBlock" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc": "2.0","id":1, "method":"getBlock", "params": [270562740, {"encoding": "json", "maxSupportedTransactionVersion":0, "transactionDetails":"full", "rewards":false}]} ' + +static int +method_getBlock(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + static const uint PATH_SLOT[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 0, + (JSON_TOKEN_INTEGER<<16) + }; + static const uint PATH_ENCODING[4] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_ENCODING, + (JSON_TOKEN_STRING<<16) + }; + static const uint PATH_MAXVERS[4] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_MAXSUPPORTEDTRANSACTIONVERSION, + (JSON_TOKEN_INTEGER<<16) + }; + static const uint PATH_DETAIL[4] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_TRANSACTIONDETAILS, + (JSON_TOKEN_STRING<<16) + }; + static const uint PATH_REWARDS[4] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_REWARDS, + (JSON_TOKEN_BOOL<<16) + }; + + ulong slot_sz = 0; + const void* slot = json_get_value(values, PATH_SLOT, 3, &slot_sz); + if (slot == NULL) { + fd_web_replier_error(replier, "getBlock requires a slot number as first parameter"); + return 0; + } + ulong slotn = (ulong)(*(long*)slot); + + ulong enc_str_sz = 0; + const void* enc_str = json_get_value(values, PATH_ENCODING, 4, &enc_str_sz); + fd_rpc_encoding_t enc; + if (enc_str == NULL || MATCH_STRING(enc_str, enc_str_sz, "json")) + enc = FD_ENC_JSON; + else if (MATCH_STRING(enc_str, enc_str_sz, "base58")) + enc = FD_ENC_BASE58; + else if (MATCH_STRING(enc_str, enc_str_sz, "base64")) + enc = FD_ENC_BASE64; + else if (MATCH_STRING(enc_str, enc_str_sz, "jsonParsed")) + enc = FD_ENC_JSON_PARSED; + else { + fd_web_replier_error(replier, "invalid data encoding %s", (const char*)enc_str); + return 0; + } + + ulong maxvers_sz = 0; + const void* maxvers = json_get_value(values, PATH_MAXVERS, 4, &maxvers_sz); + + ulong det_str_sz = 0; + const void* det_str = json_get_value(values, PATH_DETAIL, 4, &det_str_sz); + enum fd_block_detail det; + if (det_str == NULL || MATCH_STRING(det_str, det_str_sz, "full")) + det = FD_BLOCK_DETAIL_FULL; + else if (MATCH_STRING(det_str, det_str_sz, "accounts")) + det = FD_BLOCK_DETAIL_ACCTS; + else if (MATCH_STRING(det_str, det_str_sz, "signatures")) + det = FD_BLOCK_DETAIL_SIGS; + else if (MATCH_STRING(det_str, det_str_sz, "none")) + det = FD_BLOCK_DETAIL_NONE; + else { + fd_web_replier_error(replier, "invalid block detail %s", (const char*)det_str); + return 0; + } + + ulong rewards_sz = 0; + const void* rewards = json_get_value(values, PATH_REWARDS, 4, &rewards_sz); + + fd_block_t blk[1]; + fd_slot_meta_t slot_meta[1]; + ulong blk_sz; + fd_blockstore_t * blockstore = ctx->global->blockstore; + uchar * blk_data = fd_blockstore_block_query_volatile( blockstore, slotn, fd_libc_alloc_virtual(), blk, slot_meta, &blk_sz ); + if( blk_data == NULL ) { + fd_web_replier_error(replier, "failed to display block for slot %lu", slotn); + return 0; + } + + fd_textstream_t * ts = fd_web_replier_textstream(replier); + if (fd_block_to_json(ts, + ctx->call_id, + blk, + blk_data, + blk_sz, + slot_meta, + enc, + (maxvers == NULL ? 0 : *(const long*)maxvers), + det, + (rewards == NULL ? 1 : *(const int*)rewards))) { + free( blk_data ); + fd_web_replier_error(replier, "failed to display block for slot %lu", slotn); + return 0; + } + free( blk_data ); + fd_web_replier_done(replier); + return 0; +} + +// Implementation of the "getBlockCommitment" methods +static int +method_getBlockCommitment(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getBlockCommitment is not implemented"); + return 0; +} + +// Implementation of the "getBlockHeight" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d '{ "jsonrpc":"2.0","id":1, "method":"getBlockHeight" }' +static int +method_getBlockHeight(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void) values; + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_block_t blk[1]; + fd_slot_meta_t slot_meta[1]; + fd_blockstore_t * blockstore = ctx->global->blockstore; + int ret = fd_blockstore_slot_meta_query_volatile(blockstore, blockstore->smr, blk, slot_meta); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":%lu,\"id\":%lu}" CRLF, + (!ret ? blk->height : 0UL), ctx->call_id); + fd_web_replier_done(replier); + return 0; +} + +// Implementation of the "getBlockProduction" methods +static int +method_getBlockProduction(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getBlockProduction is not implemented"); + return 0; +} + +// Implementation of the "getBlocks" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc": "2.0", "id": 1, "method": "getBlocks", "params": [270562730, 270562740]} ' + +static int +method_getBlocks(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + static const uint PATH_STARTSLOT[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 0, + (JSON_TOKEN_INTEGER<<16) + }; + ulong startslot_sz = 0; + const void* startslot = json_get_value(values, PATH_STARTSLOT, 3, &startslot_sz); + if (startslot == NULL) { + fd_web_replier_error(replier, "getBlocks requires a start slot number as first parameter"); + return 0; + } + ulong startslotn = (ulong)(*(long*)startslot); + static const uint PATH_ENDSLOT[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_INTEGER<<16) + }; + ulong endslot_sz = 0; + const void* endslot = json_get_value(values, PATH_ENDSLOT, 3, &endslot_sz); + ulong endslotn = (endslot == NULL ? ULONG_MAX : (ulong)(*(long*)endslot)); + + fd_blockstore_t * blockstore = ctx->global->blockstore; + if (startslotn < blockstore->min) + startslotn = blockstore->min; + if (endslotn > blockstore->max) + endslotn = blockstore->max; + + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":["); + uint cnt = 0; + for ( ulong i = startslotn; i <= endslotn && cnt < 500000U; ++i ) { + fd_block_t blk[1]; + fd_slot_meta_t slot_meta[1]; + int ret = fd_blockstore_slot_meta_query_volatile(blockstore, i, blk, slot_meta); + if (!ret) { + fd_textstream_sprintf(ts, "%s%lu", (cnt==0 ? "" : ","), i); + ++cnt; + } + } + fd_textstream_sprintf(ts, "],\"id\":%lu}" CRLF, ctx->call_id); + + fd_web_replier_done(replier); + return 0; +} + +// Implementation of the "getBlocksWithLimit" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc": "2.0", "id":1, "method":"getBlocksWithLimit", "params":[270562730, 3]} ' + +static int +method_getBlocksWithLimit(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + static const uint PATH_SLOT[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 0, + (JSON_TOKEN_INTEGER<<16) + }; + ulong startslot_sz = 0; + const void* startslot = json_get_value(values, PATH_SLOT, 3, &startslot_sz); + if (startslot == NULL) { + fd_web_replier_error(replier, "getBlocksWithLimit requires a start slot number as first parameter"); + return 0; + } + ulong startslotn = (ulong)(*(long*)startslot); + static const uint PATH_LIMIT[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_INTEGER<<16) + }; + ulong limit_sz = 0; + const void* limit = json_get_value(values, PATH_LIMIT, 3, &limit_sz); + if (limit == NULL) { + fd_web_replier_error(replier, "getBlocksWithLimit requires a limit as second parameter"); + return 0; + } + ulong limitn = (ulong)(*(long*)limit); + + fd_blockstore_t * blockstore = ctx->global->blockstore; + if (startslotn < blockstore->min) + startslotn = blockstore->min; + if (limitn > 500000) + limitn = 500000; + + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":["); + uint cnt = 0; + for ( ulong i = startslotn; i <= blockstore->max && cnt < limitn; ++i ) { + fd_block_t blk[1]; + fd_slot_meta_t slot_meta[1]; + int ret = fd_blockstore_slot_meta_query_volatile(blockstore, i, blk, slot_meta); + if (!ret) { + fd_textstream_sprintf(ts, "%s%lu", (cnt==0 ? "" : ","), i); + ++cnt; + } + } + fd_textstream_sprintf(ts, "],\"id\":%lu}" CRLF, ctx->call_id); + + fd_web_replier_done(replier); + return 0; +} + +// Implementation of the "getBlockTime" methods +static int +method_getBlockTime(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getBlockTime is not implemented"); + return 0; +} + +// Implementation of the "getClusterNodes" methods +static int +method_getClusterNodes(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getClusterNodes is not implemented"); + return 0; +} + +// Implementation of the "getConfirmedBlock" methods +static int +method_getConfirmedBlock(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getConfirmedBlock is not implemented"); + return 0; +} + +// Implementation of the "getConfirmedBlocks" methods +static int +method_getConfirmedBlocks(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getConfirmedBlocks is not implemented"); + return 0; +} + +// Implementation of the "getConfirmedBlocksWithLimit" methods +static int +method_getConfirmedBlocksWithLimit(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getConfirmedBlocksWithLimit is not implemented"); + return 0; +} + +// Implementation of the "getConfirmedSignaturesForAddress2" methods +static int +method_getConfirmedSignaturesForAddress2(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getConfirmedSignaturesForAddress2 is not implemented"); + return 0; +} + +// Implementation of the "getConfirmedTransaction" methods +static int +method_getConfirmedTransaction(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getConfirmedTransaction is not implemented"); + return 0; +} + +// Implementation of the "getEpochInfo" methods +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc":"2.0","id":1, "method":"getEpochInfo"} ' + +static int +method_getEpochInfo(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + FD_METHOD_SCRATCH_BEGIN( 1<<28 ) { /* read_epoch consumes a ton of scratch space! */ + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_blockstore_t * blockstore = ctx->global->blockstore; + ulong smr; + fd_epoch_bank_t * epoch_bank = read_epoch_bank(ctx, fd_scratch_virtual(), &smr); + fd_slot_bank_t * slot_bank = read_slot_bank(ctx, fd_scratch_virtual()); + ulong slot_idx = 0; + ulong epoch = fd_slot_to_epoch( &epoch_bank->epoch_schedule, smr, &slot_idx ); + ulong slots_per_epoch = fd_epoch_slot_cnt( &epoch_bank->epoch_schedule, epoch ); + fd_block_t blk[1]; + fd_slot_meta_t slot_meta[1]; + int ret = fd_blockstore_slot_meta_query_volatile(blockstore, smr, blk, slot_meta); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"absoluteSlot\":%lu,\"blockHeight\":%lu,\"epoch\":%lu,\"slotIndex\":%lu,\"slotsInEpoch\":%lu,\"transactionCount\":%lu},\"id\":%lu}" CRLF, + smr, + (!ret ? blk->height : 0UL), + epoch, + slot_idx, + slots_per_epoch, + slot_bank->transaction_count, + ctx->call_id); + fd_web_replier_done(replier); + fd_readwrite_end_read( &ctx->global->lock ); + } FD_METHOD_SCRATCH_END; + return 0; +} + +// Implementation of the "getEpochSchedule" methods +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc":"2.0","id":1, "method":"getEpochSchedule"} ' + +static int +method_getEpochSchedule(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + FD_METHOD_SCRATCH_BEGIN( 1<<28 ) { /* read_epoch consumes a ton of scratch space! */ + fd_textstream_t * ts = fd_web_replier_textstream(replier); + ulong smr; + fd_epoch_bank_t * epoch_bank = read_epoch_bank(ctx, fd_scratch_virtual(), &smr); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"firstNormalEpoch\":%lu,\"firstNormalSlot\":%lu,\"leaderScheduleSlotOffset\":%lu,\"slotsPerEpoch\":%lu,\"warmup\":%s},\"id\":%lu}" CRLF, + epoch_bank->epoch_schedule.first_normal_epoch, + epoch_bank->epoch_schedule.first_normal_slot, + epoch_bank->epoch_schedule.leader_schedule_slot_offset, + epoch_bank->epoch_schedule.slots_per_epoch, + (epoch_bank->epoch_schedule.warmup ? "true" : "false"), + ctx->call_id); + fd_web_replier_done(replier); + fd_readwrite_end_read( &ctx->global->lock ); + } FD_METHOD_SCRATCH_END; + return 0; +} + +// Implementation of the "getFeeCalculatorForBlockhash" methods +static int +method_getFeeCalculatorForBlockhash(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getFeeCalculatorForBlockhash is not implemented"); + return 0; +} + +// Implementation of the "getFeeForMessage" methods +static int +method_getFeeForMessage(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getFeeForMessage is not implemented"); + return 0; +} + +// Implementation of the "getFeeRateGovernor" methods +static int +method_getFeeRateGovernor(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getFeeRateGovernor is not implemented"); + return 0; +} + +// Implementation of the "getFees" methods +static int +method_getFees(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getFees is not implemented"); + return 0; +} + +// Implementation of the "getFirstAvailableBlock" methods +static int +method_getFirstAvailableBlock(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getFirstAvailableBlock is not implemented"); + return 0; +} + +// Implementation of the "getGenesisHash" methods +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc":"2.0","id":1, "method":"getGenesisHash"} ' + +static int +method_getGenesisHash(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + FD_METHOD_SCRATCH_BEGIN( 1<<28 ) { /* read_epoch consumes a ton of scratch space! */ + ulong smr; + fd_epoch_bank_t * epoch_bank = read_epoch_bank(ctx, fd_scratch_virtual(), &smr); + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":\""); + fd_textstream_encode_base58(ts, epoch_bank->genesis_hash.uc, sizeof(fd_pubkey_t)); + fd_textstream_sprintf(ts, "\",\"id\":%lu}" CRLF, ctx->call_id); + fd_web_replier_done(replier); + fd_readwrite_end_read( &ctx->global->lock ); + } FD_METHOD_SCRATCH_END; + return 0; +} + +// Implementation of the "getHealth" methods +static int +method_getHealth(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":\"ok\",\"id\":%lu}" CRLF, ctx->call_id); + fd_web_replier_done(replier); + return 0; +} + +// Implementation of the "getHighestSnapshotSlot" methods +static int +method_getHighestSnapshotSlot(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getHighestSnapshotSlot is not implemented"); + return 0; +} + +// Implementation of the "getIdentity" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc":"2.0","id":1, "method":"getIdentity"} ' + +static int +method_getIdentity(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void) values; + (void)ctx; + fd_web_replier_error(replier, "getIdentity is not implemented"); + return 0; + /* FIXME! + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"identity\":\""); + fd_textstream_encode_base58(ts, ctx->identity->uc, sizeof(fd_pubkey_t)); + fd_textstream_sprintf(ts, "\"},\"id\":%lu}" CRLF, ctx->call_id); + fd_web_replier_done(replier); + return 0; + */ +} +// Implementation of the "getInflationGovernor" methods +static int +method_getInflationGovernor(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getInflationGovernor is not implemented"); + return 0; +} + +// Implementation of the "getInflationRate" methods +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc":"2.0","id":1, "method":"getInflationRate"} ' + +static int +method_getInflationRate(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void) values; + (void)ctx; + fd_web_replier_error(replier, "getInflationRate is not implemented"); + return 0; + /* FIXME! + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_inflation_rates_t rates; + calculate_inflation_rates( get_slot_ctx(ctx), &rates ); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"epoch\":%lu,\"foundation\":%.18f,\"total\":%.18f,\"validator\":%.18f},\"id\":%lu}" CRLF, + rates.epoch, + rates.foundation, + rates.total, + rates.validator, + ctx->call_id); + fd_web_replier_done(replier); + return 0; + */ +} + +// Implementation of the "getInflationReward" methods +static int +method_getInflationReward(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getInflationReward is not implemented"); + return 0; +} + +// Implementation of the "getLargestAccounts" methods +static int +method_getLargestAccounts(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getLargestAccounts is not implemented"); + return 0; +} + +// Implementation of the "getLatestBlockhash" methods +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc":"2.0","id":1, "method":"getLatestBlockhash"} ' + +static int +method_getLatestBlockhash(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + FD_METHOD_SCRATCH_BEGIN( 1UL<<26 ) { + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_slot_bank_t * slot_bank = read_slot_bank(ctx, fd_scratch_virtual()); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"" API_VERSION "\",\"slot\":%lu},\"value\":{\"blockhash\":\"", + slot_bank->slot); + fd_textstream_encode_base58(ts, slot_bank->poh.uc, sizeof(fd_pubkey_t)); + fd_textstream_sprintf(ts, "\",\"lastValidBlockHeight\":%lu}},\"id\":%lu}" CRLF, + slot_bank->block_height, ctx->call_id); + fd_web_replier_done(replier); + } FD_METHOD_SCRATCH_END; + return 0; +} + +// Implementation of the "getLeaderSchedule" methods +// TODO +static int +method_getLeaderSchedule(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getLeaderSchedule is not implemented"); + return 0; +} + +// Implementation of the "getMaxRetransmitSlot" methods +static int +method_getMaxRetransmitSlot(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getMaxRetransmitSlot is not implemented"); + return 0; +} + +// Implementation of the "getMaxShredInsertSlot" methods +static int +method_getMaxShredInsertSlot(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getMaxShredInsertSlot is not implemented"); + return 0; +} + +// Implementation of the "getMinimumBalanceForRentExemption" methods +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc": "2.0", "id": 1, "method": "getMinimumBalanceForRentExemption", "params": [50]} ' + +static int +method_getMinimumBalanceForRentExemption(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + FD_METHOD_SCRATCH_BEGIN( 1<<28 ) { /* read_epoch consumes a ton of scratch space! */ + static const uint PATH_SIZE[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 0, + (JSON_TOKEN_INTEGER<<16) + }; + ulong size_sz = 0; + const void* size = json_get_value(values, PATH_SIZE, 3, &size_sz); + ulong sizen = (size == NULL ? 0UL : (ulong)(*(long*)size)); + ulong smr; + fd_epoch_bank_t * epoch_bank = read_epoch_bank(ctx, fd_scratch_virtual(), &smr); + ulong min_balance = fd_rent_exempt_minimum_balance2(&epoch_bank->rent, sizen); + + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":%lu,\"id\":%lu}" CRLF, + min_balance, ctx->call_id); + fd_web_replier_done(replier); + fd_readwrite_end_read( &ctx->global->lock ); + } FD_METHOD_SCRATCH_END; + return 0; +} + +// Implementation of the "getMultipleAccounts" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc": "2.0", "id": 1, "method": "getMultipleAccounts", "params": [["Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB", "Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB", "7935owQYeYk1H6HjzKRYnT1aZpf1uXcpZNYjgTZ8q7VR"], {"encoding": "base64"}]} ' + +static int +method_getMultipleAccounts(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + FD_METHOD_SCRATCH_BEGIN( 11<<20 ) { + static const uint ENC_PATH[4] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_ENCODING, + (JSON_TOKEN_STRING<<16) + }; + ulong enc_str_sz = 0; + const void* enc_str = json_get_value(values, ENC_PATH, 4, &enc_str_sz); + fd_rpc_encoding_t enc; + if (enc_str == NULL || MATCH_STRING(enc_str, enc_str_sz, "base58")) + enc = FD_ENC_BASE58; + else if (MATCH_STRING(enc_str, enc_str_sz, "base64")) + enc = FD_ENC_BASE64; + else if (MATCH_STRING(enc_str, enc_str_sz, "base64+zstd")) + enc = FD_ENC_BASE64_ZSTD; + else if (MATCH_STRING(enc_str, enc_str_sz, "jsonParsed")) + enc = FD_ENC_JSON; + else { + fd_web_replier_error(replier, "invalid data encoding %s", (const char*)enc_str); + return 0; + } + + fd_blockstore_t * blockstore = ctx->global->blockstore; + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"" API_VERSION "\",\"slot\":%lu},\"value\":[", + blockstore->smr); + + // Iterate through account ids + for ( ulong i = 0; ; ++i ) { + // Path to argument + uint path[4]; + path[0] = (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS; + path[1] = (JSON_TOKEN_LBRACKET<<16) | 0; + path[2] = (uint) ((JSON_TOKEN_LBRACKET<<16) | i); + path[3] = (JSON_TOKEN_STRING<<16); + ulong arg_sz = 0; + const void* arg = json_get_value(values, path, 4, &arg_sz); + if (arg == NULL) + // End of list + break; + + if (i > 0) + fd_textstream_append(ts, ",", 1); + + fd_pubkey_t acct; + fd_base58_decode_32((const char *)arg, acct.uc); + fd_scratch_push(); + ulong val_sz; + void * val = read_account(ctx, &acct, fd_scratch_virtual(), &val_sz); + if (val == NULL) { + fd_textstream_sprintf(ts, "null"); + continue; + } + + fd_textstream_sprintf(ts, "{\"data\":[\""); + + fd_account_meta_t * metadata = (fd_account_meta_t *)val; + if (val_sz < metadata->hlen) { + fd_web_replier_error(replier, "failed to load account data for %s", (const char*)arg); + return 0; + } + val = (char*)val + metadata->hlen; + val_sz = val_sz - metadata->hlen; + if (val_sz > metadata->dlen) + val_sz = metadata->dlen; + + if (val_sz) { + switch (enc) { + case FD_ENC_BASE58: + if (fd_textstream_encode_base58(ts, val, val_sz)) { + fd_web_replier_error(replier, "failed to encode data in base58"); + return 0; + } + break; + case FD_ENC_BASE64: + if (fd_textstream_encode_base64(ts, val, val_sz)) { + fd_web_replier_error(replier, "failed to encode data in base64"); + return 0; + } + break; + default: + break; + } + } + + char owner[50]; + fd_base58_encode_32((uchar*)metadata->info.owner, 0, owner); + fd_textstream_sprintf(ts, "\",\"%s\"],\"executable\":%s,\"lamports\":%lu,\"owner\":\"%s\",\"rentEpoch\":%lu,\"space\":%lu}", + (const char*)enc_str, + (metadata->info.executable ? "true" : "false"), + metadata->info.lamports, + owner, + metadata->info.rent_epoch, + val_sz); + + fd_scratch_pop(); + } + + fd_textstream_sprintf(ts, "]},\"id\":%lu}" CRLF, ctx->call_id); + fd_web_replier_done(replier); + } FD_METHOD_SCRATCH_END; + return 0; +} + +// Implementation of the "getProgramAccounts" methods +static int +method_getProgramAccounts(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getProgramAccounts is not implemented"); + return 0; +} + +// Implementation of the "getRecentBlockhash" methods +static int +method_getRecentBlockhash(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getRecentBlockhash is not implemented"); + return 0; +} + +// Implementation of the "getRecentPerformanceSamples" methods +static int +method_getRecentPerformanceSamples(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getRecentPerformanceSamples is not implemented"); + return 0; +} + +// Implementation of the "getRecentPrioritizationFees" methods +static int +method_getRecentPrioritizationFees(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getRecentPrioritizationFees is not implemented"); + return 0; +} + +// Implementation of the "getSignaturesForAddress" methods +static int +method_getSignaturesForAddress(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getSignaturesForAddress is not implemented"); + return 0; +} + +// Implementation of the "getSignatureStatuses" methods +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc": "2.0", "id": 1, "method": "getSignatureStatuses", "params": [["4qj8WecUytFE96SFhdiTkc3v2AYLY7795sbSQTnYG7cPL9s6xKNHNyi3wraQc83PsNSgV8yedWbfGa4vRXfzBDzB"], {"searchTransactionHistory": true}]} ' + +static int +method_getSignatureStatuses(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_blockstore_t * blockstore = ctx->global->blockstore; + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"" API_VERSION "\",\"slot\":%lu},\"value\":[", + blockstore->smr); + + // Iterate through account ids + for ( ulong i = 0; ; ++i ) { + // Path to argument + uint path[4]; + path[0] = (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS; + path[1] = (JSON_TOKEN_LBRACKET<<16) | 0; + path[2] = (uint) ((JSON_TOKEN_LBRACKET<<16) | i); + path[3] = (JSON_TOKEN_STRING<<16); + ulong sig_sz = 0; + const void* sig = json_get_value(values, path, 4, &sig_sz); + if (sig == NULL) + // End of list + break; + + if (i > 0) + fd_textstream_append(ts, ",", 1); + + uchar key[FD_ED25519_SIG_SZ]; + if ( fd_base58_decode_64( sig, key ) == NULL ) { + fd_textstream_sprintf(ts, "null"); + continue; + } + fd_blockstore_txn_map_t elem; + if( fd_blockstore_txn_query_volatile( blockstore, key, &elem, NULL, NULL ) ) { + fd_textstream_sprintf(ts, "null"); + continue; + } + + // TODO other fields + fd_textstream_sprintf(ts, "{\"slot\":%lu,\"confirmations\":null,\"err\":null,\"confirmationStatus\":\"finalized\"}", + elem.slot); + } + + fd_textstream_sprintf(ts, "]},\"id\":%lu}" CRLF, ctx->call_id); + fd_web_replier_done(replier); + return 0; +} + +// Implementation of the "getSlot" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc":"2.0","id":1, "method":"getSlot"} ' + +static int +method_getSlot(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void) values; + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_blockstore_t * blockstore = ctx->global->blockstore; + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":%lu,\"id\":%lu}" CRLF, + blockstore->smr, ctx->call_id); + fd_web_replier_done(replier); + return 0; +} + +// Implementation of the "getSlotLeader" methods +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc":"2.0","id":1, "method":"getSlotLeader"} ' + +static int +method_getSlotLeader(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getSlotLeader is not implemented"); + /* FIXME! + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":\""); + fd_pubkey_t const * leader = fd_epoch_leaders_get(fd_exec_epoch_ctx_leaders( ctx->replay->epoch_ctx ), get_slot_ctx(ctx)->slot_bank.slot); + fd_textstream_encode_base58(ts, leader->uc, sizeof(fd_pubkey_t)); + fd_textstream_sprintf(ts, "\",\"id\":%lu}" CRLF, ctx->call_id); + fd_web_replier_done(replier); + */ + return 0; +} + +// Implementation of the "getSlotLeaders" methods +static int +method_getSlotLeaders(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getSlotLeaders is not implemented"); + return 0; +} + +// Implementation of the "getSnapshotSlot" methods +static int +method_getSnapshotSlot(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getSnapshotSlot is not implemented"); + return 0; +} + +// Implementation of the "getStakeActivation" methods +static int +method_getStakeActivation(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getStakeActivation is not implemented"); + return 0; +} + +// Implementation of the "getStakeMinimumDelegation" methods +static int +method_getStakeMinimumDelegation(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getStakeMinimumDelegation is not implemented"); + return 0; +} + +// Implementation of the "getSupply" methods +// TODO +static int +method_getSupply(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getSupply is not implemented"); + return 0; +} + +// Implementation of the "getTokenAccountBalance" methods +static int +method_getTokenAccountBalance(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getTokenAccountBalance is not implemented"); + return 0; +} + +// Implementation of the "getTokenAccountsByDelegate" methods +static int +method_getTokenAccountsByDelegate(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getTokenAccountsByDelegate is not implemented"); + return 0; +} + +// Implementation of the "getTokenAccountsByOwner" methods +static int +method_getTokenAccountsByOwner(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getTokenAccountsByOwner is not implemented"); + return 0; +} + +// Implementation of the "getTokenLargestAccounts" methods +static int +method_getTokenLargestAccounts(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getTokenLargestAccounts is not implemented"); + return 0; +} + +// Implementation of the "getTokenSupply" methods +static int +method_getTokenSupply(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getTokenSupply is not implemented"); + return 0; +} + +// Implementation of the "getTransaction" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": ["4qj8WecUytFE96SFhdiTkc3v2AYLY7795sbSQTnYG7cPL9s6xKNHNyi3wraQc83PsNSgV8yedWbfGa4vRXfzBDzB", "json"]} ' + +static int +method_getTransaction(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + static const uint PATH_SIG[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 0, + (JSON_TOKEN_STRING<<16) + }; + static const uint PATH_ENCODING[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_STRING<<16) + }; + + ulong sig_sz = 0; + const void* sig = json_get_value(values, PATH_SIG, 3, &sig_sz); + if (sig == NULL) { + fd_web_replier_error(replier, "getTransaction requires a signature as first parameter"); + return 0; + } + + ulong enc_str_sz = 0; + const void* enc_str = json_get_value(values, PATH_ENCODING, 3, &enc_str_sz); + fd_rpc_encoding_t enc; + if (enc_str == NULL || MATCH_STRING(enc_str, enc_str_sz, "json")) + enc = FD_ENC_JSON; + else if (MATCH_STRING(enc_str, enc_str_sz, "base58")) + enc = FD_ENC_BASE58; + else if (MATCH_STRING(enc_str, enc_str_sz, "base64")) + enc = FD_ENC_BASE64; + else if (MATCH_STRING(enc_str, enc_str_sz, "jsonParsed")) + enc = FD_ENC_JSON_PARSED; + else { + fd_web_replier_error(replier, "invalid data encoding %s", (const char*)enc_str); + return 0; + } + + fd_textstream_t * ts = fd_web_replier_textstream(replier); + + uchar key[FD_ED25519_SIG_SZ]; + if ( fd_base58_decode_64( sig, key) == NULL ) { + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":%lu}" CRLF, ctx->call_id); + fd_web_replier_done(replier); + return 0; + } + fd_blockstore_txn_map_t elem; + long blk_ts; + uchar txn_data_raw[FD_TXN_MTU]; + fd_blockstore_t * blockstore = ctx->global->blockstore; + if( fd_blockstore_txn_query_volatile( blockstore, key, &elem, &blk_ts, txn_data_raw ) ) { + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":%lu}" CRLF, ctx->call_id); + fd_web_replier_done(replier); + return 0; + } + + uchar txn_out[FD_TXN_MAX_SZ]; + ulong pay_sz = 0; + ulong txn_sz = fd_txn_parse_core(txn_data_raw, elem.sz, txn_out, NULL, &pay_sz, 0); + if ( txn_sz == 0 || txn_sz > FD_TXN_MAX_SZ ) + FD_LOG_ERR(("failed to parse transaction")); + + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"" API_VERSION "\",\"slot\":%lu},\"blockTime\":%ld,\"slot\":%lu,", + blockstore->smr, blk_ts/(long)1e9, elem.slot); + fd_txn_to_json( ts, (fd_txn_t *)txn_out, txn_data_raw, enc, 0, FD_BLOCK_DETAIL_FULL, 0 ); + fd_textstream_sprintf(ts, "},\"id\":%lu}" CRLF, ctx->call_id); + + fd_web_replier_done(replier); + return 0; +} + +// Implementation of the "getTransactionCount" methods +static int +method_getTransactionCount(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "getTransactionCount is not implemented"); + return 0; +} + +// Implementation of the "getVersion" method +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d ' {"jsonrpc":"2.0","id":1, "method":"getVersion"} ' + +static int +method_getVersion(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void) values; + fd_textstream_t * ts = fd_web_replier_textstream(replier); + /* TODO Where does feature-set come from? */ + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"feature-set\":666,\"solana-core\":\"" API_VERSION "\"},\"id\":%lu}" CRLF, + ctx->call_id); + fd_web_replier_done(replier); + return 0; +} + +static void +vote_account_to_json(fd_textstream_t * ts, fd_vote_accounts_pair_t_mapnode_t * vote_node) { + fd_textstream_sprintf(ts, "{\"commission\":0,\"epochVoteAccount\":true,\"epochCredits\":[[1,64,0],[2,192,64]],\"nodePubkey\":\")"); + fd_textstream_encode_base58(ts, vote_node->elem.value.owner.uc, sizeof(fd_pubkey_t)); + fd_textstream_sprintf(ts, "\",\"lastVote\":147,\"activatedStake\":%lu,\"votePubkey\":\"", + vote_node->elem.value.lamports); + fd_textstream_encode_base58(ts, vote_node->elem.key.uc, sizeof(fd_pubkey_t)); + fd_textstream_sprintf(ts, "\"}"); +} + +// Implementation of the "getVoteAccounts" methods +// curl http://localhost:8123 -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "id": 1, "method": "getVoteAccounts", "params": [ { "votePubkey": "6j9YPqDdYWc9NWrmV6tSLygog9CrkG9BfYHb5zu9eidH" } ] }' + +static int +method_getVoteAccounts(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + FD_METHOD_SCRATCH_BEGIN( 1<<28 ) { /* read_epoch consumes a ton of scratch space! */ + ulong smr; + fd_epoch_bank_t * epoch_bank = read_epoch_bank(ctx, fd_scratch_virtual(), &smr); + fd_vote_accounts_t * accts = &epoch_bank->stakes.vote_accounts; + fd_vote_accounts_pair_t_mapnode_t * root = accts->vote_accounts_root; + fd_vote_accounts_pair_t_mapnode_t * pool = accts->vote_accounts_pool; + + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"current\":["); + + int needcomma = 0; + for ( ulong i = 0; ; ++i ) { + // Path to argument + uint path[4]; + path[0] = (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS; + path[1] = (uint) ((JSON_TOKEN_LBRACKET<<16) | i); + path[2] = (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_VOTEPUBKEY; + path[3] = (JSON_TOKEN_STRING<<16); + ulong arg_sz = 0; + const void* arg = json_get_value(values, path, 4, &arg_sz); + if (arg == NULL) + // End of list + break; + + fd_vote_accounts_pair_t_mapnode_t key = { 0 }; + fd_base58_decode_32((const char *)arg, key.elem.key.uc); + fd_vote_accounts_pair_t_mapnode_t * vote_node = fd_vote_accounts_pair_t_map_find( pool, root, &key ); + if( vote_node == NULL ) continue; + + if( needcomma ) fd_textstream_sprintf(ts, ","); + vote_account_to_json(ts, vote_node); + needcomma = 1; + } + + fd_textstream_sprintf(ts, "],\"delinquent\":[]},\"id\":%lu}" CRLF, ctx->call_id); + fd_web_replier_done(replier); + fd_readwrite_end_read( &ctx->global->lock ); + } FD_METHOD_SCRATCH_END; + return 0; +} + +// Implementation of the "isBlockhashValid" methods +static int +method_isBlockhashValid(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "isBlockhashValid is not implemented"); + return 0; +} + +// Implementation of the "minimumLedgerSlot" methods +static int +method_minimumLedgerSlot(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "minimumLedgerSlot is not implemented"); + return 0; +} + +// Implementation of the "requestAirdrop" methods +static int +method_requestAirdrop(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "requestAirdrop is not implemented"); + return 0; +} + +// Implementation of the "sendTransaction" methods +static int +method_sendTransaction(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "sendTransaction is not implemented"); + return 0; +} + +// Implementation of the "simulateTransaction" methods +static int +method_simulateTransaction(struct fd_web_replier* replier, struct json_values* values, fd_rpc_ctx_t * ctx) { + (void)values; + (void)ctx; + fd_web_replier_error(replier, "simulateTransaction is not implemented"); + return 0; +} + +// Top level method dispatch function +void +fd_webserver_method_generic(struct fd_web_replier* replier, struct json_values* values, void * cb_arg) { + fd_rpc_ctx_t ctx = *( fd_rpc_ctx_t *)cb_arg; + + static const uint PATH[2] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_JSONRPC, + (JSON_TOKEN_STRING<<16) + }; + ulong arg_sz = 0; + const void* arg = json_get_value(values, PATH, 2, &arg_sz); + if (arg == NULL) { + fd_web_replier_error(replier, "missing jsonrpc member"); + return; + } + if (!MATCH_STRING(arg, arg_sz, "2.0")) { + fd_web_replier_error(replier, "jsonrpc value must be 2.0"); + return; + } + + static const uint PATH3[2] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_ID, + (JSON_TOKEN_INTEGER<<16) + }; + arg_sz = 0; + arg = json_get_value(values, PATH3, 2, &arg_sz); + if (arg == NULL) { + fd_web_replier_error(replier, "missing id member"); + return; + } + ctx.call_id = *(long*)arg; + + static const uint PATH2[2] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_METHOD, + (JSON_TOKEN_STRING<<16) + }; + arg_sz = 0; + arg = json_get_value(values, PATH2, 2, &arg_sz); + if (arg == NULL) { + fd_web_replier_error(replier, "missing method member"); + return; + } + long meth_id = fd_webserver_json_keyword((const char*)arg, arg_sz); + + switch (meth_id) { + case KEYW_RPCMETHOD_GETACCOUNTINFO: + if (!method_getAccountInfo(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETBALANCE: + if (!method_getBalance(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETBLOCK: + if (!method_getBlock(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETBLOCKCOMMITMENT: + if (!method_getBlockCommitment(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETBLOCKHEIGHT: + if (!method_getBlockHeight(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETBLOCKPRODUCTION: + if (!method_getBlockProduction(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETBLOCKS: + if (!method_getBlocks(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETBLOCKSWITHLIMIT: + if (!method_getBlocksWithLimit(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETBLOCKTIME: + if (!method_getBlockTime(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETCLUSTERNODES: + if (!method_getClusterNodes(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETCONFIRMEDBLOCK: + if (!method_getConfirmedBlock(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETCONFIRMEDBLOCKS: + if (!method_getConfirmedBlocks(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETCONFIRMEDBLOCKSWITHLIMIT: + if (!method_getConfirmedBlocksWithLimit(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETCONFIRMEDSIGNATURESFORADDRESS2: + if (!method_getConfirmedSignaturesForAddress2(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETCONFIRMEDTRANSACTION: + if (!method_getConfirmedTransaction(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETEPOCHINFO: + if (!method_getEpochInfo(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETEPOCHSCHEDULE: + if (!method_getEpochSchedule(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETFEECALCULATORFORBLOCKHASH: + if (!method_getFeeCalculatorForBlockhash(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETFEEFORMESSAGE: + if (!method_getFeeForMessage(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETFEERATEGOVERNOR: + if (!method_getFeeRateGovernor(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETFEES: + if (!method_getFees(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETFIRSTAVAILABLEBLOCK: + if (!method_getFirstAvailableBlock(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETGENESISHASH: + if (!method_getGenesisHash(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETHEALTH: + if (!method_getHealth(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETHIGHESTSNAPSHOTSLOT: + if (!method_getHighestSnapshotSlot(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETIDENTITY: + if (!method_getIdentity(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETINFLATIONGOVERNOR: + if (!method_getInflationGovernor(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETINFLATIONRATE: + if (!method_getInflationRate(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETINFLATIONREWARD: + if (!method_getInflationReward(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETLARGESTACCOUNTS: + if (!method_getLargestAccounts(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETLATESTBLOCKHASH: + if (!method_getLatestBlockhash(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETLEADERSCHEDULE: + if (!method_getLeaderSchedule(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETMAXRETRANSMITSLOT: + if (!method_getMaxRetransmitSlot(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETMAXSHREDINSERTSLOT: + if (!method_getMaxShredInsertSlot(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETMINIMUMBALANCEFORRENTEXEMPTION: + if (!method_getMinimumBalanceForRentExemption(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETMULTIPLEACCOUNTS: + if (!method_getMultipleAccounts(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETPROGRAMACCOUNTS: + if (!method_getProgramAccounts(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETRECENTBLOCKHASH: + if (!method_getRecentBlockhash(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETRECENTPERFORMANCESAMPLES: + if (!method_getRecentPerformanceSamples(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETRECENTPRIORITIZATIONFEES: + if (!method_getRecentPrioritizationFees(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETSIGNATURESFORADDRESS: + if (!method_getSignaturesForAddress(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETSIGNATURESTATUSES: + if (!method_getSignatureStatuses(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETSLOT: + if (!method_getSlot(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETSLOTLEADER: + if (!method_getSlotLeader(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETSLOTLEADERS: + if (!method_getSlotLeaders(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETSNAPSHOTSLOT: + if (!method_getSnapshotSlot(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETSTAKEACTIVATION: + if (!method_getStakeActivation(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETSTAKEMINIMUMDELEGATION: + if (!method_getStakeMinimumDelegation(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETSUPPLY: + if (!method_getSupply(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETTOKENACCOUNTBALANCE: + if (!method_getTokenAccountBalance(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETTOKENACCOUNTSBYDELEGATE: + if (!method_getTokenAccountsByDelegate(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETTOKENACCOUNTSBYOWNER: + if (!method_getTokenAccountsByOwner(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETTOKENLARGESTACCOUNTS: + if (!method_getTokenLargestAccounts(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETTOKENSUPPLY: + if (!method_getTokenSupply(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETTRANSACTION: + if (!method_getTransaction(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETTRANSACTIONCOUNT: + if (!method_getTransactionCount(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETVERSION: + if (!method_getVersion(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_GETVOTEACCOUNTS: + if (!method_getVoteAccounts(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_ISBLOCKHASHVALID: + if (!method_isBlockhashValid(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_MINIMUMLEDGERSLOT: + if (!method_minimumLedgerSlot(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_REQUESTAIRDROP: + if (!method_requestAirdrop(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_SENDTRANSACTION: + if (!method_sendTransaction(replier, values, &ctx)) + return; + break; + case KEYW_RPCMETHOD_SIMULATETRANSACTION: + if (!method_simulateTransaction(replier, values, &ctx)) + return; + break; + default: + fd_web_replier_error(replier, "unknown or unimplemented method %s", (const char*)arg); + return; + } + + /* Probably should make an error here */ + static const char* DOC= + "" CRLF + "" CRLF + "OK" CRLF + "" CRLF + "" CRLF + "" CRLF + ""; + fd_textstream_t * ts = fd_web_replier_textstream(replier); + fd_textstream_append(ts, DOC, strlen(DOC)); + fd_web_replier_done(replier); +} + +static int +ws_method_accountSubscribe(fd_websocket_ctx_t * wsctx, struct json_values * values, fd_rpc_ctx_t * ctx, fd_textstream_t * ts) { + FD_METHOD_SCRATCH_BEGIN( 11<<20 ) { + // Path to argument + static const uint PATH[3] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 0, + (JSON_TOKEN_STRING<<16) + }; + ulong arg_sz = 0; + const void* arg = json_get_value(values, PATH, 3, &arg_sz); + if (arg == NULL) { + fd_web_ws_error(wsctx, "getAccountInfo requires a string as first parameter"); + return 0; + } + fd_pubkey_t acct; + fd_base58_decode_32((const char *)arg, acct.uc); + + static const uint PATH2[4] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_ENCODING, + (JSON_TOKEN_STRING<<16) + }; + ulong enc_str_sz = 0; + const void* enc_str = json_get_value(values, PATH2, 4, &enc_str_sz); + fd_rpc_encoding_t enc; + if (enc_str == NULL || MATCH_STRING(enc_str, enc_str_sz, "base58")) + enc = FD_ENC_BASE58; + else if (MATCH_STRING(enc_str, enc_str_sz, "base64")) + enc = FD_ENC_BASE64; + else if (MATCH_STRING(enc_str, enc_str_sz, "base64+zstd")) + enc = FD_ENC_BASE64_ZSTD; + else if (MATCH_STRING(enc_str, enc_str_sz, "jsonParsed")) + enc = FD_ENC_JSON; + else { + fd_web_ws_error(wsctx, "invalid data encoding %s", (const char*)enc_str); + return 0; + } + + static const uint PATH3[5] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_DATASLICE, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_LENGTH, + (JSON_TOKEN_INTEGER<<16) + }; + static const uint PATH4[5] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_PARAMS, + (JSON_TOKEN_LBRACKET<<16) | 1, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_DATASLICE, + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_OFFSET, + (JSON_TOKEN_INTEGER<<16) + }; + ulong len_sz = 0; + const void* len_ptr = json_get_value(values, PATH3, 5, &len_sz); + ulong off_sz = 0; + const void* off_ptr = json_get_value(values, PATH4, 5, &off_sz); + if (len_ptr && off_ptr) { + if (enc == FD_ENC_JSON) { + fd_web_ws_error(wsctx, "cannot use jsonParsed encoding with slice"); + return 0; + } + } + + fd_rpc_global_ctx_t * subs = ctx->global; + fd_readwrite_start_write( &subs->lock ); + if( subs->sub_cnt >= FD_WS_MAX_SUBS ) { + fd_readwrite_end_write( &subs->lock ); + fd_web_ws_error(wsctx, "too many subscriptions"); + return 0; + } + struct fd_ws_subscription * sub = &subs->sub_list[ subs->sub_cnt++ ]; + sub->socket = wsctx; + sub->meth_id = KEYW_WS_METHOD_ACCOUNTSUBSCRIBE; + sub->call_id = ctx->call_id; + ulong subid = sub->subsc_id = ++(subs->last_subsc_id); + sub->acct_subscribe.acct = acct; + sub->acct_subscribe.enc = enc; + sub->acct_subscribe.off = (off_ptr ? *(long*)off_ptr : FD_LONG_UNSET); + sub->acct_subscribe.len = (len_ptr ? *(long*)len_ptr : FD_LONG_UNSET); + fd_readwrite_end_write( &subs->lock ); + + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":%lu,\"id\":%lu}" CRLF, + subid, sub->call_id); + + } FD_METHOD_SCRATCH_END; + + return 1; +} + +static int +ws_method_accountSubscribe_update(fd_rpc_ctx_t * ctx, fd_replay_notif_msg_t * msg, struct fd_ws_subscription * sub, fd_textstream_t * ts) { + FD_METHOD_SCRATCH_BEGIN( 11<<20 ) { + fd_websocket_ctx_t * wsctx = sub->socket; + + ulong val_sz; + void * val = read_account_with_xid(ctx, &sub->acct_subscribe.acct, &msg->acct_saved.funk_xid, fd_scratch_virtual(), &val_sz); + if (val == NULL) { + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"" API_VERSION "\",\"slot\":%lu},\"value\":null},\"subscription\":%lu}" CRLF, + msg->acct_saved.funk_xid.ul[0], sub->subsc_id); + return 1; + } + + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"method\":\"accountNotification\",\"params\":{\"result\":{\"context\":{\"apiVersion\":\"" API_VERSION "\",\"slot\":%lu},\"value\":", + msg->acct_saved.funk_xid.ul[0]); + const char * err = fd_account_to_json( ts, sub->acct_subscribe.acct, sub->acct_subscribe.enc, val, val_sz, sub->acct_subscribe.off, sub->acct_subscribe.len ); + if( err ) { + fd_web_ws_error(wsctx, "%s", err); + return 0; + } + fd_textstream_sprintf(ts, "},\"subscription\":%lu}}" CRLF, sub->subsc_id); + } FD_METHOD_SCRATCH_END; + + return 1; +} + +static int +ws_method_slotSubscribe(fd_websocket_ctx_t * wsctx, struct json_values * values, fd_rpc_ctx_t * ctx, fd_textstream_t * ts) { + (void)values; + fd_rpc_global_ctx_t * subs = ctx->global; + fd_readwrite_start_write( &subs->lock ); + if( subs->sub_cnt >= FD_WS_MAX_SUBS ) { + fd_readwrite_end_write( &subs->lock ); + fd_web_ws_error(wsctx, "too many subscriptions"); + return 0; + } + struct fd_ws_subscription * sub = &subs->sub_list[ subs->sub_cnt++ ]; + sub->socket = wsctx; + sub->meth_id = KEYW_WS_METHOD_SLOTSUBSCRIBE; + sub->call_id = ctx->call_id; + ulong subid = sub->subsc_id = ++(subs->last_subsc_id); + fd_readwrite_end_write( &subs->lock ); + + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"result\":%lu,\"id\":%lu}" CRLF, + subid, sub->call_id); + + return 1; +} + +static int +ws_method_slotSubscribe_update(fd_rpc_ctx_t * ctx, fd_replay_notif_msg_t * msg, struct fd_ws_subscription * sub, fd_textstream_t * ts) { + (void)ctx; + char bank_hash[50]; + fd_base58_encode_32(msg->slot_exec.bank_hash.uc, 0, bank_hash); + fd_textstream_sprintf(ts, "{\"jsonrpc\":\"2.0\",\"method\":\"slotNotification\",\"params\":{\"result\":{\"parent\":%lu,\"root\":%lu,\"slot\":%lu,\"bank_hash\":\"%s\"},\"subscription\":%lu}}" CRLF, + msg->slot_exec.parent, msg->slot_exec.root, msg->slot_exec.slot, + bank_hash, sub->subsc_id); + return 1; +} + +int +fd_webserver_ws_subscribe(struct json_values* values, fd_websocket_ctx_t * wsctx, void * cb_arg) { + fd_rpc_ctx_t ctx = *( fd_rpc_ctx_t *)cb_arg; + + static const uint PATH[2] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_JSONRPC, + (JSON_TOKEN_STRING<<16) + }; + ulong arg_sz = 0; + const void* arg = json_get_value(values, PATH, 2, &arg_sz); + if (arg == NULL) { + fd_web_ws_error( wsctx, "missing jsonrpc member" ); + return 0; + } + if (!MATCH_STRING(arg, arg_sz, "2.0")) { + fd_web_ws_error( wsctx, "jsonrpc value must be 2.0" ); + return 0; + } + + static const uint PATH3[2] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_ID, + (JSON_TOKEN_INTEGER<<16) + }; + arg_sz = 0; + arg = json_get_value(values, PATH3, 2, &arg_sz); + if (arg == NULL) { + fd_web_ws_error( wsctx, "missing id member" ); + return 0; + } + ctx.call_id = *(long*)arg; + + static const uint PATH2[2] = { + (JSON_TOKEN_LBRACE<<16) | KEYW_JSON_METHOD, + (JSON_TOKEN_STRING<<16) + }; + arg_sz = 0; + arg = json_get_value(values, PATH2, 2, &arg_sz); + if (arg == NULL) { + fd_web_ws_error( wsctx, "missing method member" ); + return 0; + } + long meth_id = fd_webserver_json_keyword((const char*)arg, arg_sz); + + fd_textstream_t ts; + fd_textstream_new(&ts, fd_libc_alloc_virtual(), 1UL<<15UL); + + switch (meth_id) { + case KEYW_WS_METHOD_ACCOUNTSUBSCRIBE: + if (ws_method_accountSubscribe(wsctx, values, &ctx, &ts)) { + fd_web_ws_reply( wsctx, &ts ); + fd_textstream_destroy(&ts); + return 1; + } + return 0; + case KEYW_WS_METHOD_SLOTSUBSCRIBE: + if (ws_method_slotSubscribe(wsctx, values, &ctx, &ts)) { + fd_web_ws_reply( wsctx, &ts ); + fd_textstream_destroy(&ts); + return 1; + } + return 0; + } + + fd_textstream_destroy(&ts); + fd_web_ws_error( wsctx, "unknown websocket method: %s", (const char*)arg ); + return 0; +} + +void +fd_rpc_start_service(fd_rpcserver_args_t * args, fd_rpc_ctx_t ** ctx_p) { + fd_rpc_ctx_t * ctx = (fd_rpc_ctx_t *)malloc(sizeof(fd_rpc_ctx_t)); + fd_rpc_global_ctx_t * gctx = (fd_rpc_global_ctx_t *)malloc(sizeof(fd_rpc_global_ctx_t)); + fd_memset(ctx, 0, sizeof(fd_rpc_ctx_t)); + fd_memset(gctx, 0, sizeof(fd_rpc_global_ctx_t)); + + fd_readwrite_new( &gctx->lock ); + ctx->global = gctx; + gctx->funk = args->funk; + gctx->blockstore = args->blockstore; + + FD_LOG_NOTICE(( "starting web server with %lu threads on port %u", args->num_threads, (uint)args->port )); + if (fd_webserver_start(args->num_threads, args->port, args->ws_port, &gctx->ws, ctx)) + FD_LOG_ERR(("fd_webserver_start failed")); + + *ctx_p = ctx; +} + +void +fd_rpc_stop_service(fd_rpc_ctx_t * ctx) { + FD_LOG_NOTICE(( "stopping web server" )); + if (fd_webserver_stop(&ctx->global->ws)) + FD_LOG_ERR(("fd_webserver_stop failed")); + free(ctx->global); + free(ctx); +} + +void +fd_rpc_ws_poll(fd_rpc_ctx_t * ctx) { + fd_webserver_ws_poll(&ctx->global->ws); +} + +void +fd_webserver_ws_closed(fd_websocket_ctx_t * wsctx, void * cb_arg) { + fd_rpc_ctx_t * ctx = ( fd_rpc_ctx_t *)cb_arg; + fd_rpc_global_ctx_t * subs = ctx->global; + fd_readwrite_start_write( &subs->lock ); + for( ulong i = 0; i < subs->sub_cnt; ++i ) { + if( subs->sub_list[i].socket == wsctx ) { + fd_memcpy( &subs->sub_list[i], &subs->sub_list[--(subs->sub_cnt)], sizeof(struct fd_ws_subscription) ); + --i; + } + } + fd_readwrite_end_write( &subs->lock ); +} + +void +fd_rpc_replay_notify(fd_rpc_ctx_t * ctx, fd_replay_notif_msg_t * msg) { + fd_rpc_global_ctx_t * subs = ctx->global; + fd_readwrite_start_read( &subs->lock ); + + if( subs->sub_cnt == 0 ) { + /* do nothing */ + + } else if( msg->type == FD_REPLAY_SAVED_TYPE ) { + fd_textstream_t ts; + fd_textstream_new(&ts, fd_libc_alloc_virtual(), 11UL<<21); + + /* TODO: replace with a hash table lookup? */ + for( uint i = 0; i < msg->acct_saved.acct_id_cnt; ++i ) { + fd_pubkey_t * id = &msg->acct_saved.acct_id[i]; + for( ulong j = 0; j < subs->sub_cnt; ++j ) { + struct fd_ws_subscription * sub = &subs->sub_list[ j ]; + if( sub->meth_id == KEYW_WS_METHOD_ACCOUNTSUBSCRIBE && + fd_pubkey_eq( id, &sub->acct_subscribe.acct ) ) { + fd_textstream_clear( &ts ); + if( ws_method_accountSubscribe_update( ctx, msg, sub, &ts ) ) + fd_web_ws_reply( sub->socket, &ts ); + } + } + } + + fd_textstream_destroy(&ts); + + } else if( msg->type == FD_REPLAY_SLOT_TYPE ) { + fd_textstream_t ts; + fd_textstream_new(&ts, fd_libc_alloc_virtual(), 1UL<<16); + + for( ulong j = 0; j < subs->sub_cnt; ++j ) { + struct fd_ws_subscription * sub = &subs->sub_list[ j ]; + if( sub->meth_id == KEYW_WS_METHOD_SLOTSUBSCRIBE ) { + fd_textstream_clear( &ts ); + if( ws_method_slotSubscribe_update( ctx, msg, sub, &ts ) ) + fd_web_ws_reply( sub->socket, &ts ); + } + } + + fd_textstream_destroy(&ts); + } + + fd_readwrite_end_read( &subs->lock ); +} diff --git a/src/app/rpcserver/fd_rpc_service.h b/src/app/rpcserver/fd_rpc_service.h new file mode 100644 index 0000000000..8b6b915361 --- /dev/null +++ b/src/app/rpcserver/fd_rpc_service.h @@ -0,0 +1,33 @@ +#ifndef HEADER_fd_src_flamenco_rpc_fd_rpc_service_h +#define HEADER_fd_src_flamenco_rpc_fd_rpc_service_h + +#include "../../util/fd_util.h" +#include "../../funk/fd_funk.h" +#include "../../flamenco/runtime/fd_blockstore.h" +#include "../../tango/mcache/fd_mcache.h" +#include "../../util/textstream/fd_textstream.h" +#include "../fdctl/run/tiles/fd_replay_notif.h" +#include "fd_block_to_json.h" + +struct fd_rpcserver_args { + fd_funk_t * funk; + fd_blockstore_t * blockstore; + fd_wksp_t * rep_notify_wksp; + fd_frag_meta_t * rep_notify; + ulong num_threads; + ushort port; + ushort ws_port; +}; +typedef struct fd_rpcserver_args fd_rpcserver_args_t; + +typedef struct fd_rpc_ctx fd_rpc_ctx_t; + +void fd_rpc_start_service(fd_rpcserver_args_t * args, fd_rpc_ctx_t ** ctx); + +void fd_rpc_stop_service(fd_rpc_ctx_t * ctx); + +void fd_rpc_ws_poll(fd_rpc_ctx_t * ctx); + +void fd_rpc_replay_notify(fd_rpc_ctx_t * ctx, fd_replay_notif_msg_t * msg); + +#endif /* HEADER_fd_src_flamenco_rpc_fd_rpc_service_h */ diff --git a/src/app/rpcserver/fd_webserver.c b/src/app/rpcserver/fd_webserver.c new file mode 100644 index 0000000000..3e44cd87e9 --- /dev/null +++ b/src/app/rpcserver/fd_webserver.c @@ -0,0 +1,364 @@ +#include "../../util/fd_util.h" +#include "../../ballet/base64/fd_base64.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fd_methods.h" +#include "fd_webserver.h" + +struct fd_websocket_ctx { + struct MHD_UpgradeResponseHandle *urh; + MHD_socket sock; + fd_webserver_t * ws; +}; + +// Parse the top level json request object +void json_parse_root(struct fd_web_replier* replier, json_lex_state_t* lex, void* cb_arg) { + struct json_values values; + json_values_new(&values); + + struct json_path path; + path.len = 0; + if (json_values_parse(lex, &values, &path)) { + json_values_printout(&values); + fd_webserver_method_generic(replier, &values, cb_arg); + } else { + ulong sz; + const char* text = json_lex_get_text(lex, &sz); + FD_LOG_WARNING(( "json parsing error: %s", text )); + fd_web_replier_simple_error(replier, text, (uint)sz); + } + + json_values_delete(&values); +} + +struct fd_web_replier { + const char* upload_data; + size_t upload_data_size; + unsigned int status_code; + struct MHD_Response* response; + fd_textstream_t textstream; +}; + +struct fd_web_replier* fd_web_replier_new(void) { + struct fd_web_replier* r = (struct fd_web_replier*)malloc(sizeof(struct fd_web_replier)); + r->upload_data = NULL; + r->upload_data_size = 0; + r->status_code = MHD_HTTP_OK; + r->response = NULL; + fd_textstream_new(&r->textstream, fd_libc_alloc_virtual(), 1UL<<18); // 256KB chunks + return r; +} + +void fd_web_replier_delete(struct fd_web_replier* r) { + if (r->response != NULL) + MHD_destroy_response(r->response); + fd_textstream_destroy(&r->textstream); + free(r); +} + +fd_textstream_t * fd_web_replier_textstream(struct fd_web_replier* r) { + return &r->textstream; +} + +void fd_web_replier_done(struct fd_web_replier* r) { + struct fd_iovec iov[100]; + ulong numiov = fd_textstream_get_iov_count(&r->textstream); + if (numiov > 100 || fd_textstream_get_iov(&r->textstream, iov)) { + fd_web_replier_error(r, "failure in reply generator"); + return; + } + r->status_code = MHD_HTTP_OK; + if (r->response != NULL) + MHD_destroy_response(r->response); +#pragma GCC diagnostic ignored "-Wstrict-aliasing" + r->response = MHD_create_response_from_iovec((struct MHD_IoVec *)iov, (uint)numiov, NULL, NULL); +} + +void fd_web_replier_error( struct fd_web_replier* r, const char* format, ... ) { + char text[4096]; + va_list ap; + va_start(ap, format); + /* Would be nice to vsnprintf directly into the textstream, but that's messy */ + int x = vsnprintf(text, sizeof(text), format, ap); + va_end(ap); + fd_web_replier_simple_error(r, text, (uint)x); +} + +void fd_web_ws_error( fd_websocket_ctx_t * ctx, const char* format, ... ) { + char text[4096]; + va_list ap; + va_start(ap, format); + /* Would be nice to vsnprintf directly into the textstream, but that's messy */ + int x = vsnprintf(text, sizeof(text), format, ap); + va_end(ap); + fd_web_ws_simple_error(ctx, text, (uint)x); +} + +void fd_web_replier_simple_error( struct fd_web_replier* r, const char* text, uint text_size) { +#define CRLF "\r\n" + static const char* DOC1 = +"" CRLF +"" CRLF +"ERROR" CRLF +"" CRLF +"" CRLF +"

"; + static const char* DOC2 = +"

" CRLF +"

Request:

";
+  static const char* DOC3 =
+"

" CRLF +"" CRLF +"" CRLF; + + fd_textstream_t * ts = &r->textstream; + fd_textstream_clear(ts); + fd_textstream_append(ts, DOC1, strlen(DOC1)); + fd_textstream_append(ts, text, text_size); + fd_textstream_append(ts, DOC2, strlen(DOC2)); + fd_textstream_append(ts, r->upload_data, r->upload_data_size); + fd_textstream_append(ts, DOC3, strlen(DOC3)); + + struct fd_iovec iov[100]; + ulong numiov = fd_textstream_get_iov_count(&r->textstream); + if (numiov > 100 || fd_textstream_get_iov(&r->textstream, iov)) { + FD_LOG_ERR(("failure in error reply generator")); + return; + } + + r->status_code = MHD_HTTP_BAD_REQUEST; + if (r->response != NULL) + MHD_destroy_response(r->response); + r->response = MHD_create_response_from_iovec((struct MHD_IoVec *)iov, (uint)numiov, NULL, NULL); +} + +/** + * Signature of the callback used by MHD to notify the + * application about completed requests. + * + * @param cls client-defined closure + * @param connection connection handle + * @param con_cls value as set by the last call to + * the MHD_AccessHandlerCallback + * @param toe reason for request termination + * @see MHD_OPTION_NOTIFY_COMPLETED + */ +static void completed_cb(void* cls, + struct MHD_Connection* connection, + void** con_cls, + enum MHD_RequestTerminationCode toe) +{ + (void) cls; /* Unused. Silent compiler warning. */ + (void) connection; /* Unused. Silent compiler warning. */ + (void) toe; /* Unused. Silent compiler warning. */ + + if (*con_cls != NULL) { + fd_web_replier_delete((struct fd_web_replier*) (*con_cls)); + *con_cls = NULL; + } +} + +/** + * Main MHD callback for handling requests. + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param connection handle identifying the incoming connection + * @param url the requested url + * @param method the HTTP method used ("GET", "PUT", etc.) + * @param version the HTTP version string (i.e. "HTTP/1.1") + * @param upload_data the data being uploaded (excluding HEADERS, + * for a POST that fits into memory and that is encoded + * with a supported encoding, the POST data will NOT be + * given in upload_data and is instead available as + * part of MHD_get_connection_values; very large POST + * data *will* be made available incrementally in + * upload_data) + * @param upload_data_size set initially to the size of the + * upload_data provided; the method must update this + * value to the number of bytes NOT processed; + * @param ptr pointer that the callback can set to some + * address and that will be preserved by MHD for future + * calls for this request; since the access handler may + * be called many times (i.e., for a PUT/POST operation + * with plenty of upload data) this allows the application + * to easily associate some request-specific state. + * If necessary, this state can be cleaned up in the + * global "MHD_RequestCompleted" callback (which + * can be set with the MHD_OPTION_NOTIFY_COMPLETED). + * Initially, *con_cls will be NULL. + * @return MHS_YES if the connection was handled successfully, + * MHS_NO if the socket must be closed due to a serious + * error while handling the request + */ +static enum MHD_Result handler(void* cls, + struct MHD_Connection* connection, + const char* url, + const char* method, + const char* version, + const char* upload_data, + size_t* upload_data_size, + void** con_cls) +{ + (void) url; /* Unused. Silent compiler warning. */ + (void) version; /* Unused. Silent compiler warning. */ + + fd_webserver_t * ws = (fd_webserver_t *)cls; + + if (0 != strcmp (method, "POST")) + return MHD_NO; /* unexpected method */ + + struct fd_web_replier* replier; + if (*con_cls == NULL) + *con_cls = replier = fd_web_replier_new(); + else + replier = (struct fd_web_replier*) (*con_cls); + + size_t sz = *upload_data_size; + if (sz) { + replier->upload_data = upload_data; + replier->upload_data_size = sz; + json_lex_state_t lex; + json_lex_state_new(&lex, upload_data, sz); + json_parse_root(replier, &lex, ws->cb_arg); + json_lex_state_delete(&lex); + *upload_data_size = 0; + } + + // Check if we are done with the request. This is clunky as hell, + // but I didn't design the API. + if (!sz && replier->upload_data_size) { + if (replier->response != NULL) + return MHD_queue_response (connection, replier->status_code, replier->response); + return MHD_NO; + } + return MHD_YES; +} + +int +fd_webserver_ws_request( fd_websocket_ctx_t * ctx, char const * msg, ulong msglen ) { + json_lex_state_t lex; + json_lex_state_new(&lex, msg, msglen); + struct json_values values; + json_values_new(&values); + struct json_path path; + path.len = 0; + int ret = json_values_parse(&lex, &values, &path); + if (ret) { + json_values_printout(&values); + ret = fd_webserver_ws_subscribe(&values, ctx, ctx->ws->cb_arg); + } else { + ulong sz; + const char* text = json_lex_get_text(&lex, &sz); + FD_LOG_WARNING(( "json parsing error: %s", text )); + fd_web_ws_simple_error( ctx, text, (uint)sz ); + } + json_values_delete(&values); + json_lex_state_delete(&lex); + return ret; +} + +#include "fd_websocket_support.h" + +void fd_web_ws_simple_error( fd_websocket_ctx_t * ctx, const char* text, uint text_size) { +#define CRLF "\r\n" + static const char* DOC1 = +"" CRLF +"" CRLF +"ERROR" CRLF +"" CRLF +"" CRLF +"

"; + static const char* DOC2 = +"

" CRLF +"" CRLF +"" CRLF; + + fd_textstream_t ts; + fd_textstream_new(&ts, fd_libc_alloc_virtual(), 1UL<<12); + fd_textstream_append(&ts, DOC1, strlen(DOC1)); + fd_textstream_append(&ts, text, text_size); + fd_textstream_append(&ts, DOC2, strlen(DOC2)); + + char buf[2048]; + ulong sz = fd_textstream_total_size(&ts); + if ( sz <= sizeof(buf) ) { + fd_textstream_get_output( &ts, buf ); + ws_send_frame( ctx->sock, WS_OPCODE_TEXT_FRAME, buf, sz ); + } + + fd_textstream_destroy(&ts); +} + +void fd_web_ws_reply( fd_websocket_ctx_t * ctx, fd_textstream_t * ts) { + char buf[2048]; + ulong sz = fd_textstream_total_size(ts); + if ( sz + WS_MAX_HDR_SIZE <= sizeof(buf) ) { + fd_textstream_get_output( ts, buf + WS_MAX_HDR_SIZE ); + ws_send_frame_prepend_hdr( ctx->sock, WS_OPCODE_TEXT_FRAME, buf + WS_MAX_HDR_SIZE, sz ); + } else { + char * buf2 = malloc(sz + WS_MAX_HDR_SIZE); + fd_textstream_get_output( ts, buf2 + WS_MAX_HDR_SIZE ); + ws_send_frame_prepend_hdr( ctx->sock, WS_OPCODE_TEXT_FRAME, buf2 + WS_MAX_HDR_SIZE, sz ); + free( buf2 ); + } +} + +int fd_webserver_start(ulong num_threads, ushort portno, ushort ws_portno, fd_webserver_t * ws, void * cb_arg) { + ws->cb_arg = cb_arg; + + ws->daemon = MHD_start_daemon( + MHD_USE_INTERNAL_POLLING_THREAD + | MHD_USE_SUPPRESS_DATE_NO_CLOCK + | MHD_USE_AUTO | MHD_USE_TURBO, + portno, + NULL, NULL, &handler, ws, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, + MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) num_threads, + MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, ws, + MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 1000, + MHD_OPTION_END); + if (ws->daemon == NULL) + return -1; + + ws->ws_daemon = MHD_start_daemon(MHD_ALLOW_UPGRADE | MHD_USE_AUTO_INTERNAL_THREAD, + ws_portno, NULL, NULL, &ahc_cb, ws, + MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 1, + MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 1000, + MHD_OPTION_END); + if (ws->ws_daemon == NULL) + return -1; + + ws->ws_epoll_fd = epoll_create1(0); + if( ws->ws_epoll_fd == -1 ) + return -1; + + return 0; +} + +int fd_webserver_stop(fd_webserver_t * ws) { + MHD_stop_daemon(ws->daemon); + MHD_stop_daemon(ws->ws_daemon); + close( ws->ws_epoll_fd ); + return 0; +} + +void fd_webserver_ws_poll(fd_webserver_t * ws) { + struct epoll_event events[16]; + int cnt = epoll_wait( ws->ws_epoll_fd, events, 16, 0 ); + if( cnt < 0 ) { + FD_LOG_ERR(( "epoll_wait failed: %s", strerror(errno) )); + } + for(int i = 0; i < cnt; ++i) { + epoll_selected(events + i); + } +} diff --git a/src/app/rpcserver/fd_webserver.h b/src/app/rpcserver/fd_webserver.h new file mode 100644 index 0000000000..7946ad79af --- /dev/null +++ b/src/app/rpcserver/fd_webserver.h @@ -0,0 +1,50 @@ +#ifndef HEADER_fd_src_tango_webserver_fd_webserver_h +#define HEADER_fd_src_tango_webserver_fd_webserver_h + +#include "fd_methods.h" +#include "../../util/textstream/fd_textstream.h" + +struct fd_webserver { + void * cb_arg; + struct MHD_Daemon * daemon; + struct MHD_Daemon * ws_daemon; + int ws_epoll_fd; +}; +typedef struct fd_webserver fd_webserver_t; + +typedef struct fd_websocket_ctx fd_websocket_ctx_t; + +int fd_webserver_start(ulong num_threads, ushort portno, ushort ws_portno, fd_webserver_t * ws, void * cb_arg); + +int fd_webserver_stop(fd_webserver_t * ws); + +void fd_webserver_ws_poll(fd_webserver_t * ws); + +void fd_webserver_ws_closed(fd_websocket_ctx_t * wsctx, void * cb_arg); + +#ifndef KEYW_UNKNOWN +#define KEYW_UNKNOWN -1L +#endif +long fd_webserver_json_keyword(const char* keyw, size_t keyw_sz); +const char* un_fd_webserver_json_keyword(long id); + +struct fd_web_replier; +void fd_webserver_method_generic(struct fd_web_replier* replier, struct json_values* values, void * cb_arg); + +fd_textstream_t * fd_web_replier_textstream(struct fd_web_replier* replier); +void fd_web_replier_done(struct fd_web_replier* replier); + +typedef struct fd_websocket_ctx fd_websocket_ctx_t; +int fd_webserver_ws_subscribe(struct json_values* values, fd_websocket_ctx_t * ctx, void * cb_arg); + +void fd_web_ws_reply( fd_websocket_ctx_t * ctx, fd_textstream_t * ts); + +void fd_web_replier_error( struct fd_web_replier* replier, const char* format, ... ) + __attribute__ ((format (printf, 2, 3))); +void fd_web_replier_simple_error( struct fd_web_replier* replier, const char* text, uint text_size); + +void fd_web_ws_error( fd_websocket_ctx_t * ctx, const char* format, ... ) + __attribute__ ((format (printf, 2, 3))); +void fd_web_ws_simple_error( fd_websocket_ctx_t * ctx, const char* text, uint text_size); + +#endif /* HEADER_fd_src_tango_webserver_fd_webserver_h */ diff --git a/src/app/rpcserver/fd_websocket_support.h b/src/app/rpcserver/fd_websocket_support.h new file mode 100644 index 0000000000..8832508536 --- /dev/null +++ b/src/app/rpcserver/fd_websocket_support.h @@ -0,0 +1,443 @@ +#include + +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#pragma GCC diagnostic ignored "-Wrestrict" + +#define BAD_REQUEST_PAGE \ + "\n" \ + "\n" \ + "fd_rpcserver\n" \ + "\n" \ + "\n" \ + "Bad Request\n" \ + "\n" \ + "\n" +#define UPGRADE_REQUIRED_PAGE \ + "\n" \ + "\n" \ + "fd_rpcserver\n" \ + "\n" \ + "\n" \ + "Upgrade required\n" \ + "\n" \ + "\n" + +#define WS_SEC_WEBSOCKET_VERSION "13" +#define WS_UPGRADE_VALUE "websocket" +#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define WS_GUID_LEN 36 +#define WS_KEY_LEN 24 +#define WS_KEY_GUID_LEN ((WS_KEY_LEN) + (WS_GUID_LEN)) +#define WS_FIN 128 +#define WS_OPCODE_TEXT_FRAME 1 +#define WS_OPCODE_CLOSE_FRAME 8 +#define WS_OPCODE_PING_FRAME 9 +#define WS_OPCODE_PONG_FRAME 10 +#define SHA1HashSize 20 + +static enum MHD_Result +is_websocket_request (struct MHD_Connection *con, const char *upg_header, + const char *con_header) +{ + + (void) con; /* Unused. Silent compiler warning. */ + + return ((upg_header != NULL) && (con_header != NULL) + && (0 == strcmp (upg_header, WS_UPGRADE_VALUE)) + && (NULL != strstr (con_header, "Upgrade"))) + ? MHD_YES + : MHD_NO; +} + +static void do_nothing(void * arg) { (void)arg; } + +static enum MHD_Result +send_bad_request (struct MHD_Connection *con) +{ + struct MHD_Response *res; + enum MHD_Result ret; + + res = MHD_create_response_from_buffer_with_free_callback (strlen (BAD_REQUEST_PAGE), (void *) BAD_REQUEST_PAGE, do_nothing); + ret = MHD_queue_response (con, MHD_HTTP_BAD_REQUEST, res); + MHD_destroy_response (res); + return ret; +} + +static enum MHD_Result +send_upgrade_required (struct MHD_Connection *con) +{ + struct MHD_Response *res; + enum MHD_Result ret; + + res = MHD_create_response_from_buffer_with_free_callback (strlen (UPGRADE_REQUIRED_PAGE), (void *) UPGRADE_REQUIRED_PAGE, do_nothing); + if (MHD_YES != + MHD_add_response_header (res, MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION, + WS_SEC_WEBSOCKET_VERSION)) + { + MHD_destroy_response (res); + return MHD_NO; + } + ret = MHD_queue_response (con, MHD_HTTP_UPGRADE_REQUIRED, res); + MHD_destroy_response (res); + return ret; +} + +static enum MHD_Result +ws_get_accept_value (const char *key, char * val) +{ + SHA_CTX ctx; + unsigned char hash[SHA1HashSize]; + + if ( (NULL == key) || (WS_KEY_LEN != strlen (key))) + { + return MHD_NO; + } + char str[WS_KEY_LEN + WS_GUID_LEN + 1]; + strncpy (str, key, (WS_KEY_LEN + 1)); + strncpy (str + WS_KEY_LEN, WS_GUID, WS_GUID_LEN + 1); + SHA1_Init (&ctx); + SHA1_Update (&ctx, (const unsigned char *) str, WS_KEY_GUID_LEN); + if (!SHA1_Final (hash, &ctx)) + { + return MHD_NO; + } + ulong len = fd_base64_encode(val, hash, SHA1HashSize); + val[len] = '\0'; + return MHD_YES; +} + +static void +make_blocking (MHD_socket fd) +{ +#if defined(MHD_POSIX_SOCKETS) + int flags; + + flags = fcntl (fd, F_GETFL); + if (-1 == flags) + abort (); + if ((flags & ~O_NONBLOCK) != flags) + if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) + abort (); +#elif defined(MHD_WINSOCK_SOCKETS) + unsigned long flags = 0; + + if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) + abort (); +#endif /* MHD_WINSOCK_SOCKETS */ +} + +static size_t +send_all (MHD_socket sock, const unsigned char *buf, size_t len) +{ + ssize_t ret; + size_t off; + + for (off = 0; off < len; off += (size_t) ret) + { +#if ! defined(_WIN32) || defined(__CYGWIN__) + ret = send (sock, (const void *) &buf[off], len - off, 0); +#else /* Native W32 */ + ret = send (sock, (const void *) &buf[off], (int) (len - off), 0); +#endif /* Native W32 */ + if (0 > ret) + { + if (EAGAIN == errno) + { + ret = 0; + continue; + } + break; + } + if (0 == ret) + { + break; + } + } + return off; +} + +static ssize_t +ws_send_frame (MHD_socket sock, const uchar type, const char *msg, size_t length) +{ + unsigned char *response; + unsigned char frame[10]; + unsigned char idx_first_rdata; + unsigned char buf[1<<13]; + + frame[0] = (WS_FIN | type); + if (length <= 125) + { + frame[1] = length & 0x7F; + idx_first_rdata = 2; + } + else if (0xFFFF < length) + { + frame[1] = 127; + frame[2] = (unsigned char) ((length >> 56) & 0xFF); + frame[3] = (unsigned char) ((length >> 48) & 0xFF); + frame[4] = (unsigned char) ((length >> 40) & 0xFF); + frame[5] = (unsigned char) ((length >> 32) & 0xFF); + frame[6] = (unsigned char) ((length >> 24) & 0xFF); + frame[7] = (unsigned char) ((length >> 16) & 0xFF); + frame[8] = (unsigned char) ((length >> 8) & 0xFF); + frame[9] = (unsigned char) (length & 0xFF); + idx_first_rdata = 10; + } + else + { + frame[1] = 126; + frame[2] = (length >> 8) & 0xFF; + frame[3] = length & 0xFF; + idx_first_rdata = 4; + } + if( idx_first_rdata + length <= sizeof( buf ) ) + response = buf; + else + response = malloc (idx_first_rdata + length); + if (NULL == response) + { + return -1; + } + memcpy(response, frame, idx_first_rdata); + memcpy(response + idx_first_rdata, msg, length); + size_t output = send_all (sock, response, idx_first_rdata + length); + if( response != buf ) free (response); + return (ssize_t) output; +} + +#define WS_MAX_HDR_SIZE 10U + +static ssize_t +ws_send_frame_prepend_hdr(MHD_socket sock, const uchar type, char * msg, size_t length) +{ + uchar * frame; + size_t tot_length; + if (length <= 125) + { + frame = (uchar*)msg - 2; + frame[0] = (WS_FIN | type); + frame[1] = length & 0x7F; + tot_length = length + 2; + } + else if (0xFFFF < length) + { + frame = (uchar*)msg - 10; + frame[0] = (WS_FIN | type); + frame[1] = 127; + frame[2] = (unsigned char) ((length >> 56) & 0xFF); + frame[3] = (unsigned char) ((length >> 48) & 0xFF); + frame[4] = (unsigned char) ((length >> 40) & 0xFF); + frame[5] = (unsigned char) ((length >> 32) & 0xFF); + frame[6] = (unsigned char) ((length >> 24) & 0xFF); + frame[7] = (unsigned char) ((length >> 16) & 0xFF); + frame[8] = (unsigned char) ((length >> 8) & 0xFF); + frame[9] = (unsigned char) (length & 0xFF); + tot_length = length + 10; + } + else + { + frame = (uchar*)msg - 4; + frame[0] = (WS_FIN | type); + frame[1] = 126; + frame[2] = (length >> 8) & 0xFF; + frame[3] = length & 0xFF; + tot_length = length + 4; + } + return (ssize_t) send_all (sock, frame, tot_length); +} + +static unsigned char * +ws_receive_frame (unsigned char *frame, ssize_t *length, int *type) +{ + unsigned char masks[4]; + unsigned char mask; + unsigned char *msg; + unsigned char flength; + unsigned char idx_first_mask; + unsigned char idx_first_data; + size_t data_length; + int i; + int j; + + msg = NULL; + *type = frame[0] & 0x0F; + if (frame[0] == (WS_FIN | WS_OPCODE_TEXT_FRAME) || + frame[0] == (WS_FIN | WS_OPCODE_PING_FRAME)) + { + idx_first_mask = 2; + mask = frame[1]; + flength = mask & 0x7F; + if (flength == 126) + { + idx_first_mask = 4; + } + else if (flength == 127) + { + idx_first_mask = 10; + } + idx_first_data = (unsigned char) (idx_first_mask + 4); + data_length = (size_t) *length - idx_first_data; + masks[0] = frame[idx_first_mask + 0]; + masks[1] = frame[idx_first_mask + 1]; + masks[2] = frame[idx_first_mask + 2]; + masks[3] = frame[idx_first_mask + 3]; + msg = malloc (data_length + 1); + if (NULL != msg) + { + for (i = idx_first_data, j = 0; i < *length; i++, j++) + { + msg[j] = frame[i] ^ masks[j % 4]; + } + *length = (ssize_t) data_length; + msg[j] = '\0'; + } + } + return msg; +} + +static void +epoll_selected( struct epoll_event * event ) { + fd_websocket_ctx_t * ws = event->data.ptr; + struct MHD_UpgradeResponseHandle *urh = ws->urh; + unsigned char buf[2048]; + + do { + ssize_t got = recv (ws->sock, (void *) buf, sizeof (buf), 0); + if (0 >= got) { + break; + } + int type = -1; + char * msg = (char *)ws_receive_frame (buf, &got, &type); + if (type == WS_OPCODE_TEXT_FRAME) { + if (NULL == msg) { + break; + } + if( !fd_webserver_ws_request( ws, msg, (ulong)got ) ) { + free( msg ); + break; + } + free( msg ); + /* Happy path */ + return; + } else if (type == WS_OPCODE_CLOSE_FRAME) { + break; + } else if (type == WS_OPCODE_PING_FRAME) { + ws_send_frame(ws->sock, WS_OPCODE_PONG_FRAME, msg, (ulong)got); + free( msg ); + return; + } + /* Unknown type */ + } while (0); + + fd_webserver_ws_closed(ws, ws->ws->cb_arg); + epoll_ctl(ws->ws->ws_epoll_fd, EPOLL_CTL_DEL, ws->sock, NULL); + MHD_upgrade_action (urh, MHD_UPGRADE_ACTION_CLOSE); + free (ws); +} + +static void +uh_cb (void *cls, struct MHD_Connection *con, void *req_cls, + const char *extra_in, size_t extra_in_size, MHD_socket sock, + struct MHD_UpgradeResponseHandle *urh) +{ + fd_webserver_t * ws = (fd_webserver_t *)cls; + + (void) con; /* Unused. Silent compiler warning. */ + (void) req_cls; /* Unused. Silent compiler warning. */ + (void) extra_in; /* Unused. Silent compiler warning. */ + (void) extra_in_size; /* Unused. Silent compiler warning. */ + + fd_websocket_ctx_t * wsd = malloc (sizeof (fd_websocket_ctx_t)); + memset (wsd, 0, sizeof (fd_websocket_ctx_t)); + wsd->sock = sock; + wsd->urh = urh; + wsd->ws = ws; + + make_blocking (sock); + + struct epoll_event event; + event.events = EPOLLIN; + event.data.ptr = wsd; + if (epoll_ctl(ws->ws_epoll_fd, EPOLL_CTL_ADD, sock, &event)) { + FD_LOG_ERR(("epoll_ctl failed: %s", strerror(errno) )); + } +} + +static enum MHD_Result +ahc_cb (void *cls, struct MHD_Connection *con, const char *url, + const char *method, const char *version, const char *upload_data, + size_t *upload_data_size, void **req_cls) +{ + struct MHD_Response *res; + const char *upg_header; + const char *con_header; + const char *ws_version_header; + const char *ws_key_header; + enum MHD_Result ret; + size_t key_size; + + fd_webserver_t * ws = (fd_webserver_t *)cls; + + (void) url; /* Unused. Silent compiler warning. */ + (void) upload_data; /* Unused. Silent compiler warning. */ + (void) upload_data_size; /* Unused. Silent compiler warning. */ + + if (NULL == *req_cls) + { + *req_cls = (void *) 1; + return MHD_YES; + } + *req_cls = NULL; + upg_header = MHD_lookup_connection_value (con, MHD_HEADER_KIND, + MHD_HTTP_HEADER_UPGRADE); + con_header = MHD_lookup_connection_value (con, MHD_HEADER_KIND, + MHD_HTTP_HEADER_CONNECTION); + if (MHD_NO == is_websocket_request (con, upg_header, con_header)) + { + return send_bad_request (con); + } + if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) + || (0 != strcmp (version, MHD_HTTP_VERSION_1_1))) + { + return send_bad_request (con); + } + ws_version_header = + MHD_lookup_connection_value (con, MHD_HEADER_KIND, + MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION); + if ((NULL == ws_version_header) + || (0 != strcmp (ws_version_header, WS_SEC_WEBSOCKET_VERSION))) + { + return send_upgrade_required (con); + } + ret = MHD_lookup_connection_value_n (con, MHD_HEADER_KIND, + MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY, + strlen (MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY), + &ws_key_header, &key_size); + if ((MHD_NO == ret) || (key_size != WS_KEY_LEN)) + { + return send_bad_request (con); + } + char ws_ac_value[2*SHA1HashSize+1]; + ret = ws_get_accept_value (ws_key_header, ws_ac_value); + if (MHD_NO == ret) + { + return ret; + } + res = MHD_create_response_for_upgrade (&uh_cb, ws); + if (MHD_YES != + MHD_add_response_header (res, MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT, + ws_ac_value)) + { + MHD_destroy_response (res); + return MHD_NO; + } + if (MHD_YES != + MHD_add_response_header (res, MHD_HTTP_HEADER_UPGRADE, WS_UPGRADE_VALUE)) + { + MHD_destroy_response (res); + return MHD_NO; + } + ret = MHD_queue_response (con, MHD_HTTP_SWITCHING_PROTOCOLS, res); + MHD_destroy_response (res); + return ret; +} diff --git a/src/app/rpcserver/fuzz_json_lex.c b/src/app/rpcserver/fuzz_json_lex.c new file mode 100644 index 0000000000..8cc17b1ebd --- /dev/null +++ b/src/app/rpcserver/fuzz_json_lex.c @@ -0,0 +1,52 @@ +#include +#include +#include + +#include "../../util/fd_util.h" +#include "json_lex.h" + +struct json_lex_state *lex_state = NULL; + +void free_lex_state(void) { free(lex_state); } + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + /* Set up shell without signal handlers */ + putenv("FD_LOG_BACKTRACE=0"); + fd_boot(argc, argv); + atexit(fd_halt); + + lex_state = malloc(sizeof(struct json_lex_state)); + atexit(free_lex_state); + + /* Disable parsing error logging */ + fd_log_level_stderr_set(4); + return 0; +} + +int __attribute__((optnone)) +LLVMFuzzerTestOneInput(uchar const *data, ulong size) { + json_lex_state_new(lex_state, (const char *)data, size); + + for (;;) { + long token_type = json_lex_next_token(lex_state); + + if (token_type == JSON_TOKEN_END || token_type == JSON_TOKEN_ERROR) { + json_lex_state_delete(lex_state); + return 0; + } + + ulong sz_out; + const char *out = json_lex_get_text(lex_state, &sz_out); + + if (sz_out) { + // Access the first and last byte of the state + const char a __attribute__((unused)) = out[0]; + + // A asan hit here would mean that json_lex_get_text claims that we can + // read further than we can. + const char b __attribute__((unused)) = out[sz_out - 1]; + } + } + + return 0; +} diff --git a/src/app/rpcserver/genkeywords.cxx b/src/app/rpcserver/genkeywords.cxx new file mode 100644 index 0000000000..e4050efefa --- /dev/null +++ b/src/app/rpcserver/genkeywords.cxx @@ -0,0 +1,415 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct keyword { + // Text being matched + const char* text; + // True if match is case insensitive + bool insensitive; + // Output token + const char* token; +}; + +struct matchnode { + const char* token = nullptr; + const char* text = nullptr; + std::map> children; +}; + +void genmatchnode(matchnode* node, const char* prefix, int textlen, const keyword* table) { + // Identify all possible next chars + const char* outtoken = NULL; + const char* outtext = NULL; + std::map> nexts; + int prefixlen = strlen(prefix); + assert(prefixlen <= textlen); + for (const keyword* i = table; i->text != NULL; ++i) { + if ((int)strlen(i->text) != textlen) + continue; + if (i->insensitive ? + (strncasecmp(prefix, i->text, prefixlen) == 0) : + (strncmp(prefix, i->text, prefixlen) == 0)) { + if (prefixlen == textlen) { + if (outtoken != NULL) { + fprintf(stderr, "output token %s is redundant with %s\n", outtoken, i->token); + exit(1); + } + outtoken = i->token; + outtext = i->text; + } else { + char c = i->text[prefixlen]; + nexts[c].push_back(i); + if (i->insensitive) { + if (c >= 'a' && c <='z') + nexts[c + ('A' - 'a')].push_back(i); + else if (c >= 'A' && c <='Z') + nexts[c + ('a' - 'A')].push_back(i); + } + } + } + } + if (outtoken != NULL) { + if (!nexts.empty()) { + fprintf(stderr, "output token %s is a prefix of another keyword\n", outtoken); + exit(1); + } + node->token = outtoken; + node->text = outtext; + } else { + std::map children; + for (auto& i : nexts) { + // Look for duplicate output states + bool found = false; + for (auto& j : nexts) { + if (i == j) + break; + if (i.second == j.second) { + found = true; + children[i.first] = children[j.first]; + break; + } + } + if (!found) { + // Recurse + matchnode* newnode = new matchnode; + children[i.first] = newnode; + char newprefix[prefixlen+2]; + memcpy(newprefix, prefix, prefixlen); + newprefix[prefixlen] = i.first; + newprefix[prefixlen+1] = '\0'; + genmatchnode(newnode, newprefix, textlen, table); + } + } + // Reverse the map to make later analysis easier + for (auto& i : children) + node->children[i.second].push_back(i.first); + } +} + +void genchaincode(std::vector& chain, unsigned prefixlen, FILE* fd) { + for (unsigned j = 0; j < chain.size(); ) { + if (j > 0) + fprintf(fd, " && "); + auto* n = chain[j]; + auto& chars = n->children.begin()->second; + + if (chars.size() > 1) { + fprintf(fd, "("); + bool first = true; + for (char c : chars) { + if (first) + first = false; + else + fprintf(fd, " | "); + fprintf(fd, "(keyw[%u] == '%c')", prefixlen+j, c); + } + fprintf(fd, ")"); + ++j; + + } else { + // Optimize case where we are matching a sequence of single characters + unsigned k; + for (k = 1; k < 8 && j+k < chain.size(); ++k) { + if (chain[j+k]->children.begin()->second.size() != 1) + break; + } + if (k == 1) { + fprintf(fd, "keyw[%u] == '%c'", prefixlen+j, *chars.begin()); + ++j; + + } else { + // Match up to 8 characters at once + unsigned long pattern = 0; + for (unsigned l = 0; l < k; ++l) + pattern |= ((unsigned long)(unsigned char)*chain[j+l]->children.begin()->second.begin())<<(l*8); + if (k == 8) + fprintf(fd, "(*(unsigned long*)&keyw[%u] == 0x%lXUL)", prefixlen+j, pattern); + else + fprintf(fd, "((*(unsigned long*)&keyw[%u] & 0x%lXUL) == 0x%lXUL)", prefixlen+j, (1UL<<(k*8))-1, pattern); + j += k; + } + } + } +} + +void gencode(matchnode* node, unsigned indent, unsigned prefixlen, FILE* fd) { + auto doindent = [&indent, fd](){ + for (unsigned i = 0; i < indent; ++i) + fputc(' ', fd); + }; + + // Create a chain of nodes with a single possible transition. These + // are optimized with a single if statement. + std::vector chain; + matchnode* end = node; + while (end->children.size() == 1) { + chain.push_back(end); + end = end->children.begin()->first; + } + if (!chain.empty()) { + doindent(); + fprintf(fd, "if ("); + genchaincode(chain, prefixlen, fd); + fprintf(fd, ") {\n"); + indent += 2; + } + + doindent(); + if (end->token != nullptr) + fprintf(fd, "return %s; // \"%s\"\n", end->token, end->text); + + else { + fprintf(fd, "switch (keyw[%lu]) {\n", prefixlen + chain.size()); + for (auto& i : end->children) { + for (char c : i.second) { + doindent(); + fprintf(fd, "case '%c':\n", c); + } + gencode(i.first, indent+2, (unsigned)(prefixlen + chain.size() + 1), fd); + doindent(); + fprintf(fd, " break;\n"); + } + doindent(); + fprintf(fd, "}\n"); + } + + if (!chain.empty()) { + indent -= 2; + doindent(); + fprintf(fd, "}\n"); + } +} + +void genmacros(const keyword* table, const char* funname, const char* errtoken, FILE* fd) { + unsigned j = 0; + std::set done; + for (const keyword* i = table; i->text != NULL; ++i) { + if (done.count(i->token) == 0) { + fprintf(fd, "#define %s %uL\n", i->token, j++); + done.insert(i->token); + } + } + fprintf(fd, "#ifndef %s\n", errtoken); + fprintf(fd, "#define %s -1L\n", errtoken); + fprintf(fd, "#endif\n"); + + fprintf(fd, "long %s(const char* keyw, unsigned long keyw_sz);\n", funname); + fprintf(fd, "const char* un_%s(long id);\n", funname); +} + +void genmatcher(const keyword* table, const char* funname, const char* errtoken, FILE* fd) { + std::map rootsbylen; + for (const keyword* i = table; i->text != NULL; ++i) { + int len = strlen(i->text); + if (rootsbylen.count(len) == 0) { + matchnode* root = new matchnode; + genmatchnode(root, "", len, table); + rootsbylen[len] = root; + } + } + + fprintf(fd, "long %s(const char* keyw, unsigned long keyw_sz) {\n", funname); + fprintf(fd, " switch (keyw_sz) {\n"); + for (auto& i : rootsbylen) { + fprintf(fd, " case %d:\n", i.first); + gencode(i.second, 4, 0, fd); + fprintf(fd, " break;\n"); + } + fprintf(fd, " }\n"); + fprintf(fd, " return %s;\n", errtoken); + fprintf(fd, "}\n"); + + fprintf(fd, "const char* un_%s(long id) {\n", funname); + fprintf(fd, " switch (id) {\n"); + for (const keyword* i = table; i->text != NULL; ++i) { + fprintf(fd, " case %s: return \"%s\";\n", i->token, i->text); + } + fprintf(fd, " }\n"); + fprintf(fd, " return \"???\";\n"); + fprintf(fd, "}\n"); +} + +void gentest(const keyword* table, const char* funname, const char* errtoken, FILE* fd) { + fprintf(fd, "void test_%s() {\n", funname); + for (const keyword* i = table; i->text != NULL; ++i) { + char scratch[1024]; + strncpy(scratch, i->text, sizeof(scratch)); + fprintf(fd, " assert(%s(\"%s\\0\\0\\0\\0\\0\\0\\0\", %lu) == %s);\n", + funname, scratch, strlen(scratch), i->token); + if (i->insensitive) { + for (char* p = scratch; *p != '\0'; ++p) { + if (*p >= 'a' && *p <= 'z') + *p += 'A' - 'a'; + } + fprintf(fd, " assert(%s(\"%s\\0\\0\\0\\0\\0\\0\\0\", %lu) == %s);\n", + funname, scratch, strlen(scratch), i->token); + for (char* p = scratch; *p != '\0'; ++p) { + if (*p >= 'A' && *p <= 'Z') + *p += 'a' - 'A'; + } + fprintf(fd, " assert(%s(\"%s\\0\\0\\0\\0\\0\\0\\0\", %lu) == %s);\n", + funname, scratch, strlen(scratch), i->token); + } + auto textlen = strlen(i->text); + strncpy(scratch, i->text, sizeof(scratch)); + scratch[textlen] = 'x'; + fprintf(fd, " assert(%s(\"%s\\0\\0\\0\\0\\0\\0\\0\", %lu) == %s);\n", + funname, scratch, strlen(scratch), errtoken); + strncpy(scratch, i->text, sizeof(scratch)); + scratch[textlen-1] = '\0'; + bool found = false; + for (const keyword* j = table; j->text != NULL; ++j) { + if (i != j && strcmp(scratch, j->text) == 0) { + found = true; + break; + } + } + if (!found) + fprintf(fd, " assert(%s(\"%s\\0\\0\\0\\0\\0\\0\\0\", %lu) == %s);\n", + funname, scratch, strlen(scratch), errtoken); + for (unsigned j = 0; j < textlen; ++j) { + strncpy(scratch, i->text, sizeof(scratch)); + scratch[j] = '|'; + fprintf(fd, " assert(%s(\"%s\\0\\0\\0\\0\\0\\0\\0\", %lu) == %s);\n", + funname, scratch, strlen(scratch), errtoken); + } + } + fprintf(fd, "}\n"); +} + +int main(int argc, char** argv) { + static const keyword json_table[] = { + { "jsonrpc", false, "KEYW_JSON_JSONRPC" }, + { "id", false, "KEYW_JSON_ID" }, + { "method", false, "KEYW_JSON_METHOD" }, + { "params", false, "KEYW_JSON_PARAMS" }, + + { "bytes", false, "KEYW_JSON_BYTES" }, + { "commitment", false, "KEYW_JSON_COMMITMENT" }, + { "dataSize", false, "KEYW_JSON_DATASIZE" }, + { "dataSlice", false, "KEYW_JSON_DATASLICE" }, + { "encoding", false, "KEYW_JSON_ENCODING" }, + { "epoch", false, "KEYW_JSON_EPOCH" }, + { "filters", false, "KEYW_JSON_FILTERS" }, + { "identity", false, "KEYW_JSON_IDENTITY" }, + { "length", false, "KEYW_JSON_LENGTH" }, + { "limit", false, "KEYW_JSON_LIMIT" }, + { "maxSupportedTransactionVersion", false, "KEYW_JSON_MAXSUPPORTEDTRANSACTIONVERSION" }, + { "memcmp", false, "KEYW_JSON_MEMCMP" }, + { "mint", false, "KEYW_JSON_MINT" }, + { "offset", false, "KEYW_JSON_OFFSET" }, + { "programId", false, "KEYW_JSON_PROGRAMID" }, + { "rewards", false, "KEYW_JSON_REWARDS" }, + { "searchTransactionHistory", false, "KEYW_JSON_SEARCHTRANSACTIONHISTORY" }, + { "transactionDetails", false, "KEYW_JSON_TRANSACTIONDETAILS" }, + { "votePubkey", false, "KEYW_JSON_VOTEPUBKEY" }, + + { "getAccountInfo", false, "KEYW_RPCMETHOD_GETACCOUNTINFO" }, + { "getBalance", false, "KEYW_RPCMETHOD_GETBALANCE" }, + { "getBlock", false, "KEYW_RPCMETHOD_GETBLOCK" }, + { "getBlockCommitment", false, "KEYW_RPCMETHOD_GETBLOCKCOMMITMENT" }, + { "getBlockHeight", false, "KEYW_RPCMETHOD_GETBLOCKHEIGHT" }, + { "getBlockProduction", false, "KEYW_RPCMETHOD_GETBLOCKPRODUCTION" }, + { "getBlocks", false, "KEYW_RPCMETHOD_GETBLOCKS" }, + { "getBlocksWithLimit", false, "KEYW_RPCMETHOD_GETBLOCKSWITHLIMIT" }, + { "getBlockTime", false, "KEYW_RPCMETHOD_GETBLOCKTIME" }, + { "getClusterNodes", false, "KEYW_RPCMETHOD_GETCLUSTERNODES" }, + { "getConfirmedBlock", false, "KEYW_RPCMETHOD_GETCONFIRMEDBLOCK" }, + { "getConfirmedBlocks", false, "KEYW_RPCMETHOD_GETCONFIRMEDBLOCKS" }, + { "getConfirmedBlocksWithLimit", false, "KEYW_RPCMETHOD_GETCONFIRMEDBLOCKSWITHLIMIT" }, + { "getConfirmedSignaturesForAddress2", false, "KEYW_RPCMETHOD_GETCONFIRMEDSIGNATURESFORADDRESS2" }, + { "getConfirmedTransaction", false, "KEYW_RPCMETHOD_GETCONFIRMEDTRANSACTION" }, + { "getEpochInfo", false, "KEYW_RPCMETHOD_GETEPOCHINFO" }, + { "getEpochSchedule", false, "KEYW_RPCMETHOD_GETEPOCHSCHEDULE" }, + { "getFeeCalculatorForBlockhash", false, "KEYW_RPCMETHOD_GETFEECALCULATORFORBLOCKHASH" }, + { "getFeeForMessage", false, "KEYW_RPCMETHOD_GETFEEFORMESSAGE" }, + { "getFeeRateGovernor", false, "KEYW_RPCMETHOD_GETFEERATEGOVERNOR" }, + { "getFees", false, "KEYW_RPCMETHOD_GETFEES" }, + { "getFirstAvailableBlock", false, "KEYW_RPCMETHOD_GETFIRSTAVAILABLEBLOCK" }, + { "getGenesisHash", false, "KEYW_RPCMETHOD_GETGENESISHASH" }, + { "getHealth", false, "KEYW_RPCMETHOD_GETHEALTH" }, + { "getHighestSnapshotSlot", false, "KEYW_RPCMETHOD_GETHIGHESTSNAPSHOTSLOT" }, + { "getIdentity", false, "KEYW_RPCMETHOD_GETIDENTITY" }, + { "getInflationGovernor", false, "KEYW_RPCMETHOD_GETINFLATIONGOVERNOR" }, + { "getInflationRate", false, "KEYW_RPCMETHOD_GETINFLATIONRATE" }, + { "getInflationReward", false, "KEYW_RPCMETHOD_GETINFLATIONREWARD" }, + { "getLargestAccounts", false, "KEYW_RPCMETHOD_GETLARGESTACCOUNTS" }, + { "getLatestBlockhash", false, "KEYW_RPCMETHOD_GETLATESTBLOCKHASH" }, + { "getLeaderSchedule", false, "KEYW_RPCMETHOD_GETLEADERSCHEDULE" }, + { "getMaxRetransmitSlot", false, "KEYW_RPCMETHOD_GETMAXRETRANSMITSLOT" }, + { "getMaxShredInsertSlot", false, "KEYW_RPCMETHOD_GETMAXSHREDINSERTSLOT" }, + { "getMinimumBalanceForRentExemption", false, "KEYW_RPCMETHOD_GETMINIMUMBALANCEFORRENTEXEMPTION" }, + { "getMultipleAccounts", false, "KEYW_RPCMETHOD_GETMULTIPLEACCOUNTS" }, + { "getProgramAccounts", false, "KEYW_RPCMETHOD_GETPROGRAMACCOUNTS" }, + { "getRecentBlockhash", false, "KEYW_RPCMETHOD_GETRECENTBLOCKHASH" }, + { "getRecentPerformanceSamples", false, "KEYW_RPCMETHOD_GETRECENTPERFORMANCESAMPLES" }, + { "getRecentPrioritizationFees", false, "KEYW_RPCMETHOD_GETRECENTPRIORITIZATIONFEES" }, + { "getSignaturesForAddress", false, "KEYW_RPCMETHOD_GETSIGNATURESFORADDRESS" }, + { "getSignatureStatuses", false, "KEYW_RPCMETHOD_GETSIGNATURESTATUSES" }, + { "getSlot", false, "KEYW_RPCMETHOD_GETSLOT" }, + { "getSlotLeader", false, "KEYW_RPCMETHOD_GETSLOTLEADER" }, + { "getSlotLeaders", false, "KEYW_RPCMETHOD_GETSLOTLEADERS" }, + { "getSnapshotSlot", false, "KEYW_RPCMETHOD_GETSNAPSHOTSLOT" }, + { "getStakeActivation", false, "KEYW_RPCMETHOD_GETSTAKEACTIVATION" }, + { "getStakeMinimumDelegation", false, "KEYW_RPCMETHOD_GETSTAKEMINIMUMDELEGATION" }, + { "getSupply", false, "KEYW_RPCMETHOD_GETSUPPLY" }, + { "getTokenAccountBalance", false, "KEYW_RPCMETHOD_GETTOKENACCOUNTBALANCE" }, + { "getTokenAccountsByDelegate", false, "KEYW_RPCMETHOD_GETTOKENACCOUNTSBYDELEGATE" }, + { "getTokenAccountsByOwner", false, "KEYW_RPCMETHOD_GETTOKENACCOUNTSBYOWNER" }, + { "getTokenLargestAccounts", false, "KEYW_RPCMETHOD_GETTOKENLARGESTACCOUNTS" }, + { "getTokenSupply", false, "KEYW_RPCMETHOD_GETTOKENSUPPLY" }, + { "getTransaction", false, "KEYW_RPCMETHOD_GETTRANSACTION" }, + { "getTransactionCount", false, "KEYW_RPCMETHOD_GETTRANSACTIONCOUNT" }, + { "getVersion", false, "KEYW_RPCMETHOD_GETVERSION" }, + { "getVoteAccounts", false, "KEYW_RPCMETHOD_GETVOTEACCOUNTS" }, + { "isBlockhashValid", false, "KEYW_RPCMETHOD_ISBLOCKHASHVALID" }, + { "minimumLedgerSlot", false, "KEYW_RPCMETHOD_MINIMUMLEDGERSLOT" }, + { "requestAirdrop", false, "KEYW_RPCMETHOD_REQUESTAIRDROP" }, + { "sendTransaction", false, "KEYW_RPCMETHOD_SENDTRANSACTION" }, + { "simulateTransaction", false, "KEYW_RPCMETHOD_SIMULATETRANSACTION" }, + + { "accountSubscribe", false, "KEYW_WS_METHOD_ACCOUNTSUBSCRIBE" }, + { "accountUnsubscribe", false, "KEYW_WS_METHOD_ACCOUNTUNSUBSCRIBE" }, + { "blockSubscribe", false, "KEYW_WS_METHOD_BLOCKSUBSCRIBE" }, + { "blockUnsubscribe", false, "KEYW_WS_METHOD_BLOCKUNSUBSCRIBE" }, + { "logsSubscribe", false, "KEYW_WS_METHOD_LOGSSUBSCRIBE" }, + { "logsUnsubscribe", false, "KEYW_WS_METHOD_LOGSUNSUBSCRIBE" }, + { "programSubscribe", false, "KEYW_WS_METHOD_PROGRAMSUBSCRIBE" }, + { "programUnsubscribe", false, "KEYW_WS_METHOD_PROGRAMUNSUBSCRIBE" }, + { "rootSubscribe", false, "KEYW_WS_METHOD_ROOTSUBSCRIBE" }, + { "rootUnsubscribe", false, "KEYW_WS_METHOD_ROOTUNSUBSCRIBE" }, + { "signatureSubscribe", false, "KEYW_WS_METHOD_SIGNATURESUBSCRIBE" }, + { "signatureUnsubscribe", false, "KEYW_WS_METHOD_SIGNATUREUNSUBSCRIBE" }, + { "slotSubscribe", false, "KEYW_WS_METHOD_SLOTSUBSCRIBE" }, + { "slotUnsubscribe", false, "KEYW_WS_METHOD_SLOTUNSUBSCRIBE" }, + { "slotsUpdatesSubscribe", false, "KEYW_WS_METHOD_SLOTSUPDATESSUBSCRIBE" }, + { "slotsUpdatesUnsubscribe", false, "KEYW_WS_METHOD_SLOTSUPDATESUNSUBSCRIBE" }, + { "voteSubscribe", false, "KEYW_WS_METHOD_VOTESUBSCRIBE" }, + { "voteUnsubscribe", false, "KEYW_WS_METHOD_VOTEUNSUBSCRIBE" }, + + { NULL, false, NULL } + }; + FILE* fd = fopen("keywords.h", "w"); + fprintf(fd, "// This file is generated by genkeywords.cxx. DO NOT EDIT DIRECTLY!\n"); + genmacros(json_table, "fd_webserver_json_keyword", "KEYW_UNKNOWN", fd); + fclose(fd); + + fd = fopen("keywords.c", "w"); + fprintf(fd, "// This file is generated by genkeywords.cxx. DO NOT EDIT DIRECTLY!\n"); + fprintf(fd, "#include \"keywords.h\"\n"); + genmatcher(json_table, "fd_webserver_json_keyword", "KEYW_UNKNOWN", fd); + fclose(fd); + + fd = fopen("test_keywords.h", "w"); + gentest(json_table, "fd_webserver_json_keyword", "KEYW_UNKNOWN", fd); + fclose(fd); + + return 0; +} diff --git a/src/app/rpcserver/json_lex.c b/src/app/rpcserver/json_lex.c new file mode 100644 index 0000000000..40f9569f0c --- /dev/null +++ b/src/app/rpcserver/json_lex.c @@ -0,0 +1,451 @@ +#include "json_lex.h" +#include +#include +#include + +static char* json_lex_append_prepare(json_lex_state_t* lex, ulong sz); +static void json_lex_append_char(json_lex_state_t* lex, uint ch); + +void json_lex_state_new(struct json_lex_state* state, + const char* json, + ulong json_sz) { + state->json = json; + state->json_sz = json_sz; + state->pos = 0; + state->last_tok = JSON_TOKEN_ERROR; + state->last_bool = 0; + state->last_str = state->last_str_firstbuf; + state->last_str_sz = 0; + state->last_str_alloc = sizeof(state->last_str_firstbuf); + state->last_str_firstbuf[0] = '\0'; +} + +void json_lex_state_delete(struct json_lex_state* state) { + if (state->last_str != state->last_str_firstbuf) + free(state->last_str); +} + +// Parse a numeric constant +static long json_lex_parse_number(struct json_lex_state* state, const char* start_pos) { + // Scan to the end of the number + const char* pos = start_pos; + const char* const end_pos = state->json + state->json_sz; + if (pos < end_pos && *pos == '-') + pos++; + while (pos < end_pos && (uchar)(*pos - '0') <= (uchar)9) + pos++; + int isfloat = 0; + if (pos < end_pos && *pos == '.') { + isfloat = 1; + pos++; + while (pos < end_pos && (uchar)(*pos - '0') <= (uchar)9) + pos++; + } + if (pos < end_pos && (*pos == 'e' || *pos == 'E')) { + isfloat = 1; + pos++; + if (pos < end_pos && (*pos == '+' || *pos == '-')) + pos++; + while (pos < end_pos && (uchar)(*pos - '0') <= (uchar)9) + pos++; + } + + // Numbers must end on whitespace or punctuation + if (pos < end_pos) { + switch (*pos) { + case ' ': case '\t': case '\r': case '\n': + case '[': case ']': case '{': case '}': + case ',': case ':': + break; + default: + state->pos = (ulong)(start_pos - state->json); + json_lex_sprintf(state, "malformed number at position %lu in json", state->pos); + return JSON_TOKEN_ERROR; + } + } + + // Store the number in string form + ulong text_sz = (ulong)(pos - start_pos); + if( text_sz >= state->last_str_alloc ) { + state->pos = (ulong)(start_pos - state->json); + json_lex_sprintf(state, "malformed number at position %lu in json", state->pos); + return JSON_TOKEN_ERROR; + } + fd_memcpy(state->last_str, start_pos, text_sz); + state->last_str[text_sz] = '\0'; + state->last_str_sz = text_sz; + state->pos = (ulong)(pos - state->json); + return (isfloat ? JSON_TOKEN_FLOAT : JSON_TOKEN_INTEGER); +} + +// Validate a segment of UTF-8 encoded test. If an error is found, a +// pointer to it is returned. A NULL is returned if there is no error. +static const char* json_lex_validate_encoding(const char* t, const char* t_end) { + /**** +Code Points First Byte Second Byte Third Byte Fourth Byte +U+0020..U+007F 20..7F +U+0080..U+07FF C2..DF 80..BF +U+0800..U+0FFF E0 A0..BF 80..BF +U+1000..U+CFFF E1..EC 80..BF 80..BF +U+D000..U+D7FF ED 80..9F 80..BF +U+E000..U+FFFF EE..EF 80..BF 80..BF +U+10000..U+3FFFF F0 90..BF 80..BF 80..BF +U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF +U+100000..U+10FFFF F4 80..8F 80..BF 80..BF +Also, '"' and '\' are not allowed. + ****/ + // Fast case lookup table based on the leading byte + static const uchar case_table[0x100] = { + 0 /* 00 */, 0 /* 01 */, 0 /* 02 */, 0 /* 03 */, 0 /* 04 */, 0 /* 05 */, 0 /* 06 */, 0 /* 07 */, + 0 /* 08 */, 1 /* 09 */, 1 /* 0a */, 0 /* 0b */, 1 /* 0c */, 0 /* 0d */, 0 /* 0e */, 0 /* 0f */, + 0 /* 10 */, 0 /* 11 */, 0 /* 12 */, 0 /* 13 */, 0 /* 14 */, 0 /* 15 */, 0 /* 16 */, 0 /* 17 */, + 0 /* 18 */, 0 /* 19 */, 0 /* 1a */, 0 /* 1b */, 0 /* 1c */, 0 /* 1d */, 0 /* 1e */, 0 /* 1f */, + 1 /* 20 */, 1 /* 21 */, 0 /* 22 */, 1 /* 23 */, 1 /* 24 */, 1 /* 25 */, 1 /* 26 */, 1 /* 27 */, + 1 /* 28 */, 1 /* 29 */, 1 /* 2a */, 1 /* 2b */, 1 /* 2c */, 1 /* 2d */, 1 /* 2e */, 1 /* 2f */, + 1 /* 30 */, 1 /* 31 */, 1 /* 32 */, 1 /* 33 */, 1 /* 34 */, 1 /* 35 */, 1 /* 36 */, 1 /* 37 */, + 1 /* 38 */, 1 /* 39 */, 1 /* 3a */, 1 /* 3b */, 1 /* 3c */, 1 /* 3d */, 1 /* 3e */, 1 /* 3f */, + 1 /* 40 */, 1 /* 41 */, 1 /* 42 */, 1 /* 43 */, 1 /* 44 */, 1 /* 45 */, 1 /* 46 */, 1 /* 47 */, + 1 /* 48 */, 1 /* 49 */, 1 /* 4a */, 1 /* 4b */, 1 /* 4c */, 1 /* 4d */, 1 /* 4e */, 1 /* 4f */, + 1 /* 50 */, 1 /* 51 */, 1 /* 52 */, 1 /* 53 */, 1 /* 54 */, 1 /* 55 */, 1 /* 56 */, 1 /* 57 */, + 1 /* 58 */, 1 /* 59 */, 1 /* 5a */, 1 /* 5b */, 0 /* 5c */, 1 /* 5d */, 1 /* 5e */, 1 /* 5f */, + 1 /* 60 */, 1 /* 61 */, 1 /* 62 */, 1 /* 63 */, 1 /* 64 */, 1 /* 65 */, 1 /* 66 */, 1 /* 67 */, + 1 /* 68 */, 1 /* 69 */, 1 /* 6a */, 1 /* 6b */, 1 /* 6c */, 1 /* 6d */, 1 /* 6e */, 1 /* 6f */, + 1 /* 70 */, 1 /* 71 */, 1 /* 72 */, 1 /* 73 */, 1 /* 74 */, 1 /* 75 */, 1 /* 76 */, 1 /* 77 */, + 1 /* 78 */, 1 /* 79 */, 1 /* 7a */, 1 /* 7b */, 1 /* 7c */, 1 /* 7d */, 1 /* 7e */, 1 /* 7f */, + 0 /* 80 */, 0 /* 81 */, 0 /* 82 */, 0 /* 83 */, 0 /* 84 */, 0 /* 85 */, 0 /* 86 */, 0 /* 87 */, + 0 /* 88 */, 0 /* 89 */, 0 /* 8a */, 0 /* 8b */, 0 /* 8c */, 0 /* 8d */, 0 /* 8e */, 0 /* 8f */, + 0 /* 90 */, 0 /* 91 */, 0 /* 92 */, 0 /* 93 */, 0 /* 94 */, 0 /* 95 */, 0 /* 96 */, 0 /* 97 */, + 0 /* 98 */, 0 /* 99 */, 0 /* 9a */, 0 /* 9b */, 0 /* 9c */, 0 /* 9d */, 0 /* 9e */, 0 /* 9f */, + 0 /* a0 */, 0 /* a1 */, 0 /* a2 */, 0 /* a3 */, 0 /* a4 */, 0 /* a5 */, 0 /* a6 */, 0 /* a7 */, + 0 /* a8 */, 0 /* a9 */, 0 /* aa */, 0 /* ab */, 0 /* ac */, 0 /* ad */, 0 /* ae */, 0 /* af */, + 0 /* b0 */, 0 /* b1 */, 0 /* b2 */, 0 /* b3 */, 0 /* b4 */, 0 /* b5 */, 0 /* b6 */, 0 /* b7 */, + 0 /* b8 */, 0 /* b9 */, 0 /* ba */, 0 /* bb */, 0 /* bc */, 0 /* bd */, 0 /* be */, 0 /* bf */, + 0 /* c0 */, 0 /* c1 */, 2 /* c2 */, 2 /* c3 */, 2 /* c4 */, 2 /* c5 */, 2 /* c6 */, 2 /* c7 */, + 2 /* c8 */, 2 /* c9 */, 2 /* ca */, 2 /* cb */, 2 /* cc */, 2 /* cd */, 2 /* ce */, 2 /* cf */, + 2 /* d0 */, 2 /* d1 */, 2 /* d2 */, 2 /* d3 */, 2 /* d4 */, 2 /* d5 */, 2 /* d6 */, 2 /* d7 */, + 2 /* d8 */, 2 /* d9 */, 2 /* da */, 2 /* db */, 2 /* dc */, 2 /* dd */, 2 /* de */, 2 /* df */, + 3 /* e0 */, 4 /* e1 */, 4 /* e2 */, 4 /* e3 */, 4 /* e4 */, 4 /* e5 */, 4 /* e6 */, 4 /* e7 */, + 4 /* e8 */, 4 /* e9 */, 4 /* ea */, 4 /* eb */, 4 /* ec */, 5 /* ed */, 6 /* ee */, 6 /* ef */, + 7 /* f0 */, 8 /* f1 */, 8 /* f2 */, 8 /* f3 */, 9 /* f4 */, 0 /* f5 */, 0 /* f6 */, 0 /* f7 */, + 0 /* f8 */, 0 /* f9 */, 0 /* fa */, 0 /* fb */, 0 /* fc */, 0 /* fd */, 0 /* fe */, 0 /* ff */ + }; + while (t < t_end) { + switch (case_table[(uchar)t[0]]) { + case 0: // error + return t; + case 1: // 20..7F + ++t; + break; + case 2: // C2..DF + // Determine if a character is in a range +#define MATCH(_ch_, _low_, _high_) ((uchar)_ch_ - (uchar)_low_ <= (uchar)(_high_ - _low_)) + if (!(t+2 <= t_end && MATCH(t[1], 0x80, 0xBF))) + return t; + t += 2; + break; + case 3: // E0 + if (!(t+3 <= t_end && MATCH(t[1], 0xA0, 0xBF) && MATCH(t[2], 0x80, 0xBF))) + return t; + t += 3; + break; + case 4: // E1..EC + if (!(t+3 <= t_end && MATCH(t[1], 0x80, 0xBF) && MATCH(t[2], 0x80, 0xBF))) + return t; + t += 3; + break; + case 5: // ED + if (!(t+3 <= t_end && MATCH(t[1], 0x80, 0x9F) && MATCH(t[2], 0x80, 0xBF))) + return t; + t += 3; + break; + case 6: // EE..EF + if (!(t+3 <= t_end && MATCH(t[1], 0x80, 0xBF) && MATCH(t[2], 0x80, 0xBF))) + return t; + t += 3; + break; + case 7: // F0 + if (!(t+4 <= t_end && MATCH(t[1], 0x90, 0xBF) && MATCH(t[2], 0x80, 0xBF) && MATCH(t[3], 0x80, 0xBF))) + return t; + t += 4; + break; + case 8: // F1..F3 + if (!(t+4 <= t_end && MATCH(t[1], 0x80, 0xBF) && MATCH(t[2], 0x80, 0xBF) && MATCH(t[3], 0x80, 0xBF))) + return t; + t += 4; + break; + case 9: // F4 + if (!(t+4 <= t_end && MATCH(t[1], 0x80, 0x8F) && MATCH(t[2], 0x80, 0xBF) && MATCH(t[3], 0x80, 0xBF))) + return t; + t += 4; + break; + } +#undef MATCH + } + return NULL; +} + +// Parse a json string. All characters are decoded to pure UTF-8 +static long json_lex_parse_string(struct json_lex_state* state, const char* start_pos) { + state->last_str_sz = 0; + state->last_str[0] = '\0'; + const char* pos = start_pos + 1; // Skip leading quote + const char* const end_pos = state->json + state->json_sz; + // Loop over all characters + while (pos < end_pos) { + if (*pos == '"') { + state->pos = (ulong)(pos + 1 - state->json); + return JSON_TOKEN_STRING; + } + + if (*pos != '\\') { + // A segment of simple text without escapes + const char* s = pos; + do { + pos++; + } while (pos < end_pos && *pos != '"' && *pos != '\\'); + // Make sure the text is correctly encoded + const char* err_pos = json_lex_validate_encoding(s, pos); + if (err_pos) { + // Report the error + state->pos = (ulong)(start_pos - state->json); + json_lex_sprintf(state, "invalid character literal at position %ld in json", err_pos - state->json); + return JSON_TOKEN_ERROR; + } + // Just copy out the text + fd_memcpy(json_lex_append_prepare(state, (ulong)(pos - s)), s, (ulong)(pos - s)); + continue; // break out of switch and continue outer loop + } + + // Process an escape + if (pos + 2 > end_pos) + break; + uint ch; + switch (pos[1]) { + // Simple escapes + case '"': ch = 0x22; pos += 2; break; + case '\\': ch = 0x5C; pos += 2; break; + case '/': ch = 0x2F; pos += 2; break; + case 'b': ch = 0x8; pos += 2; break; + case 'f': ch = 0xC; pos += 2; break; + case 'n': ch = 0xA; pos += 2; break; + case 'r': ch = 0xD; pos += 2; break; + case 't': ch = 0x9; pos += 2; break; + + case 'u': // Hexadecimal escape + if (pos + 6 <= end_pos) { + ch = 0; + unsigned i; + for (i = 2; i < 6; ++i) { + char j = pos[i]; + if ((uchar)(j - '0') <= (uchar)9) + ch = (ch<<4) + (uchar)(j - '0'); + else if ((uchar)(j - 'a') <= (uchar)5) + ch = (ch<<4) + (uchar)(j - ('a' - 10)); + else if ((uchar)(j - 'A') <= (uchar)5) + ch = (ch<<4) + (uchar)(j - ('A' - 10)); + else + break; + } + // See if the loop succeeded + if (i == 6) { + pos += 6; + break; // Fall out of switch to append_char + } + } + // Fall through to error case + __attribute__((fallthrough)); + default: + state->pos = (ulong)(start_pos - state->json); + json_lex_sprintf(state, "invalid character literal at position %ld in json", pos - state->json); + return JSON_TOKEN_ERROR; + } + // Append the escaped character + json_lex_append_char(state, ch); + + } + // We were looking for a closing quote + state->pos = (ulong)(start_pos - state->json); + json_lex_sprintf(state, "unterminated string starting at position %lu in json", state->pos); + return JSON_TOKEN_ERROR; +} + +// Report a lexical error +static long json_lex_error(struct json_lex_state* state, const char* pos) { + state->pos = (ulong)(pos - state->json); + json_lex_sprintf(state, "lexical error at position %lu in json", state->pos); + return JSON_TOKEN_ERROR; +} + +// Scan the next lexical token +long json_lex_next_token(struct json_lex_state* state) { + const char* pos = state->json + state->pos; + const char* end_pos = state->json + state->json_sz; + while (pos < end_pos) { + switch (*pos) { + // Whitespace + case ' ': case '\t': case '\r': case '\n': + ++pos; + continue; + + // Single character cases + case '[': + state->pos = (ulong)(pos + 1 - state->json); + return state->last_tok = JSON_TOKEN_LBRACKET; + case ']': + state->pos = (ulong)(pos + 1 - state->json); + return state->last_tok = JSON_TOKEN_RBRACKET; + case '{': + state->pos = (ulong)(pos + 1 - state->json); + return state->last_tok = JSON_TOKEN_LBRACE; + case '}': + state->pos = (ulong)(pos + 1 - state->json); + return state->last_tok = JSON_TOKEN_RBRACE; + case ',': + state->pos = (ulong)(pos + 1 - state->json); + return state->last_tok = JSON_TOKEN_COMMA; + case ':': + state->pos = (ulong)(pos + 1 - state->json); + return state->last_tok = JSON_TOKEN_COLON; + + case 'n': // null + if (pos + 4 <= end_pos && pos[1] == 'u' && pos[2] == 'l' && pos[3] == 'l') { + state->pos = (ulong)(pos + 4 - state->json); + return state->last_tok = JSON_TOKEN_NULL; + } + return state->last_tok = json_lex_error(state, pos); + + case 't': // true + if (pos + 4 <= end_pos && pos[1] == 'r' && pos[2] == 'u' && pos[3] == 'e') { + state->pos = (ulong)(pos + 4 - state->json); + state->last_bool = 1; + return state->last_tok = JSON_TOKEN_BOOL; + } + return state->last_tok = json_lex_error(state, pos); + + case 'f': // false + if (pos + 5 <= end_pos && pos[1] == 'a' && pos[2] == 'l' && pos[3] == 's' && pos[4] == 'e') { + state->pos = (ulong)(pos + 5 - state->json); + state->last_bool = 0; + return state->last_tok = JSON_TOKEN_BOOL; + } + return state->last_tok = json_lex_error(state, pos); + + // number + case '-': case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + return state->last_tok = json_lex_parse_number(state, pos); + + case '"': // string + return state->last_tok = json_lex_parse_string(state, pos); + + default: // Any other character + return state->last_tok = json_lex_error(state, pos); + } + } + state->pos = (ulong)(pos - state->json); + return state->last_tok = JSON_TOKEN_END; +} + +const char* json_lex_get_text(json_lex_state_t* state, ulong* sz) { + if (sz != NULL) + *sz = state->last_str_sz; + return state->last_str; +} + +// Convert the string to an integer (assuming decimal representation) +long json_lex_as_int(json_lex_state_t* lex) { + // Gangster conversion of decimal text to int + const char* i = lex->last_str; + const char* i_end = i + lex->last_str_sz; + int isneg = 0; + if (i < i_end && *i == '-') { + isneg = 1; + i++; + } + long n = 0; + while (i < i_end) + n = n*10 + (*(i++) - '0'); + return (isneg ? -n : n); +} + +// Convert the string to a float +double json_lex_as_float(json_lex_state_t* lex) { + return strtod(lex->last_str, NULL); +} + +// Reserve space at the end of the string for additional text. The +// pointer to the new space is returned (e.g. for memcpy). +static char* json_lex_append_prepare(json_lex_state_t* lex, ulong sz) { + // Get the new string size + ulong new_sz = lex->last_str_sz + sz; + // Make sure there is enough room, including a null terminator + if (new_sz + 1 > lex->last_str_alloc) { + // Grow the allocation + do { + lex->last_str_alloc <<= 1; + } while (new_sz + 1 > lex->last_str_alloc); + char* oldstr = lex->last_str; + lex->last_str = (char*)malloc(lex->last_str_alloc); + // Copy the old content to the new space + fd_memcpy(lex->last_str, oldstr, lex->last_str_sz); + if (oldstr != lex->last_str_firstbuf) + free(oldstr); + } + // Stick on a null terminator + char* res = lex->last_str + lex->last_str_sz; + res[sz] = '\0'; + lex->last_str_sz = new_sz; + return res; +} + +// Append a unicode character to the string. The character is +// converted to UTF-8 encoding. +static void json_lex_append_char(json_lex_state_t* lex, uint ch) { + // Encode in UTF-8 + if (ch < 0x80) { + char* dest = json_lex_append_prepare(lex, 1); + *dest = (char)ch; + } else if (ch < 0x800) { + char* dest = json_lex_append_prepare(lex, 2); + *(dest++) = (char)((ch>>6) | 0xC0); + *dest = (char)((ch & 0x3F) | 0x80); + } else if (ch < 0x10000) { + char* dest = json_lex_append_prepare(lex, 3); + *(dest++) = (char)((ch>>12) | 0xE0); + *(dest++) = (char)(((ch>>6) & 0x3F) | 0x80); + *dest = (char)((ch & 0x3F) | 0x80); + } else if (ch < 0x110000) { + char* dest = json_lex_append_prepare(lex, 4); + *(dest++) = (char)((ch>>18) | 0xF0); + *(dest++) = (char)(((ch>>12) & 0x3F) | 0x80); + *(dest++) = (char)(((ch>>6) & 0x3F) | 0x80); + *dest = (char)((ch & 0x3F) | 0x80); + } +} + +// Replaces the string with the result of a formatted printf. +void json_lex_sprintf(json_lex_state_t* lex, const char* format, ...) { + va_list ap; + va_start(ap, format); + int r = vsnprintf(lex->last_str, lex->last_str_alloc, format, ap); + va_end(ap); + if (r >= 0) { + if ((ulong)r >= lex->last_str_alloc) { + /* Try again with more space */ + if (lex->last_str != lex->last_str_firstbuf) + free(lex->last_str); + do { + lex->last_str_alloc <<= 1; + } while ((ulong)r + 1U > lex->last_str_alloc); + lex->last_str = (char*)malloc(lex->last_str_alloc); + va_list ap; + va_start(ap, format); + r = vsnprintf(lex->last_str, lex->last_str_alloc, format, ap); + va_end(ap); + } + lex->last_str_sz = (ulong)r; + } else { + lex->last_str_sz = 0; + lex->last_str[0] = '\0'; + } +} diff --git a/src/app/rpcserver/json_lex.h b/src/app/rpcserver/json_lex.h new file mode 100644 index 0000000000..d430837b0f --- /dev/null +++ b/src/app/rpcserver/json_lex.h @@ -0,0 +1,66 @@ +/***** + Header file for a json lexical scanner +*****/ + +#include "../../util/fd_util.h" + +// Lexical token value +#define JSON_TOKEN_LBRACKET 1 /* [ */ +#define JSON_TOKEN_RBRACKET 2 /* ] */ +#define JSON_TOKEN_LBRACE 3 /* { */ +#define JSON_TOKEN_RBRACE 4 /* } */ +#define JSON_TOKEN_COLON 5 /* : */ +#define JSON_TOKEN_COMMA 6 /* , */ +#define JSON_TOKEN_NULL 7 /* null */ +#define JSON_TOKEN_BOOL 8 /* true or false */ +#define JSON_TOKEN_INTEGER 9 +#define JSON_TOKEN_FLOAT 10 +#define JSON_TOKEN_STRING 11 + +#define JSON_TOKEN_END 0 /* end of input */ +#define JSON_TOKEN_ERROR -1 + +// Lexical state +struct json_lex_state { + // Input json text + const char* json; + ulong json_sz; + + // Current position in text + ulong pos; + // Last token parsed + long last_tok; + + // Value of last boolean + int last_bool; + // Value of last string, number (as text), or error message. UTF-8 encoded. + char* last_str; + ulong last_str_sz; + ulong last_str_alloc; + char last_str_firstbuf[512]; +}; +typedef struct json_lex_state json_lex_state_t; + +// Initialize a lexical state given some json text +void json_lex_state_new(json_lex_state_t* state, + const char* json, + ulong json_sz); + +void json_lex_state_delete(json_lex_state_t* state); + +// Retrieve the next token +long json_lex_next_token(json_lex_state_t* state); + +// Get the last lexical text result. This can be a string, number (as +// text), or error message. +const char* json_lex_get_text(json_lex_state_t* state, ulong* sz); + +// Convert the string to an integer (assuming decimal representation) +long json_lex_as_int(json_lex_state_t* lex); + +// Convert the string to a float +double json_lex_as_float(json_lex_state_t* lex); + +// Replaces the string with the result of a formatted printf. +void json_lex_sprintf(json_lex_state_t* lex, const char* format, ...) + __attribute__ ((format (printf, 2, 3))); diff --git a/src/app/rpcserver/keywords.c b/src/app/rpcserver/keywords.c new file mode 100644 index 0000000000..faf5068141 --- /dev/null +++ b/src/app/rpcserver/keywords.c @@ -0,0 +1,828 @@ +// This file is generated by genkeywords.cxx. DO NOT EDIT DIRECTLY! +#include "keywords.h" +long fd_webserver_json_keyword(const char* keyw, unsigned long keyw_sz) { + switch (keyw_sz) { + case 2: + if (((*(unsigned long*)&keyw[0] & 0xFFFFUL) == 0x6469UL)) { + return KEYW_JSON_ID; // "id" + } + break; + case 4: + if (((*(unsigned long*)&keyw[0] & 0xFFFFFFFFUL) == 0x746E696DUL)) { + return KEYW_JSON_MINT; // "mint" + } + break; + case 5: + switch (keyw[0]) { + case 'b': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFUL) == 0x73657479UL)) { + return KEYW_JSON_BYTES; // "bytes" + } + break; + case 'e': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFUL) == 0x68636F70UL)) { + return KEYW_JSON_EPOCH; // "epoch" + } + break; + case 'l': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFUL) == 0x74696D69UL)) { + return KEYW_JSON_LIMIT; // "limit" + } + break; + } + break; + case 6: + switch (keyw[0]) { + case 'l': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFUL) == 0x6874676E65UL)) { + return KEYW_JSON_LENGTH; // "length" + } + break; + case 'm': + if (keyw[1] == 'e') { + switch (keyw[2]) { + case 'm': + if (((*(unsigned long*)&keyw[3] & 0xFFFFFFUL) == 0x706D63UL)) { + return KEYW_JSON_MEMCMP; // "memcmp" + } + break; + case 't': + if (((*(unsigned long*)&keyw[3] & 0xFFFFFFUL) == 0x646F68UL)) { + return KEYW_JSON_METHOD; // "method" + } + break; + } + } + break; + case 'o': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFUL) == 0x7465736666UL)) { + return KEYW_JSON_OFFSET; // "offset" + } + break; + case 'p': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFUL) == 0x736D617261UL)) { + return KEYW_JSON_PARAMS; // "params" + } + break; + } + break; + case 7: + switch (keyw[0]) { + case 'j': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFFFUL) == 0x6370726E6F73UL)) { + return KEYW_JSON_JSONRPC; // "jsonrpc" + } + break; + case 'r': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFFFUL) == 0x736472617765UL)) { + return KEYW_JSON_REWARDS; // "rewards" + } + break; + case 'f': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFFFUL) == 0x737265746C69UL)) { + return KEYW_JSON_FILTERS; // "filters" + } + break; + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'F': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFUL) == 0x736565UL)) { + return KEYW_RPCMETHOD_GETFEES; // "getFees" + } + break; + case 'S': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFUL) == 0x746F6CUL)) { + return KEYW_RPCMETHOD_GETSLOT; // "getSlot" + } + break; + } + } + break; + } + break; + case 8: + switch (keyw[0]) { + case 'd': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFFFFFUL) == 0x657A6953617461UL)) { + return KEYW_JSON_DATASIZE; // "dataSize" + } + break; + case 'e': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFFFFFUL) == 0x676E69646F636EUL)) { + return KEYW_JSON_ENCODING; // "encoding" + } + break; + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFFFFFUL) == 0x6B636F6C427465UL)) { + return KEYW_RPCMETHOD_GETBLOCK; // "getBlock" + } + break; + case 'i': + if (((*(unsigned long*)&keyw[1] & 0xFFFFFFFFFFFFFFUL) == 0x797469746E6564UL)) { + return KEYW_JSON_IDENTITY; // "identity" + } + break; + } + break; + case 9: + switch (keyw[0]) { + case 'd': + if ((*(unsigned long*)&keyw[1] == 0x6563696C53617461UL)) { + return KEYW_JSON_DATASLICE; // "dataSlice" + } + break; + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'B': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFFFFFUL) == 0x736B636F6CUL)) { + return KEYW_RPCMETHOD_GETBLOCKS; // "getBlocks" + } + break; + case 'H': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFFFFFUL) == 0x68746C6165UL)) { + return KEYW_RPCMETHOD_GETHEALTH; // "getHealth" + } + break; + case 'S': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFFFFFUL) == 0x796C707075UL)) { + return KEYW_RPCMETHOD_GETSUPPLY; // "getSupply" + } + break; + } + } + break; + case 'p': + if ((*(unsigned long*)&keyw[1] == 0x64496D6172676F72UL)) { + return KEYW_JSON_PROGRAMID; // "programId" + } + break; + } + break; + case 10: + switch (keyw[0]) { + case 'c': + if ((*(unsigned long*)&keyw[1] == 0x6E656D74696D6D6FUL) && keyw[9] == 't') { + return KEYW_JSON_COMMITMENT; // "commitment" + } + break; + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'B': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFFFFFFFUL) == 0x65636E616C61UL)) { + return KEYW_RPCMETHOD_GETBALANCE; // "getBalance" + } + break; + case 'V': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFFFFFFFUL) == 0x6E6F69737265UL)) { + return KEYW_RPCMETHOD_GETVERSION; // "getVersion" + } + break; + } + } + break; + case 'v': + if ((*(unsigned long*)&keyw[1] == 0x656B62755065746FUL) && keyw[9] == 'y') { + return KEYW_JSON_VOTEPUBKEY; // "votePubkey" + } + break; + } + break; + case 11: + if ((*(unsigned long*)&keyw[0] == 0x746E656449746567UL) && ((*(unsigned long*)&keyw[8] & 0xFFFFFFUL) == 0x797469UL)) { + return KEYW_RPCMETHOD_GETIDENTITY; // "getIdentity" + } + break; + case 12: + if (((*(unsigned long*)&keyw[0] & 0xFFFFFFUL) == 0x746567UL)) { + switch (keyw[3]) { + case 'B': + if ((*(unsigned long*)&keyw[4] == 0x656D69546B636F6CUL)) { + return KEYW_RPCMETHOD_GETBLOCKTIME; // "getBlockTime" + } + break; + case 'E': + if ((*(unsigned long*)&keyw[4] == 0x6F666E4968636F70UL)) { + return KEYW_RPCMETHOD_GETEPOCHINFO; // "getEpochInfo" + } + break; + } + } + break; + case 13: + switch (keyw[0]) { + case 'g': + if ((*(unsigned long*)&keyw[1] == 0x654C746F6C537465UL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFUL) == 0x72656461UL)) { + return KEYW_RPCMETHOD_GETSLOTLEADER; // "getSlotLeader" + } + break; + case 'l': + if ((*(unsigned long*)&keyw[1] == 0x637362755373676FUL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFUL) == 0x65626972UL)) { + return KEYW_WS_METHOD_LOGSSUBSCRIBE; // "logsSubscribe" + } + break; + case 'r': + if ((*(unsigned long*)&keyw[1] == 0x6373627553746F6FUL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFUL) == 0x65626972UL)) { + return KEYW_WS_METHOD_ROOTSUBSCRIBE; // "rootSubscribe" + } + break; + case 's': + if ((*(unsigned long*)&keyw[1] == 0x6373627553746F6CUL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFUL) == 0x65626972UL)) { + return KEYW_WS_METHOD_SLOTSUBSCRIBE; // "slotSubscribe" + } + break; + case 'v': + if ((*(unsigned long*)&keyw[1] == 0x637362755365746FUL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFUL) == 0x65626972UL)) { + return KEYW_WS_METHOD_VOTESUBSCRIBE; // "voteSubscribe" + } + break; + } + break; + case 14: + switch (keyw[0]) { + case 'b': + if ((*(unsigned long*)&keyw[1] == 0x736275536B636F6CUL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFFFUL) == 0x6562697263UL)) { + return KEYW_WS_METHOD_BLOCKSUBSCRIBE; // "blockSubscribe" + } + break; + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'A': + if ((*(unsigned long*)&keyw[4] == 0x6E49746E756F6363UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFUL) == 0x6F66UL)) { + return KEYW_RPCMETHOD_GETACCOUNTINFO; // "getAccountInfo" + } + break; + case 'B': + if ((*(unsigned long*)&keyw[4] == 0x676965486B636F6CUL) && ((*(unsigned long*)&keyw[12] & 0xFFFFUL) == 0x7468UL)) { + return KEYW_RPCMETHOD_GETBLOCKHEIGHT; // "getBlockHeight" + } + break; + case 'G': + if ((*(unsigned long*)&keyw[4] == 0x6148736973656E65UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFUL) == 0x6873UL)) { + return KEYW_RPCMETHOD_GETGENESISHASH; // "getGenesisHash" + } + break; + case 'S': + if ((*(unsigned long*)&keyw[4] == 0x656461654C746F6CUL) && ((*(unsigned long*)&keyw[12] & 0xFFFFUL) == 0x7372UL)) { + return KEYW_RPCMETHOD_GETSLOTLEADERS; // "getSlotLeaders" + } + break; + case 'T': + switch (keyw[4]) { + case 'o': + if ((*(unsigned long*)&keyw[5] == 0x6C707075536E656BUL) && keyw[13] == 'y') { + return KEYW_RPCMETHOD_GETTOKENSUPPLY; // "getTokenSupply" + } + break; + case 'r': + if ((*(unsigned long*)&keyw[5] == 0x6F69746361736E61UL) && keyw[13] == 'n') { + return KEYW_RPCMETHOD_GETTRANSACTION; // "getTransaction" + } + break; + } + break; + } + } + break; + case 'r': + if ((*(unsigned long*)&keyw[1] == 0x6941747365757165UL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFFFUL) == 0x706F726472UL)) { + return KEYW_RPCMETHOD_REQUESTAIRDROP; // "requestAirdrop" + } + break; + } + break; + case 15: + switch (keyw[0]) { + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'C': + if ((*(unsigned long*)&keyw[4] == 0x6F4E72657473756CUL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFUL) == 0x736564UL)) { + return KEYW_RPCMETHOD_GETCLUSTERNODES; // "getClusterNodes" + } + break; + case 'S': + if ((*(unsigned long*)&keyw[4] == 0x53746F687370616EUL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFUL) == 0x746F6CUL)) { + return KEYW_RPCMETHOD_GETSNAPSHOTSLOT; // "getSnapshotSlot" + } + break; + case 'V': + if ((*(unsigned long*)&keyw[4] == 0x756F63634165746FUL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFUL) == 0x73746EUL)) { + return KEYW_RPCMETHOD_GETVOTEACCOUNTS; // "getVoteAccounts" + } + break; + } + } + break; + case 'l': + if ((*(unsigned long*)&keyw[1] == 0x6275736E5573676FUL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFFFFFUL) == 0x656269726373UL)) { + return KEYW_WS_METHOD_LOGSUNSUBSCRIBE; // "logsUnsubscribe" + } + break; + case 'r': + if ((*(unsigned long*)&keyw[1] == 0x6275736E55746F6FUL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFFFFFUL) == 0x656269726373UL)) { + return KEYW_WS_METHOD_ROOTUNSUBSCRIBE; // "rootUnsubscribe" + } + break; + case 's': + switch (keyw[1]) { + case 'e': + if ((*(unsigned long*)&keyw[2] == 0x61736E617254646EUL) && ((*(unsigned long*)&keyw[10] & 0xFFFFFFFFFFUL) == 0x6E6F697463UL)) { + return KEYW_RPCMETHOD_SENDTRANSACTION; // "sendTransaction" + } + break; + case 'l': + if ((*(unsigned long*)&keyw[2] == 0x736275736E55746FUL) && ((*(unsigned long*)&keyw[10] & 0xFFFFFFFFFFUL) == 0x6562697263UL)) { + return KEYW_WS_METHOD_SLOTUNSUBSCRIBE; // "slotUnsubscribe" + } + break; + } + break; + case 'v': + if ((*(unsigned long*)&keyw[1] == 0x6275736E5565746FUL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFFFFFUL) == 0x656269726373UL)) { + return KEYW_WS_METHOD_VOTEUNSUBSCRIBE; // "voteUnsubscribe" + } + break; + } + break; + case 16: + switch (keyw[0]) { + case 'a': + if ((*(unsigned long*)&keyw[1] == 0x7553746E756F6363UL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFFFFFFFUL) == 0x65626972637362UL)) { + return KEYW_WS_METHOD_ACCOUNTSUBSCRIBE; // "accountSubscribe" + } + break; + case 'b': + if ((*(unsigned long*)&keyw[1] == 0x75736E556B636F6CUL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFFFFFFFUL) == 0x65626972637362UL)) { + return KEYW_WS_METHOD_BLOCKUNSUBSCRIBE; // "blockUnsubscribe" + } + break; + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'E': + if ((*(unsigned long*)&keyw[4] == 0x6568635368636F70UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFUL) == 0x656C7564UL)) { + return KEYW_RPCMETHOD_GETEPOCHSCHEDULE; // "getEpochSchedule" + } + break; + case 'F': + if ((*(unsigned long*)&keyw[4] == 0x73654D726F466565UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFUL) == 0x65676173UL)) { + return KEYW_RPCMETHOD_GETFEEFORMESSAGE; // "getFeeForMessage" + } + break; + case 'I': + if ((*(unsigned long*)&keyw[4] == 0x6E6F6974616C666EUL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFUL) == 0x65746152UL)) { + return KEYW_RPCMETHOD_GETINFLATIONRATE; // "getInflationRate" + } + break; + } + } + break; + case 'i': + if ((*(unsigned long*)&keyw[1] == 0x61686B636F6C4273UL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFFFFFFFUL) == 0x64696C61566873UL)) { + return KEYW_RPCMETHOD_ISBLOCKHASHVALID; // "isBlockhashValid" + } + break; + case 'p': + if ((*(unsigned long*)&keyw[1] == 0x75536D6172676F72UL) && ((*(unsigned long*)&keyw[9] & 0xFFFFFFFFFFFFFFUL) == 0x65626972637362UL)) { + return KEYW_WS_METHOD_PROGRAMSUBSCRIBE; // "programSubscribe" + } + break; + } + break; + case 17: + switch (keyw[0]) { + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'C': + if ((*(unsigned long*)&keyw[4] == 0x64656D7269666E6FUL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFUL) == 0x6B636F6C42UL)) { + return KEYW_RPCMETHOD_GETCONFIRMEDBLOCK; // "getConfirmedBlock" + } + break; + case 'L': + if ((*(unsigned long*)&keyw[4] == 0x6863537265646165UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFUL) == 0x656C756465UL)) { + return KEYW_RPCMETHOD_GETLEADERSCHEDULE; // "getLeaderSchedule" + } + break; + } + } + break; + case 'm': + if ((*(unsigned long*)&keyw[1] == 0x654C6D756D696E69UL) && (*(unsigned long*)&keyw[9] == 0x746F6C5372656764UL)) { + return KEYW_RPCMETHOD_MINIMUMLEDGERSLOT; // "minimumLedgerSlot" + } + break; + } + break; + case 18: + switch (keyw[0]) { + case 'a': + if ((*(unsigned long*)&keyw[1] == 0x6E55746E756F6363UL) && (*(unsigned long*)&keyw[9] == 0x6269726373627573UL) && keyw[17] == 'e') { + return KEYW_WS_METHOD_ACCOUNTUNSUBSCRIBE; // "accountUnsubscribe" + } + break; + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'B': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFFFUL) == 0x6B636F6CUL)) { + switch (keyw[8]) { + case 'C': + if ((*(unsigned long*)&keyw[9] == 0x6E656D74696D6D6FUL) && keyw[17] == 't') { + return KEYW_RPCMETHOD_GETBLOCKCOMMITMENT; // "getBlockCommitment" + } + break; + case 'P': + if ((*(unsigned long*)&keyw[9] == 0x6F69746375646F72UL) && keyw[17] == 'n') { + return KEYW_RPCMETHOD_GETBLOCKPRODUCTION; // "getBlockProduction" + } + break; + case 's': + if ((*(unsigned long*)&keyw[9] == 0x696D694C68746957UL) && keyw[17] == 't') { + return KEYW_RPCMETHOD_GETBLOCKSWITHLIMIT; // "getBlocksWithLimit" + } + break; + } + } + break; + case 'C': + if ((*(unsigned long*)&keyw[4] == 0x64656D7269666E6FUL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFFFUL) == 0x736B636F6C42UL)) { + return KEYW_RPCMETHOD_GETCONFIRMEDBLOCKS; // "getConfirmedBlocks" + } + break; + case 'F': + if ((*(unsigned long*)&keyw[4] == 0x6F47657461526565UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFFFUL) == 0x726F6E726576UL)) { + return KEYW_RPCMETHOD_GETFEERATEGOVERNOR; // "getFeeRateGovernor" + } + break; + case 'I': + if ((*(unsigned long*)&keyw[4] == 0x6E6F6974616C666EUL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFFFUL) == 0x647261776552UL)) { + return KEYW_RPCMETHOD_GETINFLATIONREWARD; // "getInflationReward" + } + break; + case 'L': + if (keyw[4] == 'a') { + switch (keyw[5]) { + case 'r': + if ((*(unsigned long*)&keyw[6] == 0x6F63634174736567UL) && ((*(unsigned long*)&keyw[14] & 0xFFFFFFFFUL) == 0x73746E75UL)) { + return KEYW_RPCMETHOD_GETLARGESTACCOUNTS; // "getLargestAccounts" + } + break; + case 't': + if ((*(unsigned long*)&keyw[6] == 0x6B636F6C42747365UL) && ((*(unsigned long*)&keyw[14] & 0xFFFFFFFFUL) == 0x68736168UL)) { + return KEYW_RPCMETHOD_GETLATESTBLOCKHASH; // "getLatestBlockhash" + } + break; + } + } + break; + case 'P': + if ((*(unsigned long*)&keyw[4] == 0x63416D6172676F72UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFFFUL) == 0x73746E756F63UL)) { + return KEYW_RPCMETHOD_GETPROGRAMACCOUNTS; // "getProgramAccounts" + } + break; + case 'R': + if ((*(unsigned long*)&keyw[4] == 0x6F6C42746E656365UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFFFUL) == 0x687361686B63UL)) { + return KEYW_RPCMETHOD_GETRECENTBLOCKHASH; // "getRecentBlockhash" + } + break; + case 'S': + if ((*(unsigned long*)&keyw[4] == 0x69746341656B6174UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFFFUL) == 0x6E6F69746176UL)) { + return KEYW_RPCMETHOD_GETSTAKEACTIVATION; // "getStakeActivation" + } + break; + } + } + break; + case 'p': + if ((*(unsigned long*)&keyw[1] == 0x6E556D6172676F72UL) && (*(unsigned long*)&keyw[9] == 0x6269726373627573UL) && keyw[17] == 'e') { + return KEYW_WS_METHOD_PROGRAMUNSUBSCRIBE; // "programUnsubscribe" + } + break; + case 's': + if ((*(unsigned long*)&keyw[1] == 0x65727574616E6769UL) && (*(unsigned long*)&keyw[9] == 0x6269726373627553UL) && keyw[17] == 'e') { + return KEYW_WS_METHOD_SIGNATURESUBSCRIBE; // "signatureSubscribe" + } + break; + case 't': + if ((*(unsigned long*)&keyw[1] == 0x69746361736E6172UL) && (*(unsigned long*)&keyw[9] == 0x6C69617465446E6FUL) && keyw[17] == 's') { + return KEYW_JSON_TRANSACTIONDETAILS; // "transactionDetails" + } + break; + } + break; + case 19: + switch (keyw[0]) { + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'M': + if ((*(unsigned long*)&keyw[4] == 0x41656C7069746C75UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFFFFFUL) == 0x73746E756F6363UL)) { + return KEYW_RPCMETHOD_GETMULTIPLEACCOUNTS; // "getMultipleAccounts" + } + break; + case 'T': + if ((*(unsigned long*)&keyw[4] == 0x69746361736E6172UL) && ((*(unsigned long*)&keyw[12] & 0xFFFFFFFFFFFFFFUL) == 0x746E756F436E6FUL)) { + return KEYW_RPCMETHOD_GETTRANSACTIONCOUNT; // "getTransactionCount" + } + break; + } + } + break; + case 's': + if ((*(unsigned long*)&keyw[1] == 0x546574616C756D69UL) && (*(unsigned long*)&keyw[9] == 0x69746361736E6172UL) && ((*(unsigned long*)&keyw[17] & 0xFFFFUL) == 0x6E6FUL)) { + return KEYW_RPCMETHOD_SIMULATETRANSACTION; // "simulateTransaction" + } + break; + } + break; + case 20: + switch (keyw[0]) { + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'I': + if ((*(unsigned long*)&keyw[4] == 0x6E6F6974616C666EUL) && (*(unsigned long*)&keyw[12] == 0x726F6E7265766F47UL)) { + return KEYW_RPCMETHOD_GETINFLATIONGOVERNOR; // "getInflationGovernor" + } + break; + case 'M': + if ((*(unsigned long*)&keyw[4] == 0x6E61727465527861UL) && (*(unsigned long*)&keyw[12] == 0x746F6C5374696D73UL)) { + return KEYW_RPCMETHOD_GETMAXRETRANSMITSLOT; // "getMaxRetransmitSlot" + } + break; + case 'S': + if ((*(unsigned long*)&keyw[4] == 0x65727574616E6769UL) && (*(unsigned long*)&keyw[12] == 0x7365737574617453UL)) { + return KEYW_RPCMETHOD_GETSIGNATURESTATUSES; // "getSignatureStatuses" + } + break; + } + } + break; + case 's': + if ((*(unsigned long*)&keyw[1] == 0x65727574616E6769UL) && (*(unsigned long*)&keyw[9] == 0x7263736275736E55UL) && ((*(unsigned long*)&keyw[17] & 0xFFFFFFUL) == 0x656269UL)) { + return KEYW_WS_METHOD_SIGNATUREUNSUBSCRIBE; // "signatureUnsubscribe" + } + break; + } + break; + case 21: + switch (keyw[0]) { + case 'g': + if ((*(unsigned long*)&keyw[1] == 0x72685378614D7465UL) && (*(unsigned long*)&keyw[9] == 0x747265736E496465UL) && ((*(unsigned long*)&keyw[17] & 0xFFFFFFFFUL) == 0x746F6C53UL)) { + return KEYW_RPCMETHOD_GETMAXSHREDINSERTSLOT; // "getMaxShredInsertSlot" + } + break; + case 's': + if ((*(unsigned long*)&keyw[1] == 0x6164705573746F6CUL) && (*(unsigned long*)&keyw[9] == 0x6373627553736574UL) && ((*(unsigned long*)&keyw[17] & 0xFFFFFFFFUL) == 0x65626972UL)) { + return KEYW_WS_METHOD_SLOTSUPDATESSUBSCRIBE; // "slotsUpdatesSubscribe" + } + break; + } + break; + case 22: + if (((*(unsigned long*)&keyw[0] & 0xFFFFFFUL) == 0x746567UL)) { + switch (keyw[3]) { + case 'F': + if ((*(unsigned long*)&keyw[4] == 0x6961764174737269UL) && (*(unsigned long*)&keyw[12] == 0x6F6C42656C62616CUL) && ((*(unsigned long*)&keyw[20] & 0xFFFFUL) == 0x6B63UL)) { + return KEYW_RPCMETHOD_GETFIRSTAVAILABLEBLOCK; // "getFirstAvailableBlock" + } + break; + case 'H': + if ((*(unsigned long*)&keyw[4] == 0x6E53747365686769UL) && (*(unsigned long*)&keyw[12] == 0x6C53746F68737061UL) && ((*(unsigned long*)&keyw[20] & 0xFFFFUL) == 0x746FUL)) { + return KEYW_RPCMETHOD_GETHIGHESTSNAPSHOTSLOT; // "getHighestSnapshotSlot" + } + break; + case 'T': + if ((*(unsigned long*)&keyw[4] == 0x6F6363416E656B6FUL) && (*(unsigned long*)&keyw[12] == 0x6E616C6142746E75UL) && ((*(unsigned long*)&keyw[20] & 0xFFFFUL) == 0x6563UL)) { + return KEYW_RPCMETHOD_GETTOKENACCOUNTBALANCE; // "getTokenAccountBalance" + } + break; + } + } + break; + case 23: + switch (keyw[0]) { + case 'g': + if (((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL)) { + switch (keyw[3]) { + case 'C': + if ((*(unsigned long*)&keyw[4] == 0x64656D7269666E6FUL) && (*(unsigned long*)&keyw[12] == 0x746361736E617254UL) && ((*(unsigned long*)&keyw[20] & 0xFFFFFFUL) == 0x6E6F69UL)) { + return KEYW_RPCMETHOD_GETCONFIRMEDTRANSACTION; // "getConfirmedTransaction" + } + break; + case 'S': + if ((*(unsigned long*)&keyw[4] == 0x65727574616E6769UL) && (*(unsigned long*)&keyw[12] == 0x72646441726F4673UL) && ((*(unsigned long*)&keyw[20] & 0xFFFFFFUL) == 0x737365UL)) { + return KEYW_RPCMETHOD_GETSIGNATURESFORADDRESS; // "getSignaturesForAddress" + } + break; + case 'T': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFFFUL) == 0x6E656B6FUL)) { + switch (keyw[8]) { + case 'A': + if ((*(unsigned long*)&keyw[9] == 0x4273746E756F6363UL) && ((*(unsigned long*)&keyw[17] & 0xFFFFFFFFFFFFUL) == 0x72656E774F79UL)) { + return KEYW_RPCMETHOD_GETTOKENACCOUNTSBYOWNER; // "getTokenAccountsByOwner" + } + break; + case 'L': + if ((*(unsigned long*)&keyw[9] == 0x6341747365677261UL) && ((*(unsigned long*)&keyw[17] & 0xFFFFFFFFFFFFUL) == 0x73746E756F63UL)) { + return KEYW_RPCMETHOD_GETTOKENLARGESTACCOUNTS; // "getTokenLargestAccounts" + } + break; + } + } + break; + } + } + break; + case 's': + if ((*(unsigned long*)&keyw[1] == 0x6164705573746F6CUL) && (*(unsigned long*)&keyw[9] == 0x6275736E55736574UL) && ((*(unsigned long*)&keyw[17] & 0xFFFFFFFFFFFFUL) == 0x656269726373UL)) { + return KEYW_WS_METHOD_SLOTSUPDATESUNSUBSCRIBE; // "slotsUpdatesUnsubscribe" + } + break; + } + break; + case 24: + if ((*(unsigned long*)&keyw[0] == 0x7254686372616573UL) && (*(unsigned long*)&keyw[8] == 0x6F69746361736E61UL) && (*(unsigned long*)&keyw[16] == 0x79726F747369486EUL)) { + return KEYW_JSON_SEARCHTRANSACTIONHISTORY; // "searchTransactionHistory" + } + break; + case 25: + if ((*(unsigned long*)&keyw[0] == 0x656B617453746567UL) && (*(unsigned long*)&keyw[8] == 0x446D756D696E694DUL) && (*(unsigned long*)&keyw[16] == 0x6F69746167656C65UL) && keyw[24] == 'n') { + return KEYW_RPCMETHOD_GETSTAKEMINIMUMDELEGATION; // "getStakeMinimumDelegation" + } + break; + case 26: + if ((*(unsigned long*)&keyw[0] == 0x6E656B6F54746567UL) && (*(unsigned long*)&keyw[8] == 0x73746E756F636341UL) && (*(unsigned long*)&keyw[16] == 0x6167656C65447942UL) && ((*(unsigned long*)&keyw[24] & 0xFFFFUL) == 0x6574UL)) { + return KEYW_RPCMETHOD_GETTOKENACCOUNTSBYDELEGATE; // "getTokenAccountsByDelegate" + } + break; + case 27: + if (((*(unsigned long*)&keyw[0] & 0xFFFFFFUL) == 0x746567UL)) { + switch (keyw[3]) { + case 'C': + if ((*(unsigned long*)&keyw[4] == 0x64656D7269666E6FUL) && (*(unsigned long*)&keyw[12] == 0x6957736B636F6C42UL) && ((*(unsigned long*)&keyw[20] & 0xFFFFFFFFFFFFFFUL) == 0x74696D694C6874UL)) { + return KEYW_RPCMETHOD_GETCONFIRMEDBLOCKSWITHLIMIT; // "getConfirmedBlocksWithLimit" + } + break; + case 'R': + if (((*(unsigned long*)&keyw[4] & 0xFFFFFFFFFFFFUL) == 0x50746E656365UL)) { + switch (keyw[10]) { + case 'e': + if ((*(unsigned long*)&keyw[11] == 0x636E616D726F6672UL) && (*(unsigned long*)&keyw[19] == 0x73656C706D615365UL)) { + return KEYW_RPCMETHOD_GETRECENTPERFORMANCESAMPLES; // "getRecentPerformanceSamples" + } + break; + case 'r': + if ((*(unsigned long*)&keyw[11] == 0x617A697469726F69UL) && (*(unsigned long*)&keyw[19] == 0x736565466E6F6974UL)) { + return KEYW_RPCMETHOD_GETRECENTPRIORITIZATIONFEES; // "getRecentPrioritizationFees" + } + break; + } + } + break; + } + } + break; + case 28: + if ((*(unsigned long*)&keyw[0] == 0x6143656546746567UL) && (*(unsigned long*)&keyw[8] == 0x726F74616C75636CUL) && (*(unsigned long*)&keyw[16] == 0x6B636F6C42726F46UL) && ((*(unsigned long*)&keyw[24] & 0xFFFFFFFFUL) == 0x68736168UL)) { + return KEYW_RPCMETHOD_GETFEECALCULATORFORBLOCKHASH; // "getFeeCalculatorForBlockhash" + } + break; + case 30: + if ((*(unsigned long*)&keyw[0] == 0x6F7070755378616DUL) && (*(unsigned long*)&keyw[8] == 0x6E61725464657472UL) && (*(unsigned long*)&keyw[16] == 0x566E6F6974636173UL) && ((*(unsigned long*)&keyw[24] & 0xFFFFFFFFFFFFUL) == 0x6E6F69737265UL)) { + return KEYW_JSON_MAXSUPPORTEDTRANSACTIONVERSION; // "maxSupportedTransactionVersion" + } + break; + case 33: + if (((*(unsigned long*)&keyw[0] & 0xFFFFFFUL) == 0x746567UL)) { + switch (keyw[3]) { + case 'C': + if ((*(unsigned long*)&keyw[4] == 0x64656D7269666E6FUL) && (*(unsigned long*)&keyw[12] == 0x727574616E676953UL) && (*(unsigned long*)&keyw[20] == 0x646441726F467365UL) && ((*(unsigned long*)&keyw[28] & 0xFFFFFFFFFFUL) == 0x3273736572UL)) { + return KEYW_RPCMETHOD_GETCONFIRMEDSIGNATURESFORADDRESS2; // "getConfirmedSignaturesForAddress2" + } + break; + case 'M': + if ((*(unsigned long*)&keyw[4] == 0x61426D756D696E69UL) && (*(unsigned long*)&keyw[12] == 0x726F4665636E616CUL) && (*(unsigned long*)&keyw[20] == 0x6D657845746E6552UL) && ((*(unsigned long*)&keyw[28] & 0xFFFFFFFFFFUL) == 0x6E6F697470UL)) { + return KEYW_RPCMETHOD_GETMINIMUMBALANCEFORRENTEXEMPTION; // "getMinimumBalanceForRentExemption" + } + break; + } + } + break; + } + return KEYW_UNKNOWN; +} +const char* un_fd_webserver_json_keyword(long id) { + switch (id) { + case KEYW_JSON_JSONRPC: return "jsonrpc"; + case KEYW_JSON_ID: return "id"; + case KEYW_JSON_METHOD: return "method"; + case KEYW_JSON_PARAMS: return "params"; + case KEYW_JSON_BYTES: return "bytes"; + case KEYW_JSON_COMMITMENT: return "commitment"; + case KEYW_JSON_DATASIZE: return "dataSize"; + case KEYW_JSON_DATASLICE: return "dataSlice"; + case KEYW_JSON_ENCODING: return "encoding"; + case KEYW_JSON_EPOCH: return "epoch"; + case KEYW_JSON_FILTERS: return "filters"; + case KEYW_JSON_IDENTITY: return "identity"; + case KEYW_JSON_LENGTH: return "length"; + case KEYW_JSON_LIMIT: return "limit"; + case KEYW_JSON_MAXSUPPORTEDTRANSACTIONVERSION: return "maxSupportedTransactionVersion"; + case KEYW_JSON_MEMCMP: return "memcmp"; + case KEYW_JSON_MINT: return "mint"; + case KEYW_JSON_OFFSET: return "offset"; + case KEYW_JSON_PROGRAMID: return "programId"; + case KEYW_JSON_REWARDS: return "rewards"; + case KEYW_JSON_SEARCHTRANSACTIONHISTORY: return "searchTransactionHistory"; + case KEYW_JSON_TRANSACTIONDETAILS: return "transactionDetails"; + case KEYW_JSON_VOTEPUBKEY: return "votePubkey"; + case KEYW_RPCMETHOD_GETACCOUNTINFO: return "getAccountInfo"; + case KEYW_RPCMETHOD_GETBALANCE: return "getBalance"; + case KEYW_RPCMETHOD_GETBLOCK: return "getBlock"; + case KEYW_RPCMETHOD_GETBLOCKCOMMITMENT: return "getBlockCommitment"; + case KEYW_RPCMETHOD_GETBLOCKHEIGHT: return "getBlockHeight"; + case KEYW_RPCMETHOD_GETBLOCKPRODUCTION: return "getBlockProduction"; + case KEYW_RPCMETHOD_GETBLOCKS: return "getBlocks"; + case KEYW_RPCMETHOD_GETBLOCKSWITHLIMIT: return "getBlocksWithLimit"; + case KEYW_RPCMETHOD_GETBLOCKTIME: return "getBlockTime"; + case KEYW_RPCMETHOD_GETCLUSTERNODES: return "getClusterNodes"; + case KEYW_RPCMETHOD_GETCONFIRMEDBLOCK: return "getConfirmedBlock"; + case KEYW_RPCMETHOD_GETCONFIRMEDBLOCKS: return "getConfirmedBlocks"; + case KEYW_RPCMETHOD_GETCONFIRMEDBLOCKSWITHLIMIT: return "getConfirmedBlocksWithLimit"; + case KEYW_RPCMETHOD_GETCONFIRMEDSIGNATURESFORADDRESS2: return "getConfirmedSignaturesForAddress2"; + case KEYW_RPCMETHOD_GETCONFIRMEDTRANSACTION: return "getConfirmedTransaction"; + case KEYW_RPCMETHOD_GETEPOCHINFO: return "getEpochInfo"; + case KEYW_RPCMETHOD_GETEPOCHSCHEDULE: return "getEpochSchedule"; + case KEYW_RPCMETHOD_GETFEECALCULATORFORBLOCKHASH: return "getFeeCalculatorForBlockhash"; + case KEYW_RPCMETHOD_GETFEEFORMESSAGE: return "getFeeForMessage"; + case KEYW_RPCMETHOD_GETFEERATEGOVERNOR: return "getFeeRateGovernor"; + case KEYW_RPCMETHOD_GETFEES: return "getFees"; + case KEYW_RPCMETHOD_GETFIRSTAVAILABLEBLOCK: return "getFirstAvailableBlock"; + case KEYW_RPCMETHOD_GETGENESISHASH: return "getGenesisHash"; + case KEYW_RPCMETHOD_GETHEALTH: return "getHealth"; + case KEYW_RPCMETHOD_GETHIGHESTSNAPSHOTSLOT: return "getHighestSnapshotSlot"; + case KEYW_RPCMETHOD_GETIDENTITY: return "getIdentity"; + case KEYW_RPCMETHOD_GETINFLATIONGOVERNOR: return "getInflationGovernor"; + case KEYW_RPCMETHOD_GETINFLATIONRATE: return "getInflationRate"; + case KEYW_RPCMETHOD_GETINFLATIONREWARD: return "getInflationReward"; + case KEYW_RPCMETHOD_GETLARGESTACCOUNTS: return "getLargestAccounts"; + case KEYW_RPCMETHOD_GETLATESTBLOCKHASH: return "getLatestBlockhash"; + case KEYW_RPCMETHOD_GETLEADERSCHEDULE: return "getLeaderSchedule"; + case KEYW_RPCMETHOD_GETMAXRETRANSMITSLOT: return "getMaxRetransmitSlot"; + case KEYW_RPCMETHOD_GETMAXSHREDINSERTSLOT: return "getMaxShredInsertSlot"; + case KEYW_RPCMETHOD_GETMINIMUMBALANCEFORRENTEXEMPTION: return "getMinimumBalanceForRentExemption"; + case KEYW_RPCMETHOD_GETMULTIPLEACCOUNTS: return "getMultipleAccounts"; + case KEYW_RPCMETHOD_GETPROGRAMACCOUNTS: return "getProgramAccounts"; + case KEYW_RPCMETHOD_GETRECENTBLOCKHASH: return "getRecentBlockhash"; + case KEYW_RPCMETHOD_GETRECENTPERFORMANCESAMPLES: return "getRecentPerformanceSamples"; + case KEYW_RPCMETHOD_GETRECENTPRIORITIZATIONFEES: return "getRecentPrioritizationFees"; + case KEYW_RPCMETHOD_GETSIGNATURESFORADDRESS: return "getSignaturesForAddress"; + case KEYW_RPCMETHOD_GETSIGNATURESTATUSES: return "getSignatureStatuses"; + case KEYW_RPCMETHOD_GETSLOT: return "getSlot"; + case KEYW_RPCMETHOD_GETSLOTLEADER: return "getSlotLeader"; + case KEYW_RPCMETHOD_GETSLOTLEADERS: return "getSlotLeaders"; + case KEYW_RPCMETHOD_GETSNAPSHOTSLOT: return "getSnapshotSlot"; + case KEYW_RPCMETHOD_GETSTAKEACTIVATION: return "getStakeActivation"; + case KEYW_RPCMETHOD_GETSTAKEMINIMUMDELEGATION: return "getStakeMinimumDelegation"; + case KEYW_RPCMETHOD_GETSUPPLY: return "getSupply"; + case KEYW_RPCMETHOD_GETTOKENACCOUNTBALANCE: return "getTokenAccountBalance"; + case KEYW_RPCMETHOD_GETTOKENACCOUNTSBYDELEGATE: return "getTokenAccountsByDelegate"; + case KEYW_RPCMETHOD_GETTOKENACCOUNTSBYOWNER: return "getTokenAccountsByOwner"; + case KEYW_RPCMETHOD_GETTOKENLARGESTACCOUNTS: return "getTokenLargestAccounts"; + case KEYW_RPCMETHOD_GETTOKENSUPPLY: return "getTokenSupply"; + case KEYW_RPCMETHOD_GETTRANSACTION: return "getTransaction"; + case KEYW_RPCMETHOD_GETTRANSACTIONCOUNT: return "getTransactionCount"; + case KEYW_RPCMETHOD_GETVERSION: return "getVersion"; + case KEYW_RPCMETHOD_GETVOTEACCOUNTS: return "getVoteAccounts"; + case KEYW_RPCMETHOD_ISBLOCKHASHVALID: return "isBlockhashValid"; + case KEYW_RPCMETHOD_MINIMUMLEDGERSLOT: return "minimumLedgerSlot"; + case KEYW_RPCMETHOD_REQUESTAIRDROP: return "requestAirdrop"; + case KEYW_RPCMETHOD_SENDTRANSACTION: return "sendTransaction"; + case KEYW_RPCMETHOD_SIMULATETRANSACTION: return "simulateTransaction"; + case KEYW_WS_METHOD_ACCOUNTSUBSCRIBE: return "accountSubscribe"; + case KEYW_WS_METHOD_ACCOUNTUNSUBSCRIBE: return "accountUnsubscribe"; + case KEYW_WS_METHOD_BLOCKSUBSCRIBE: return "blockSubscribe"; + case KEYW_WS_METHOD_BLOCKUNSUBSCRIBE: return "blockUnsubscribe"; + case KEYW_WS_METHOD_LOGSSUBSCRIBE: return "logsSubscribe"; + case KEYW_WS_METHOD_LOGSUNSUBSCRIBE: return "logsUnsubscribe"; + case KEYW_WS_METHOD_PROGRAMSUBSCRIBE: return "programSubscribe"; + case KEYW_WS_METHOD_PROGRAMUNSUBSCRIBE: return "programUnsubscribe"; + case KEYW_WS_METHOD_ROOTSUBSCRIBE: return "rootSubscribe"; + case KEYW_WS_METHOD_ROOTUNSUBSCRIBE: return "rootUnsubscribe"; + case KEYW_WS_METHOD_SIGNATURESUBSCRIBE: return "signatureSubscribe"; + case KEYW_WS_METHOD_SIGNATUREUNSUBSCRIBE: return "signatureUnsubscribe"; + case KEYW_WS_METHOD_SLOTSUBSCRIBE: return "slotSubscribe"; + case KEYW_WS_METHOD_SLOTUNSUBSCRIBE: return "slotUnsubscribe"; + case KEYW_WS_METHOD_SLOTSUPDATESSUBSCRIBE: return "slotsUpdatesSubscribe"; + case KEYW_WS_METHOD_SLOTSUPDATESUNSUBSCRIBE: return "slotsUpdatesUnsubscribe"; + case KEYW_WS_METHOD_VOTESUBSCRIBE: return "voteSubscribe"; + case KEYW_WS_METHOD_VOTEUNSUBSCRIBE: return "voteUnsubscribe"; + } + return "???"; +} diff --git a/src/app/rpcserver/keywords.h b/src/app/rpcserver/keywords.h new file mode 100644 index 0000000000..57bb9f7911 --- /dev/null +++ b/src/app/rpcserver/keywords.h @@ -0,0 +1,110 @@ +// This file is generated by genkeywords.cxx. DO NOT EDIT DIRECTLY! +#define KEYW_JSON_JSONRPC 0L +#define KEYW_JSON_ID 1L +#define KEYW_JSON_METHOD 2L +#define KEYW_JSON_PARAMS 3L +#define KEYW_JSON_BYTES 4L +#define KEYW_JSON_COMMITMENT 5L +#define KEYW_JSON_DATASIZE 6L +#define KEYW_JSON_DATASLICE 7L +#define KEYW_JSON_ENCODING 8L +#define KEYW_JSON_EPOCH 9L +#define KEYW_JSON_FILTERS 10L +#define KEYW_JSON_IDENTITY 11L +#define KEYW_JSON_LENGTH 12L +#define KEYW_JSON_LIMIT 13L +#define KEYW_JSON_MAXSUPPORTEDTRANSACTIONVERSION 14L +#define KEYW_JSON_MEMCMP 15L +#define KEYW_JSON_MINT 16L +#define KEYW_JSON_OFFSET 17L +#define KEYW_JSON_PROGRAMID 18L +#define KEYW_JSON_REWARDS 19L +#define KEYW_JSON_SEARCHTRANSACTIONHISTORY 20L +#define KEYW_JSON_TRANSACTIONDETAILS 21L +#define KEYW_JSON_VOTEPUBKEY 22L +#define KEYW_RPCMETHOD_GETACCOUNTINFO 23L +#define KEYW_RPCMETHOD_GETBALANCE 24L +#define KEYW_RPCMETHOD_GETBLOCK 25L +#define KEYW_RPCMETHOD_GETBLOCKCOMMITMENT 26L +#define KEYW_RPCMETHOD_GETBLOCKHEIGHT 27L +#define KEYW_RPCMETHOD_GETBLOCKPRODUCTION 28L +#define KEYW_RPCMETHOD_GETBLOCKS 29L +#define KEYW_RPCMETHOD_GETBLOCKSWITHLIMIT 30L +#define KEYW_RPCMETHOD_GETBLOCKTIME 31L +#define KEYW_RPCMETHOD_GETCLUSTERNODES 32L +#define KEYW_RPCMETHOD_GETCONFIRMEDBLOCK 33L +#define KEYW_RPCMETHOD_GETCONFIRMEDBLOCKS 34L +#define KEYW_RPCMETHOD_GETCONFIRMEDBLOCKSWITHLIMIT 35L +#define KEYW_RPCMETHOD_GETCONFIRMEDSIGNATURESFORADDRESS2 36L +#define KEYW_RPCMETHOD_GETCONFIRMEDTRANSACTION 37L +#define KEYW_RPCMETHOD_GETEPOCHINFO 38L +#define KEYW_RPCMETHOD_GETEPOCHSCHEDULE 39L +#define KEYW_RPCMETHOD_GETFEECALCULATORFORBLOCKHASH 40L +#define KEYW_RPCMETHOD_GETFEEFORMESSAGE 41L +#define KEYW_RPCMETHOD_GETFEERATEGOVERNOR 42L +#define KEYW_RPCMETHOD_GETFEES 43L +#define KEYW_RPCMETHOD_GETFIRSTAVAILABLEBLOCK 44L +#define KEYW_RPCMETHOD_GETGENESISHASH 45L +#define KEYW_RPCMETHOD_GETHEALTH 46L +#define KEYW_RPCMETHOD_GETHIGHESTSNAPSHOTSLOT 47L +#define KEYW_RPCMETHOD_GETIDENTITY 48L +#define KEYW_RPCMETHOD_GETINFLATIONGOVERNOR 49L +#define KEYW_RPCMETHOD_GETINFLATIONRATE 50L +#define KEYW_RPCMETHOD_GETINFLATIONREWARD 51L +#define KEYW_RPCMETHOD_GETLARGESTACCOUNTS 52L +#define KEYW_RPCMETHOD_GETLATESTBLOCKHASH 53L +#define KEYW_RPCMETHOD_GETLEADERSCHEDULE 54L +#define KEYW_RPCMETHOD_GETMAXRETRANSMITSLOT 55L +#define KEYW_RPCMETHOD_GETMAXSHREDINSERTSLOT 56L +#define KEYW_RPCMETHOD_GETMINIMUMBALANCEFORRENTEXEMPTION 57L +#define KEYW_RPCMETHOD_GETMULTIPLEACCOUNTS 58L +#define KEYW_RPCMETHOD_GETPROGRAMACCOUNTS 59L +#define KEYW_RPCMETHOD_GETRECENTBLOCKHASH 60L +#define KEYW_RPCMETHOD_GETRECENTPERFORMANCESAMPLES 61L +#define KEYW_RPCMETHOD_GETRECENTPRIORITIZATIONFEES 62L +#define KEYW_RPCMETHOD_GETSIGNATURESFORADDRESS 63L +#define KEYW_RPCMETHOD_GETSIGNATURESTATUSES 64L +#define KEYW_RPCMETHOD_GETSLOT 65L +#define KEYW_RPCMETHOD_GETSLOTLEADER 66L +#define KEYW_RPCMETHOD_GETSLOTLEADERS 67L +#define KEYW_RPCMETHOD_GETSNAPSHOTSLOT 68L +#define KEYW_RPCMETHOD_GETSTAKEACTIVATION 69L +#define KEYW_RPCMETHOD_GETSTAKEMINIMUMDELEGATION 70L +#define KEYW_RPCMETHOD_GETSUPPLY 71L +#define KEYW_RPCMETHOD_GETTOKENACCOUNTBALANCE 72L +#define KEYW_RPCMETHOD_GETTOKENACCOUNTSBYDELEGATE 73L +#define KEYW_RPCMETHOD_GETTOKENACCOUNTSBYOWNER 74L +#define KEYW_RPCMETHOD_GETTOKENLARGESTACCOUNTS 75L +#define KEYW_RPCMETHOD_GETTOKENSUPPLY 76L +#define KEYW_RPCMETHOD_GETTRANSACTION 77L +#define KEYW_RPCMETHOD_GETTRANSACTIONCOUNT 78L +#define KEYW_RPCMETHOD_GETVERSION 79L +#define KEYW_RPCMETHOD_GETVOTEACCOUNTS 80L +#define KEYW_RPCMETHOD_ISBLOCKHASHVALID 81L +#define KEYW_RPCMETHOD_MINIMUMLEDGERSLOT 82L +#define KEYW_RPCMETHOD_REQUESTAIRDROP 83L +#define KEYW_RPCMETHOD_SENDTRANSACTION 84L +#define KEYW_RPCMETHOD_SIMULATETRANSACTION 85L +#define KEYW_WS_METHOD_ACCOUNTSUBSCRIBE 86L +#define KEYW_WS_METHOD_ACCOUNTUNSUBSCRIBE 87L +#define KEYW_WS_METHOD_BLOCKSUBSCRIBE 88L +#define KEYW_WS_METHOD_BLOCKUNSUBSCRIBE 89L +#define KEYW_WS_METHOD_LOGSSUBSCRIBE 90L +#define KEYW_WS_METHOD_LOGSUNSUBSCRIBE 91L +#define KEYW_WS_METHOD_PROGRAMSUBSCRIBE 92L +#define KEYW_WS_METHOD_PROGRAMUNSUBSCRIBE 93L +#define KEYW_WS_METHOD_ROOTSUBSCRIBE 94L +#define KEYW_WS_METHOD_ROOTUNSUBSCRIBE 95L +#define KEYW_WS_METHOD_SIGNATURESUBSCRIBE 96L +#define KEYW_WS_METHOD_SIGNATUREUNSUBSCRIBE 97L +#define KEYW_WS_METHOD_SLOTSUBSCRIBE 98L +#define KEYW_WS_METHOD_SLOTUNSUBSCRIBE 99L +#define KEYW_WS_METHOD_SLOTSUPDATESSUBSCRIBE 100L +#define KEYW_WS_METHOD_SLOTSUPDATESUNSUBSCRIBE 101L +#define KEYW_WS_METHOD_VOTESUBSCRIBE 102L +#define KEYW_WS_METHOD_VOTEUNSUBSCRIBE 103L +#ifndef KEYW_UNKNOWN +#define KEYW_UNKNOWN -1L +#endif +long fd_webserver_json_keyword(const char* keyw, unsigned long keyw_sz); +const char* un_fd_webserver_json_keyword(long id); diff --git a/src/app/rpcserver/main.c b/src/app/rpcserver/main.c new file mode 100644 index 0000000000..7df5e92337 --- /dev/null +++ b/src/app/rpcserver/main.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include "../../util/wksp/fd_wksp_private.h" +#include "../../disco/topo/fd_topo.h" +#include "fd_rpc_service.h" + +/* +static void usage( char const * progname ) { + fprintf( stderr, "fd_rpcserver usage: %s\n", progname ); + fprintf( stderr, " --wksp-name-funk funk workspace name\n" ); + fprintf( stderr, " --wksp-name-blockstore blockstore workspace name\n" ); + fprintf( stderr, " --wksp-name-replay-notify replay notification workspace name\n" ); + fprintf( stderr, " --num-threads number of http service threads\n" ); + fprintf( stderr, " --port http service port\n" ); +} +*/ + +static void +init_args( int * argc, char *** argv, fd_rpcserver_args_t * args ) { + char const * wksp_name = fd_env_strip_cmdline_cstr ( argc, argv, "--wksp-name-funk", NULL, "fd1_funk.wksp" ); + FD_LOG_NOTICE(( "attaching to workspace \"%s\"", wksp_name )); + fd_wksp_t * wksp = fd_wksp_attach( wksp_name ); + if( FD_UNLIKELY( !wksp ) ) + FD_LOG_ERR(( "unable to attach to \"%s\"\n\tprobably does not exist or bad permissions", wksp_name )); + fd_wksp_tag_query_info_t info; + ulong tag = FD_FUNK_MAGIC; + if( fd_wksp_tag_query( wksp, &tag, 1, &info, 1 ) <= 0 ) { + FD_LOG_ERR(( "workspace \"%s\" does not contain a funk", wksp_name )); + } + void * shmem = fd_wksp_laddr_fast( wksp, info.gaddr_lo ); + args->funk = fd_funk_join( shmem ); + if( args->funk == NULL ) { + FD_LOG_ERR(( "failed to join a funky" )); + } + fd_wksp_mprotect( wksp, 1 ); + + wksp_name = fd_env_strip_cmdline_cstr ( argc, argv, "--wksp-name-blockstore", NULL, "fd1_bstore.wksp" ); + FD_LOG_NOTICE(( "attaching to workspace \"%s\"", wksp_name )); + wksp = fd_wksp_attach( wksp_name ); + if( FD_UNLIKELY( !wksp ) ) + FD_LOG_ERR(( "unable to attach to \"%s\"\n\tprobably does not exist or bad permissions", wksp_name )); + tag = FD_BLOCKSTORE_MAGIC; + if( fd_wksp_tag_query( wksp, &tag, 1, &info, 1 ) <= 0 ) { + FD_LOG_ERR(( "workspace \"%s\" does not contain a blockstore", wksp_name )); + } + shmem = fd_wksp_laddr_fast( wksp, info.gaddr_lo ); + args->blockstore = fd_blockstore_join( shmem ); + if( args->blockstore == NULL ) { + FD_LOG_ERR(( "failed to join a blockstore" )); + } + FD_LOG_NOTICE(( "blockstore has slot min=%lu smr=%lu max=%lu", + args->blockstore->min, args->blockstore->smr, args->blockstore->max )); + fd_wksp_mprotect( wksp, 1 ); + + wksp_name = fd_env_strip_cmdline_cstr ( argc, argv, "--wksp-name-replay-notify", NULL, "fd1_replay_notif.wksp" ); + FD_LOG_NOTICE(( "attaching to workspace \"%s\"", wksp_name )); + args->rep_notify_wksp = wksp = fd_wksp_attach( wksp_name ); + if( FD_UNLIKELY( !wksp ) ) + FD_LOG_ERR(( "unable to attach to \"%s\"\n\tprobably does not exist or bad permissions", wksp_name )); + ulong offset = fd_ulong_align_up( fd_wksp_private_data_off( wksp->part_max ), fd_topo_workspace_align() ); + args->rep_notify = fd_mcache_join( (void *)((ulong)wksp + offset) ); + if( args->rep_notify == NULL ) { + FD_LOG_ERR(( "failed to join a replay notifier" )); + } + + args->num_threads = fd_env_strip_cmdline_ulong( argc, argv, "--num-threads", NULL, 10 ); + + args->port = (ushort)fd_env_strip_cmdline_ulong( argc, argv, "--port", NULL, 8899 ); + args->ws_port = (ushort)fd_env_strip_cmdline_ulong( argc, argv, "--ws-port", NULL, 8900 ); +} + +static int stopflag = 0; +static void +signal1( int sig ) { + (void)sig; + stopflag = 1; +} + +int main( int argc, char ** argv ) { + fd_boot( &argc, &argv ); + fd_rpcserver_args_t args; + init_args( &argc, &argv, &args ); + + struct sigaction sa = { + .sa_handler = signal1, + .sa_flags = 0, + }; + if( FD_UNLIKELY( sigaction( SIGTERM, &sa, NULL ) ) ) + FD_LOG_ERR(( "sigaction(SIGTERM) failed (%i-%s)", errno, fd_io_strerror( errno ) )); + if( FD_UNLIKELY( sigaction( SIGINT, &sa, NULL ) ) ) + FD_LOG_ERR(( "sigaction(SIGINT) failed (%i-%s)", errno, fd_io_strerror( errno ) )); + + fd_rpc_ctx_t * ctx = NULL; + fd_rpc_start_service( &args, &ctx ); + + fd_frag_meta_t * mcache = args.rep_notify; + fd_wksp_t * mcache_wksp = args.rep_notify_wksp; + ulong depth = fd_mcache_depth( mcache ); + ulong seq_expect = fd_mcache_seq0( mcache ); + while( !stopflag ) { + while( 1 ) { + fd_frag_meta_t const * mline = mcache + fd_mcache_line_idx( seq_expect, depth ); + + ulong seq_found = fd_frag_meta_seq_query( mline ); + long diff = fd_seq_diff( seq_found, seq_expect ); + if( FD_UNLIKELY( diff ) ) { /* caught up or overrun, optimize for expected sequence number ready */ + if( FD_UNLIKELY( diff>0L ) ) { + FD_LOG_NOTICE(( "overrun: seq=%lu seq_found=%lu diff=%ld", seq_expect, seq_found, diff )); + seq_expect = seq_found; + } else { + /* caught up */ + break; + } + continue; + } + + fd_replay_notif_msg_t msg; + FD_TEST( mline->sz == sizeof(msg) ); + fd_memcpy(&msg, fd_chunk_to_laddr( mcache_wksp, mline->chunk ), sizeof(msg)); + + seq_found = fd_frag_meta_seq_query( mline ); + diff = fd_seq_diff( seq_found, seq_expect ); + if( FD_UNLIKELY( diff ) ) { /* overrun, optimize for expected sequence number ready */ + FD_LOG_NOTICE(( "overrun: seq=%lu seq_found=%lu diff=%ld", seq_expect, seq_found, diff )); + seq_expect = seq_found; + continue; + } + + fd_rpc_replay_notify( ctx, &msg ); + + ++seq_expect; + } + + fd_rpc_ws_poll( ctx ); + } + + fd_rpc_stop_service( ctx ); + + fd_halt(); + return 0; +} diff --git a/src/app/rpcserver/test_keywords.c b/src/app/rpcserver/test_keywords.c new file mode 100644 index 0000000000..93f7fc2877 --- /dev/null +++ b/src/app/rpcserver/test_keywords.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include "keywords.h" +#include "test_keywords.h" + +int main(int argc, char** argv) { + (void)argc; + (void)argv; + test_fd_webserver_json_keyword(); + printf("test passed!\n"); + return 0; +} diff --git a/src/app/rpcserver/test_keywords.h b/src/app/rpcserver/test_keywords.h new file mode 100644 index 0000000000..3bd49624f0 --- /dev/null +++ b/src/app/rpcserver/test_keywords.h @@ -0,0 +1,1919 @@ +void test_fd_webserver_json_keyword(void) { + assert(fd_webserver_json_keyword("jsonrpc\0\0\0\0\0\0\0", 7) == KEYW_JSON_JSONRPC); + assert(fd_webserver_json_keyword("jsonrpcx\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("jsonrp\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|sonrpc\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("j|onrpc\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("js|nrpc\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("jso|rpc\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("json|pc\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("jsonr|c\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("jsonrp|\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("id\0\0\0\0\0\0\0", 2) == KEYW_JSON_ID); + assert(fd_webserver_json_keyword("idx\0\0\0\0\0\0\0", 3) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("i\0\0\0\0\0\0\0", 1) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|d\0\0\0\0\0\0\0", 2) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("i|\0\0\0\0\0\0\0", 2) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("method\0\0\0\0\0\0\0", 6) == KEYW_JSON_METHOD); + assert(fd_webserver_json_keyword("methodx\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("metho\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ethod\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("m|thod\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("me|hod\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("met|od\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("meth|d\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("metho|\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("params\0\0\0\0\0\0\0", 6) == KEYW_JSON_PARAMS); + assert(fd_webserver_json_keyword("paramsx\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("param\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|arams\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("p|rams\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("pa|ams\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("par|ms\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("para|s\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("param|\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("bytes\0\0\0\0\0\0\0", 5) == KEYW_JSON_BYTES); + assert(fd_webserver_json_keyword("bytesx\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("byte\0\0\0\0\0\0\0", 4) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ytes\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("b|tes\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("by|es\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("byt|s\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("byte|\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("commitment\0\0\0\0\0\0\0", 10) == KEYW_JSON_COMMITMENT); + assert(fd_webserver_json_keyword("commitmentx\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("commitmen\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ommitment\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("c|mmitment\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("co|mitment\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("com|itment\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("comm|tment\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("commi|ment\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("commit|ent\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("commitm|nt\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("commitme|t\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("commitmen|\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataSize\0\0\0\0\0\0\0", 8) == KEYW_JSON_DATASIZE); + assert(fd_webserver_json_keyword("dataSizex\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataSiz\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ataSize\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("d|taSize\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("da|aSize\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dat|Size\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("data|ize\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataS|ze\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataSi|e\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataSiz|\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataSlice\0\0\0\0\0\0\0", 9) == KEYW_JSON_DATASLICE); + assert(fd_webserver_json_keyword("dataSlicex\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataSlic\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ataSlice\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("d|taSlice\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("da|aSlice\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dat|Slice\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("data|lice\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataS|ice\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataSl|ce\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataSli|e\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("dataSlic|\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("encoding\0\0\0\0\0\0\0", 8) == KEYW_JSON_ENCODING); + assert(fd_webserver_json_keyword("encodingx\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("encodin\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ncoding\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("e|coding\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("en|oding\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("enc|ding\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("enco|ing\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("encod|ng\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("encodi|g\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("encodin|\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("epoch\0\0\0\0\0\0\0", 5) == KEYW_JSON_EPOCH); + assert(fd_webserver_json_keyword("epochx\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("epoc\0\0\0\0\0\0\0", 4) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|poch\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("e|och\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ep|ch\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("epo|h\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("epoc|\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("filters\0\0\0\0\0\0\0", 7) == KEYW_JSON_FILTERS); + assert(fd_webserver_json_keyword("filtersx\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("filter\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ilters\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("f|lters\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("fi|ters\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("fil|ers\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("filt|rs\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("filte|s\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("filter|\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("identity\0\0\0\0\0\0\0", 8) == KEYW_JSON_IDENTITY); + assert(fd_webserver_json_keyword("identityx\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("identit\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|dentity\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("i|entity\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("id|ntity\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ide|tity\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("iden|ity\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ident|ty\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("identi|y\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("identit|\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("length\0\0\0\0\0\0\0", 6) == KEYW_JSON_LENGTH); + assert(fd_webserver_json_keyword("lengthx\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("lengt\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ength\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("l|ngth\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("le|gth\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("len|th\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("leng|h\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("lengt|\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("limit\0\0\0\0\0\0\0", 5) == KEYW_JSON_LIMIT); + assert(fd_webserver_json_keyword("limitx\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("limi\0\0\0\0\0\0\0", 4) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|imit\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("l|mit\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("li|it\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("lim|t\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("limi|\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_JSON_MAXSUPPORTEDTRANSACTIONVERSION); + assert(fd_webserver_json_keyword("maxSupportedTransactionVersionx\0\0\0\0\0\0\0", 31) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransactionVersio\0\0\0\0\0\0\0", 29) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|axSupportedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("m|xSupportedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ma|SupportedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("max|upportedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxS|pportedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSu|portedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSup|ortedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupp|rtedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSuppo|tedTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSuppor|edTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupport|dTransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupporte|TransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupported|ransactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedT|ansactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTr|nsactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTra|sactionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTran|actionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTrans|ctionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransa|tionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransac|ionVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransact|onVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransacti|nVersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransactio|Version\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransaction|ersion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransactionV|rsion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransactionVe|sion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransactionVer|ion\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransactionVers|on\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransactionVersi|n\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("maxSupportedTransactionVersio|\0\0\0\0\0\0\0", 30) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("memcmp\0\0\0\0\0\0\0", 6) == KEYW_JSON_MEMCMP); + assert(fd_webserver_json_keyword("memcmpx\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("memcm\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|emcmp\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("m|mcmp\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("me|cmp\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("mem|mp\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("memc|p\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("memcm|\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("mint\0\0\0\0\0\0\0", 4) == KEYW_JSON_MINT); + assert(fd_webserver_json_keyword("mintx\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("min\0\0\0\0\0\0\0", 3) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|int\0\0\0\0\0\0\0", 4) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("m|nt\0\0\0\0\0\0\0", 4) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("mi|t\0\0\0\0\0\0\0", 4) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("min|\0\0\0\0\0\0\0", 4) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("offset\0\0\0\0\0\0\0", 6) == KEYW_JSON_OFFSET); + assert(fd_webserver_json_keyword("offsetx\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("offse\0\0\0\0\0\0\0", 5) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ffset\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("o|fset\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("of|set\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("off|et\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("offs|t\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("offse|\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programId\0\0\0\0\0\0\0", 9) == KEYW_JSON_PROGRAMID); + assert(fd_webserver_json_keyword("programIdx\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programI\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|rogramId\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("p|ogramId\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("pr|gramId\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("pro|ramId\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("prog|amId\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("progr|mId\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("progra|Id\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("program|d\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programI|\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rewards\0\0\0\0\0\0\0", 7) == KEYW_JSON_REWARDS); + assert(fd_webserver_json_keyword("rewardsx\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("reward\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ewards\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("r|wards\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("re|ards\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rew|rds\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rewa|ds\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rewar|s\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("reward|\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransactionHistory\0\0\0\0\0\0\0", 24) == KEYW_JSON_SEARCHTRANSACTIONHISTORY); + assert(fd_webserver_json_keyword("searchTransactionHistoryx\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransactionHistor\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|earchTransactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("s|archTransactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("se|rchTransactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sea|chTransactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sear|hTransactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searc|TransactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("search|ransactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchT|ansactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTr|nsactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTra|sactionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTran|actionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTrans|ctionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransa|tionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransac|ionHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransact|onHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransacti|nHistory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransactio|History\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransaction|istory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransactionH|story\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransactionHi|tory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransactionHis|ory\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransactionHist|ry\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransactionHisto|y\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("searchTransactionHistor|\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transactionDetails\0\0\0\0\0\0\0", 18) == KEYW_JSON_TRANSACTIONDETAILS); + assert(fd_webserver_json_keyword("transactionDetailsx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transactionDetail\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ransactionDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("t|ansactionDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("tr|nsactionDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("tra|sactionDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("tran|actionDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("trans|ctionDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transa|tionDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transac|ionDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transact|onDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transacti|nDetails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transactio|Details\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transaction|etails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transactionD|tails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transactionDe|ails\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transactionDet|ils\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transactionDeta|ls\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transactionDetai|s\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("transactionDetail|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("votePubkey\0\0\0\0\0\0\0", 10) == KEYW_JSON_VOTEPUBKEY); + assert(fd_webserver_json_keyword("votePubkeyx\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("votePubke\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|otePubkey\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("v|tePubkey\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("vo|ePubkey\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("vot|Pubkey\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("vote|ubkey\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteP|bkey\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("votePu|key\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("votePub|ey\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("votePubk|y\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("votePubke|\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAccountInfo\0\0\0\0\0\0\0", 14) == KEYW_RPCMETHOD_GETACCOUNTINFO); + assert(fd_webserver_json_keyword("getAccountInfox\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAccountInf\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etAccountInfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tAccountInfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|AccountInfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ccountInfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getA|countInfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAc|ountInfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAcc|untInfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAcco|ntInfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAccou|tInfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAccoun|Info\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAccount|nfo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAccountI|fo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAccountIn|o\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getAccountInf|\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBalance\0\0\0\0\0\0\0", 10) == KEYW_RPCMETHOD_GETBALANCE); + assert(fd_webserver_json_keyword("getBalancex\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBalanc\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etBalance\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tBalance\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Balance\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|alance\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getB|lance\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBa|ance\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBal|nce\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBala|ce\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBalan|e\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBalanc|\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlock\0\0\0\0\0\0\0", 8) == KEYW_RPCMETHOD_GETBLOCK); + assert(fd_webserver_json_keyword("getBlockx\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBloc\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etBlock\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tBlock\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Block\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|lock\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getB|ock\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBl|ck\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlo|k\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBloc|\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockCommitment\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETBLOCKCOMMITMENT); + assert(fd_webserver_json_keyword("getBlockCommitmentx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockCommitmen\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etBlockCommitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tBlockCommitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|BlockCommitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|lockCommitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getB|ockCommitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBl|ckCommitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlo|kCommitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBloc|Commitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlock|ommitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockC|mmitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockCo|mitment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockCom|itment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockComm|tment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockCommi|ment\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockCommit|ent\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockCommitm|nt\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockCommitme|t\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockCommitmen|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockHeight\0\0\0\0\0\0\0", 14) == KEYW_RPCMETHOD_GETBLOCKHEIGHT); + assert(fd_webserver_json_keyword("getBlockHeightx\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockHeigh\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etBlockHeight\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tBlockHeight\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|BlockHeight\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|lockHeight\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getB|ockHeight\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBl|ckHeight\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlo|kHeight\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBloc|Height\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlock|eight\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockH|ight\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockHe|ght\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockHei|ht\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockHeig|t\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockHeigh|\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockProduction\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETBLOCKPRODUCTION); + assert(fd_webserver_json_keyword("getBlockProductionx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockProductio\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etBlockProduction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tBlockProduction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|BlockProduction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|lockProduction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getB|ockProduction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBl|ckProduction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlo|kProduction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBloc|Production\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlock|roduction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockP|oduction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockPr|duction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockPro|uction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockProd|ction\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockProdu|tion\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockProduc|ion\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockProduct|on\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockProducti|n\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockProductio|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocks\0\0\0\0\0\0\0", 9) == KEYW_RPCMETHOD_GETBLOCKS); + assert(fd_webserver_json_keyword("getBlocksx\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etBlocks\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tBlocks\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Blocks\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|locks\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getB|ocks\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBl|cks\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlo|ks\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBloc|s\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlock|\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksWithLimit\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETBLOCKSWITHLIMIT); + assert(fd_webserver_json_keyword("getBlocksWithLimitx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksWithLimi\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etBlocksWithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tBlocksWithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|BlocksWithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|locksWithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getB|ocksWithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBl|cksWithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlo|ksWithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBloc|sWithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlock|WithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocks|ithLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksW|thLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksWi|hLimit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksWit|Limit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksWith|imit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksWithL|mit\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksWithLi|it\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksWithLim|t\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlocksWithLimi|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockTime\0\0\0\0\0\0\0", 12) == KEYW_RPCMETHOD_GETBLOCKTIME); + assert(fd_webserver_json_keyword("getBlockTimex\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockTim\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etBlockTime\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tBlockTime\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|BlockTime\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|lockTime\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getB|ockTime\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBl|ckTime\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlo|kTime\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBloc|Time\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlock|ime\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockT|me\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockTi|e\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getBlockTim|\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getClusterNodes\0\0\0\0\0\0\0", 15) == KEYW_RPCMETHOD_GETCLUSTERNODES); + assert(fd_webserver_json_keyword("getClusterNodesx\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getClusterNode\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etClusterNodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tClusterNodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|ClusterNodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|lusterNodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getC|usterNodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCl|sterNodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getClu|terNodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getClus|erNodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getClust|rNodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCluste|Nodes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCluster|odes\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getClusterN|des\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getClusterNo|es\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getClusterNod|s\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getClusterNode|\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlock\0\0\0\0\0\0\0", 17) == KEYW_RPCMETHOD_GETCONFIRMEDBLOCK); + assert(fd_webserver_json_keyword("getConfirmedBlockx\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBloc\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etConfirmedBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tConfirmedBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|ConfirmedBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|onfirmedBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getC|nfirmedBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCo|firmedBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCon|irmedBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConf|rmedBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfi|medBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfir|edBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirm|dBlock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirme|Block\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmed|lock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedB|ock\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBl|ck\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlo|k\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBloc|\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocks\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETCONFIRMEDBLOCKS); + assert(fd_webserver_json_keyword("getConfirmedBlocksx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etConfirmedBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tConfirmedBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|ConfirmedBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|onfirmedBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getC|nfirmedBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCo|firmedBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCon|irmedBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConf|rmedBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfi|medBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfir|edBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirm|dBlocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirme|Blocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmed|locks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedB|ocks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBl|cks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlo|ks\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBloc|s\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlock|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_RPCMETHOD_GETCONFIRMEDBLOCKSWITHLIMIT); + assert(fd_webserver_json_keyword("getConfirmedBlocksWithLimitx\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksWithLimi\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etConfirmedBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tConfirmedBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|ConfirmedBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|onfirmedBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getC|nfirmedBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCo|firmedBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCon|irmedBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConf|rmedBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfi|medBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfir|edBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirm|dBlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirme|BlocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmed|locksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedB|ocksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBl|cksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlo|ksWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBloc|sWithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlock|WithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocks|ithLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksW|thLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksWi|hLimit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksWit|Limit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksWith|imit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksWithL|mit\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksWithLi|it\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksWithLim|t\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedBlocksWithLimi|\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_RPCMETHOD_GETCONFIRMEDSIGNATURESFORADDRESS2); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForAddress2x\0\0\0\0\0\0\0", 34) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForAddress\0\0\0\0\0\0\0", 32) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etConfirmedSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tConfirmedSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|ConfirmedSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|onfirmedSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getC|nfirmedSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCo|firmedSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCon|irmedSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConf|rmedSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfi|medSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfir|edSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirm|dSignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirme|SignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmed|ignaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedS|gnaturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSi|naturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSig|aturesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSign|turesForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSigna|uresForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignat|resForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignatu|esForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignatur|sForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignature|ForAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignatures|orAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesF|rAddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesFo|Address2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesFor|ddress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForA|dress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForAd|ress2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForAdd|ess2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForAddr|ss2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForAddre|s2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForAddres|2\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedSignaturesForAddress|\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTransaction\0\0\0\0\0\0\0", 23) == KEYW_RPCMETHOD_GETCONFIRMEDTRANSACTION); + assert(fd_webserver_json_keyword("getConfirmedTransactionx\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTransactio\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etConfirmedTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tConfirmedTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|ConfirmedTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|onfirmedTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getC|nfirmedTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCo|firmedTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getCon|irmedTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConf|rmedTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfi|medTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfir|edTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirm|dTransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirme|Transaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmed|ransaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedT|ansaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTr|nsaction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTra|saction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTran|action\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTrans|ction\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTransa|tion\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTransac|ion\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTransact|on\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTransacti|n\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getConfirmedTransactio|\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochInfo\0\0\0\0\0\0\0", 12) == KEYW_RPCMETHOD_GETEPOCHINFO); + assert(fd_webserver_json_keyword("getEpochInfox\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochInf\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etEpochInfo\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tEpochInfo\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|EpochInfo\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|pochInfo\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getE|ochInfo\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEp|chInfo\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpo|hInfo\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpoc|Info\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpoch|nfo\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochI|fo\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochIn|o\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochInf|\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochSchedule\0\0\0\0\0\0\0", 16) == KEYW_RPCMETHOD_GETEPOCHSCHEDULE); + assert(fd_webserver_json_keyword("getEpochSchedulex\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochSchedul\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etEpochSchedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tEpochSchedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|EpochSchedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|pochSchedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getE|ochSchedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEp|chSchedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpo|hSchedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpoc|Schedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpoch|chedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochS|hedule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochSc|edule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochSch|dule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochSche|ule\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochSched|le\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochSchedu|e\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getEpochSchedul|\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_RPCMETHOD_GETFEECALCULATORFORBLOCKHASH); + assert(fd_webserver_json_keyword("getFeeCalculatorForBlockhashx\0\0\0\0\0\0\0", 29) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForBlockhas\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etFeeCalculatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tFeeCalculatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|FeeCalculatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|eeCalculatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getF|eCalculatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFe|CalculatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFee|alculatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeC|lculatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCa|culatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCal|ulatorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalc|latorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalcu|atorForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalcul|torForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalcula|orForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculat|rForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculato|ForBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculator|orBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorF|rBlockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorFo|Blockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorFor|lockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForB|ockhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForBl|ckhash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForBlo|khash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForBloc|hash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForBlock|ash\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForBlockh|sh\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForBlockha|h\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeCalculatorForBlockhas|\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeForMessage\0\0\0\0\0\0\0", 16) == KEYW_RPCMETHOD_GETFEEFORMESSAGE); + assert(fd_webserver_json_keyword("getFeeForMessagex\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeForMessag\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etFeeForMessage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tFeeForMessage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|FeeForMessage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|eeForMessage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getF|eForMessage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFe|ForMessage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFee|orMessage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeF|rMessage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeFo|Message\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeFor|essage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeForM|ssage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeForMe|sage\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeForMes|age\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeForMess|ge\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeForMessa|e\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeForMessag|\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRateGovernor\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETFEERATEGOVERNOR); + assert(fd_webserver_json_keyword("getFeeRateGovernorx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRateGoverno\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etFeeRateGovernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tFeeRateGovernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|FeeRateGovernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|eeRateGovernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getF|eRateGovernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFe|RateGovernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFee|ateGovernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeR|teGovernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRa|eGovernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRat|Governor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRate|overnor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRateG|vernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRateGo|ernor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRateGov|rnor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRateGove|nor\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRateGover|or\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRateGovern|r\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFeeRateGoverno|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFees\0\0\0\0\0\0\0", 7) == KEYW_RPCMETHOD_GETFEES); + assert(fd_webserver_json_keyword("getFeesx\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFee\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etFees\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tFees\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Fees\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ees\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getF|es\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFe|s\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFee|\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvailableBlock\0\0\0\0\0\0\0", 22) == KEYW_RPCMETHOD_GETFIRSTAVAILABLEBLOCK); + assert(fd_webserver_json_keyword("getFirstAvailableBlockx\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvailableBloc\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etFirstAvailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tFirstAvailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|FirstAvailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|irstAvailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getF|rstAvailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFi|stAvailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFir|tAvailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirs|AvailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirst|vailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstA|ailableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAv|ilableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAva|lableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvai|ableBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvail|bleBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvaila|leBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvailab|eBlock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvailabl|Block\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvailable|lock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvailableB|ock\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvailableBl|ck\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvailableBlo|k\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getFirstAvailableBloc|\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGenesisHash\0\0\0\0\0\0\0", 14) == KEYW_RPCMETHOD_GETGENESISHASH); + assert(fd_webserver_json_keyword("getGenesisHashx\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGenesisHas\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etGenesisHash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tGenesisHash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|GenesisHash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|enesisHash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getG|nesisHash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGe|esisHash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGen|sisHash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGene|isHash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGenes|sHash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGenesi|Hash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGenesis|ash\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGenesisH|sh\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGenesisHa|h\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getGenesisHas|\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHealth\0\0\0\0\0\0\0", 9) == KEYW_RPCMETHOD_GETHEALTH); + assert(fd_webserver_json_keyword("getHealthx\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHealt\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etHealth\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tHealth\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Health\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ealth\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getH|alth\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHe|lth\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHea|th\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHeal|h\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHealt|\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_RPCMETHOD_GETHIGHESTSNAPSHOTSLOT); + assert(fd_webserver_json_keyword("getHighestSnapshotSlotx\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnapshotSlo\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etHighestSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tHighestSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|HighestSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ighestSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getH|ghestSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHi|hestSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHig|estSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHigh|stSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighe|tSnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighes|SnapshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighest|napshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestS|apshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSn|pshotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSna|shotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnap|hotSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnaps|otSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnapsh|tSlot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnapsho|Slot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnapshot|lot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnapshotS|ot\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnapshotSl|t\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getHighestSnapshotSlo|\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIdentity\0\0\0\0\0\0\0", 11) == KEYW_RPCMETHOD_GETIDENTITY); + assert(fd_webserver_json_keyword("getIdentityx\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIdentit\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etIdentity\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tIdentity\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Identity\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|dentity\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getI|entity\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getId|ntity\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIde|tity\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIden|ity\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIdent|ty\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIdenti|y\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIdentit|\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationGovernor\0\0\0\0\0\0\0", 20) == KEYW_RPCMETHOD_GETINFLATIONGOVERNOR); + assert(fd_webserver_json_keyword("getInflationGovernorx\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationGoverno\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etInflationGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tInflationGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|InflationGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|nflationGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getI|flationGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIn|lationGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInf|ationGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInfl|tionGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInfla|ionGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflat|onGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflati|nGovernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflatio|Governor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflation|overnor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationG|vernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationGo|ernor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationGov|rnor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationGove|nor\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationGover|or\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationGovern|r\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationGoverno|\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationRate\0\0\0\0\0\0\0", 16) == KEYW_RPCMETHOD_GETINFLATIONRATE); + assert(fd_webserver_json_keyword("getInflationRatex\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationRat\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etInflationRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tInflationRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|InflationRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|nflationRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getI|flationRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIn|lationRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInf|ationRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInfl|tionRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInfla|ionRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflat|onRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflati|nRate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflatio|Rate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflation|ate\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationR|te\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationRa|e\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationRat|\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationReward\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETINFLATIONREWARD); + assert(fd_webserver_json_keyword("getInflationRewardx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationRewar\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etInflationReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tInflationReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|InflationReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|nflationReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getI|flationReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getIn|lationReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInf|ationReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInfl|tionReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInfla|ionReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflat|onReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflati|nReward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflatio|Reward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflation|eward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationR|ward\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationRe|ard\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationRew|rd\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationRewa|d\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getInflationRewar|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargestAccounts\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETLARGESTACCOUNTS); + assert(fd_webserver_json_keyword("getLargestAccountsx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargestAccount\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etLargestAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tLargestAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|LargestAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|argestAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getL|rgestAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLa|gestAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLar|estAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLarg|stAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLarge|tAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLarges|Accounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargest|ccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargestA|counts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargestAc|ounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargestAcc|unts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargestAcco|nts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargestAccou|ts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargestAccoun|s\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLargestAccount|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestBlockhash\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETLATESTBLOCKHASH); + assert(fd_webserver_json_keyword("getLatestBlockhashx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestBlockhas\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etLatestBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tLatestBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|LatestBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|atestBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getL|testBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLa|estBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLat|stBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLate|tBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLates|Blockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatest|lockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestB|ockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestBl|ckhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestBlo|khash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestBloc|hash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestBlock|ash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestBlockh|sh\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestBlockha|h\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLatestBlockhas|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeaderSchedule\0\0\0\0\0\0\0", 17) == KEYW_RPCMETHOD_GETLEADERSCHEDULE); + assert(fd_webserver_json_keyword("getLeaderSchedulex\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeaderSchedul\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etLeaderSchedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tLeaderSchedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|LeaderSchedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|eaderSchedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getL|aderSchedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLe|derSchedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLea|erSchedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLead|rSchedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeade|Schedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeader|chedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeaderS|hedule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeaderSc|edule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeaderSch|dule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeaderSche|ule\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeaderSched|le\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeaderSchedu|e\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getLeaderSchedul|\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetransmitSlot\0\0\0\0\0\0\0", 20) == KEYW_RPCMETHOD_GETMAXRETRANSMITSLOT); + assert(fd_webserver_json_keyword("getMaxRetransmitSlotx\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetransmitSlo\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etMaxRetransmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tMaxRetransmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|MaxRetransmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|axRetransmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getM|xRetransmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMa|RetransmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMax|etransmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxR|transmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRe|ransmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRet|ansmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetr|nsmitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetra|smitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetran|mitSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetrans|itSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetransm|tSlot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetransmi|Slot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetransmit|lot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetransmitS|ot\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetransmitSl|t\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxRetransmitSlo|\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_RPCMETHOD_GETMAXSHREDINSERTSLOT); + assert(fd_webserver_json_keyword("getMaxShredInsertSlotx\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredInsertSlo\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etMaxShredInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tMaxShredInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|MaxShredInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|axShredInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getM|xShredInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMa|ShredInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMax|hredInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxS|redInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxSh|edInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShr|dInsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShre|InsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShred|nsertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredI|sertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredIn|ertSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredIns|rtSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredInse|tSlot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredInser|Slot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredInsert|lot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredInsertS|ot\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredInsertSl|t\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMaxShredInsertSlo|\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_RPCMETHOD_GETMINIMUMBALANCEFORRENTEXEMPTION); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentExemptionx\0\0\0\0\0\0\0", 34) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentExemptio\0\0\0\0\0\0\0", 32) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etMinimumBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tMinimumBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|MinimumBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|inimumBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getM|nimumBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMi|imumBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMin|mumBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMini|umBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinim|mBalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimu|BalanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimum|alanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumB|lanceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBa|anceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBal|nceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBala|ceForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalan|eForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanc|ForRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalance|orRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceF|rRentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceFo|RentExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceFor|entExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForR|ntExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRe|tExemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRen|Exemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRent|xemption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentE|emption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentEx|mption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentExe|ption\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentExem|tion\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentExemp|ion\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentExempt|on\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentExempti|n\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMinimumBalanceForRentExemptio|\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipleAccounts\0\0\0\0\0\0\0", 19) == KEYW_RPCMETHOD_GETMULTIPLEACCOUNTS); + assert(fd_webserver_json_keyword("getMultipleAccountsx\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipleAccount\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etMultipleAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tMultipleAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|MultipleAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ultipleAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getM|ltipleAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMu|tipleAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMul|ipleAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMult|pleAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMulti|leAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultip|eAccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipl|Accounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultiple|ccounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipleA|counts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipleAc|ounts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipleAcc|unts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipleAcco|nts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipleAccou|ts\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipleAccoun|s\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getMultipleAccount|\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgramAccounts\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETPROGRAMACCOUNTS); + assert(fd_webserver_json_keyword("getProgramAccountsx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgramAccount\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etProgramAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tProgramAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|ProgramAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|rogramAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getP|ogramAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getPr|gramAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getPro|ramAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProg|amAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgr|mAccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgra|Accounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgram|ccounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgramA|counts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgramAc|ounts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgramAcc|unts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgramAcco|nts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgramAccou|ts\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgramAccoun|s\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getProgramAccount|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentBlockhash\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETRECENTBLOCKHASH); + assert(fd_webserver_json_keyword("getRecentBlockhashx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentBlockhas\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etRecentBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tRecentBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|RecentBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ecentBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getR|centBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRe|entBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRec|ntBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRece|tBlockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecen|Blockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecent|lockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentB|ockhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentBl|ckhash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentBlo|khash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentBloc|hash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentBlock|ash\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentBlockh|sh\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentBlockha|h\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentBlockhas|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_RPCMETHOD_GETRECENTPERFORMANCESAMPLES); + assert(fd_webserver_json_keyword("getRecentPerformanceSamplesx\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformanceSample\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etRecentPerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tRecentPerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|RecentPerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ecentPerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getR|centPerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRe|entPerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRec|ntPerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRece|tPerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecen|PerformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecent|erformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentP|rformanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPe|formanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPer|ormanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerf|rmanceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerfo|manceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerfor|anceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerform|nceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerforma|ceSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerforman|eSamples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformanc|Samples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformance|amples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformanceS|mples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformanceSa|ples\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformanceSam|les\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformanceSamp|es\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformanceSampl|s\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPerformanceSample|\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_RPCMETHOD_GETRECENTPRIORITIZATIONFEES); + assert(fd_webserver_json_keyword("getRecentPrioritizationFeesx\0\0\0\0\0\0\0", 28) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritizationFee\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etRecentPrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tRecentPrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|RecentPrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ecentPrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getR|centPrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRe|entPrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRec|ntPrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRece|tPrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecen|PrioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecent|rioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentP|ioritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPr|oritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPri|ritizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrio|itizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrior|tizationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPriori|izationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPriorit|zationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioriti|ationFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritiz|tionFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritiza|ionFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritizat|onFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritizati|nFees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritizatio|Fees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritization|ees\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritizationF|es\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritizationFe|s\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getRecentPrioritizationFee|\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesForAddress\0\0\0\0\0\0\0", 23) == KEYW_RPCMETHOD_GETSIGNATURESFORADDRESS); + assert(fd_webserver_json_keyword("getSignaturesForAddressx\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesForAddres\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etSignaturesForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tSignaturesForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|SignaturesForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ignaturesForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getS|gnaturesForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSi|naturesForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSig|aturesForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSign|turesForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSigna|uresForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignat|resForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatu|esForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatur|sForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignature|ForAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatures|orAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesF|rAddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesFo|Address\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesFor|ddress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesForA|dress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesForAd|ress\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesForAdd|ess\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesForAddr|ss\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesForAddre|s\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignaturesForAddres|\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatureStatuses\0\0\0\0\0\0\0", 20) == KEYW_RPCMETHOD_GETSIGNATURESTATUSES); + assert(fd_webserver_json_keyword("getSignatureStatusesx\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatureStatuse\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etSignatureStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tSignatureStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|SignatureStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ignatureStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getS|gnatureStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSi|natureStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSig|atureStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSign|tureStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSigna|ureStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignat|reStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatu|eStatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatur|Statuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignature|tatuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatureS|atuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatureSt|tuses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatureSta|uses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatureStat|ses\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatureStatu|es\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatureStatus|s\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSignatureStatuse|\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlot\0\0\0\0\0\0\0", 7) == KEYW_RPCMETHOD_GETSLOT); + assert(fd_webserver_json_keyword("getSlotx\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlo\0\0\0\0\0\0\0", 6) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etSlot\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tSlot\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Slot\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|lot\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getS|ot\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSl|t\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlo|\0\0\0\0\0\0\0", 7) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLeader\0\0\0\0\0\0\0", 13) == KEYW_RPCMETHOD_GETSLOTLEADER); + assert(fd_webserver_json_keyword("getSlotLeaderx\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLeade\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etSlotLeader\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tSlotLeader\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|SlotLeader\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|lotLeader\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getS|otLeader\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSl|tLeader\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlo|Leader\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlot|eader\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotL|ader\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLe|der\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLea|er\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLead|r\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLeade|\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLeaders\0\0\0\0\0\0\0", 14) == KEYW_RPCMETHOD_GETSLOTLEADERS); + assert(fd_webserver_json_keyword("getSlotLeadersx\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etSlotLeaders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tSlotLeaders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|SlotLeaders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|lotLeaders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getS|otLeaders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSl|tLeaders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlo|Leaders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlot|eaders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotL|aders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLe|ders\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLea|ers\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLead|rs\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLeade|s\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSlotLeader|\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnapshotSlot\0\0\0\0\0\0\0", 15) == KEYW_RPCMETHOD_GETSNAPSHOTSLOT); + assert(fd_webserver_json_keyword("getSnapshotSlotx\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnapshotSlo\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etSnapshotSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tSnapshotSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|SnapshotSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|napshotSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getS|apshotSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSn|pshotSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSna|shotSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnap|hotSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnaps|otSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnapsh|tSlot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnapsho|Slot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnapshot|lot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnapshotS|ot\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnapshotSl|t\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSnapshotSlo|\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeActivation\0\0\0\0\0\0\0", 18) == KEYW_RPCMETHOD_GETSTAKEACTIVATION); + assert(fd_webserver_json_keyword("getStakeActivationx\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeActivatio\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etStakeActivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tStakeActivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|StakeActivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|takeActivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getS|akeActivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSt|keActivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSta|eActivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStak|Activation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStake|ctivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeA|tivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeAc|ivation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeAct|vation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeActi|ation\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeActiv|tion\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeActiva|ion\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeActivat|on\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeActivati|n\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeActivatio|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_RPCMETHOD_GETSTAKEMINIMUMDELEGATION); + assert(fd_webserver_json_keyword("getStakeMinimumDelegationx\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDelegatio\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etStakeMinimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tStakeMinimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|StakeMinimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|takeMinimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getS|akeMinimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSt|keMinimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSta|eMinimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStak|MinimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStake|inimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeM|nimumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMi|imumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMin|mumDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMini|umDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinim|mDelegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimu|Delegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimum|elegation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumD|legation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDe|egation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDel|gation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDele|ation\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDeleg|tion\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDelega|ion\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDelegat|on\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDelegati|n\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getStakeMinimumDelegatio|\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSupply\0\0\0\0\0\0\0", 9) == KEYW_RPCMETHOD_GETSUPPLY); + assert(fd_webserver_json_keyword("getSupplyx\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSuppl\0\0\0\0\0\0\0", 8) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etSupply\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tSupply\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Supply\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|upply\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getS|pply\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSu|ply\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSup|ly\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSupp|y\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getSuppl|\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountBalance\0\0\0\0\0\0\0", 22) == KEYW_RPCMETHOD_GETTOKENACCOUNTBALANCE); + assert(fd_webserver_json_keyword("getTokenAccountBalancex\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountBalanc\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etTokenAccountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tTokenAccountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|TokenAccountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|okenAccountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getT|kenAccountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTo|enAccountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTok|nAccountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToke|AccountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToken|ccountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenA|countBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAc|ountBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAcc|untBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAcco|ntBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccou|tBalance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccoun|Balance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccount|alance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountB|lance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountBa|ance\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountBal|nce\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountBala|ce\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountBalan|e\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountBalanc|\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_RPCMETHOD_GETTOKENACCOUNTSBYDELEGATE); + assert(fd_webserver_json_keyword("getTokenAccountsByDelegatex\0\0\0\0\0\0\0", 27) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByDelegat\0\0\0\0\0\0\0", 25) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etTokenAccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tTokenAccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|TokenAccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|okenAccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getT|kenAccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTo|enAccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTok|nAccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToke|AccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToken|ccountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenA|countsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAc|ountsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAcc|untsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAcco|ntsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccou|tsByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccoun|sByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccount|ByDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccounts|yDelegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsB|Delegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsBy|elegate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByD|legate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByDe|egate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByDel|gate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByDele|ate\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByDeleg|te\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByDelega|e\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByDelegat|\0\0\0\0\0\0\0", 26) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_RPCMETHOD_GETTOKENACCOUNTSBYOWNER); + assert(fd_webserver_json_keyword("getTokenAccountsByOwnerx\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByOwne\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etTokenAccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tTokenAccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|TokenAccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|okenAccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getT|kenAccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTo|enAccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTok|nAccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToke|AccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToken|ccountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenA|countsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAc|ountsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAcc|untsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAcco|ntsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccou|tsByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccoun|sByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccount|ByOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccounts|yOwner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsB|Owner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsBy|wner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByO|ner\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByOw|er\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByOwn|r\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenAccountsByOwne|\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargestAccounts\0\0\0\0\0\0\0", 23) == KEYW_RPCMETHOD_GETTOKENLARGESTACCOUNTS); + assert(fd_webserver_json_keyword("getTokenLargestAccountsx\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargestAccount\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etTokenLargestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tTokenLargestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|TokenLargestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|okenLargestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getT|kenLargestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTo|enLargestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTok|nLargestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToke|LargestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToken|argestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenL|rgestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLa|gestAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLar|estAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLarg|stAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLarge|tAccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLarges|Accounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargest|ccounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargestA|counts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargestAc|ounts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargestAcc|unts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargestAcco|nts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargestAccou|ts\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargestAccoun|s\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenLargestAccount|\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenSupply\0\0\0\0\0\0\0", 14) == KEYW_RPCMETHOD_GETTOKENSUPPLY); + assert(fd_webserver_json_keyword("getTokenSupplyx\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenSuppl\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etTokenSupply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tTokenSupply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|TokenSupply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|okenSupply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getT|kenSupply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTo|enSupply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTok|nSupply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToke|Supply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getToken|upply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenS|pply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenSu|ply\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenSup|ly\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenSupp|y\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTokenSuppl|\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransaction\0\0\0\0\0\0\0", 14) == KEYW_RPCMETHOD_GETTRANSACTION); + assert(fd_webserver_json_keyword("getTransactionx\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransactio\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etTransaction\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tTransaction\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Transaction\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ransaction\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getT|ansaction\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTr|nsaction\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTra|saction\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTran|action\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTrans|ction\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransa|tion\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransac|ion\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransact|on\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransacti|n\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransactio|\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransactionCount\0\0\0\0\0\0\0", 19) == KEYW_RPCMETHOD_GETTRANSACTIONCOUNT); + assert(fd_webserver_json_keyword("getTransactionCountx\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransactionCoun\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etTransactionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tTransactionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|TransactionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ransactionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getT|ansactionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTr|nsactionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTra|sactionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTran|actionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTrans|ctionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransa|tionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransac|ionCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransact|onCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransacti|nCount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransactio|Count\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransaction|ount\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransactionC|unt\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransactionCo|nt\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransactionCou|t\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getTransactionCoun|\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVersion\0\0\0\0\0\0\0", 10) == KEYW_RPCMETHOD_GETVERSION); + assert(fd_webserver_json_keyword("getVersionx\0\0\0\0\0\0\0", 11) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVersio\0\0\0\0\0\0\0", 9) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etVersion\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tVersion\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|Version\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|ersion\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getV|rsion\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVe|sion\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVer|ion\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVers|on\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVersi|n\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVersio|\0\0\0\0\0\0\0", 10) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVoteAccounts\0\0\0\0\0\0\0", 15) == KEYW_RPCMETHOD_GETVOTEACCOUNTS); + assert(fd_webserver_json_keyword("getVoteAccountsx\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVoteAccount\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|etVoteAccounts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("g|tVoteAccounts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ge|VoteAccounts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("get|oteAccounts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getV|teAccounts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVo|eAccounts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVot|Accounts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVote|ccounts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVoteA|counts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVoteAc|ounts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVoteAcc|unts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVoteAcco|nts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVoteAccou|ts\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVoteAccoun|s\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("getVoteAccount|\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockhashValid\0\0\0\0\0\0\0", 16) == KEYW_RPCMETHOD_ISBLOCKHASHVALID); + assert(fd_webserver_json_keyword("isBlockhashValidx\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockhashVali\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|sBlockhashValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("i|BlockhashValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("is|lockhashValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isB|ockhashValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBl|ckhashValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlo|khashValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBloc|hashValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlock|ashValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockh|shValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockha|hValid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockhas|Valid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockhash|alid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockhashV|lid\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockhashVa|id\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockhashVal|d\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("isBlockhashVali|\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLedgerSlot\0\0\0\0\0\0\0", 17) == KEYW_RPCMETHOD_MINIMUMLEDGERSLOT); + assert(fd_webserver_json_keyword("minimumLedgerSlotx\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLedgerSlo\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|inimumLedgerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("m|nimumLedgerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("mi|imumLedgerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("min|mumLedgerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("mini|umLedgerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minim|mLedgerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimu|LedgerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimum|edgerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumL|dgerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLe|gerSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLed|erSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLedg|rSlot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLedge|Slot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLedger|lot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLedgerS|ot\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLedgerSl|t\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("minimumLedgerSlo|\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("requestAirdrop\0\0\0\0\0\0\0", 14) == KEYW_RPCMETHOD_REQUESTAIRDROP); + assert(fd_webserver_json_keyword("requestAirdropx\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("requestAirdro\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|equestAirdrop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("r|questAirdrop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("re|uestAirdrop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("req|estAirdrop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("requ|stAirdrop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("reque|tAirdrop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("reques|Airdrop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("request|irdrop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("requestA|rdrop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("requestAi|drop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("requestAir|rop\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("requestAird|op\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("requestAirdr|p\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("requestAirdro|\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTransaction\0\0\0\0\0\0\0", 15) == KEYW_RPCMETHOD_SENDTRANSACTION); + assert(fd_webserver_json_keyword("sendTransactionx\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTransactio\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|endTransaction\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("s|ndTransaction\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("se|dTransaction\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sen|Transaction\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("send|ransaction\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendT|ansaction\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTr|nsaction\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTra|saction\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTran|action\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTrans|ction\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTransa|tion\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTransac|ion\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTransact|on\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTransacti|n\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sendTransactio|\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTransaction\0\0\0\0\0\0\0", 19) == KEYW_RPCMETHOD_SIMULATETRANSACTION); + assert(fd_webserver_json_keyword("simulateTransactionx\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTransactio\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|imulateTransaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("s|mulateTransaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("si|ulateTransaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sim|lateTransaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simu|ateTransaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simul|teTransaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simula|eTransaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulat|Transaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulate|ransaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateT|ansaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTr|nsaction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTra|saction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTran|action\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTrans|ction\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTransa|tion\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTransac|ion\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTransact|on\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTransacti|n\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("simulateTransactio|\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountSubscribe\0\0\0\0\0\0\0", 16) == KEYW_WS_METHOD_ACCOUNTSUBSCRIBE); + assert(fd_webserver_json_keyword("accountSubscribex\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountSubscrib\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ccountSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("a|countSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ac|ountSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("acc|untSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("acco|ntSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accou|tSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accoun|Subscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("account|ubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountS|bscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountSu|scribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountSub|cribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountSubs|ribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountSubsc|ibe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountSubscr|be\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountSubscri|e\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountSubscrib|\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_WS_METHOD_ACCOUNTUNSUBSCRIBE); + assert(fd_webserver_json_keyword("accountUnsubscribex\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUnsubscrib\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ccountUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("a|countUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ac|ountUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("acc|untUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("acco|ntUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accou|tUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accoun|Unsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("account|nsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountU|subscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUn|ubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUns|bscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUnsu|scribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUnsub|cribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUnsubs|ribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUnsubsc|ibe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUnsubscr|be\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUnsubscri|e\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("accountUnsubscrib|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockSubscribe\0\0\0\0\0\0\0", 14) == KEYW_WS_METHOD_BLOCKSUBSCRIBE); + assert(fd_webserver_json_keyword("blockSubscribex\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockSubscrib\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|lockSubscribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("b|ockSubscribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("bl|ckSubscribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blo|kSubscribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("bloc|Subscribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("block|ubscribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockS|bscribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockSu|scribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockSub|cribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockSubs|ribe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockSubsc|ibe\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockSubscr|be\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockSubscri|e\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockSubscrib|\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUnsubscribe\0\0\0\0\0\0\0", 16) == KEYW_WS_METHOD_BLOCKUNSUBSCRIBE); + assert(fd_webserver_json_keyword("blockUnsubscribex\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUnsubscrib\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|lockUnsubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("b|ockUnsubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("bl|ckUnsubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blo|kUnsubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("bloc|Unsubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("block|nsubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockU|subscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUn|ubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUns|bscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUnsu|scribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUnsub|cribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUnsubs|ribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUnsubsc|ibe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUnsubscr|be\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUnsubscri|e\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("blockUnsubscrib|\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsSubscribe\0\0\0\0\0\0\0", 13) == KEYW_WS_METHOD_LOGSSUBSCRIBE); + assert(fd_webserver_json_keyword("logsSubscribex\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsSubscrib\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ogsSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("l|gsSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("lo|sSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("log|Subscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logs|ubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsS|bscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsSu|scribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsSub|cribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsSubs|ribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsSubsc|ibe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsSubscr|be\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsSubscri|e\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsSubscrib|\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_WS_METHOD_LOGSUNSUBSCRIBE); + assert(fd_webserver_json_keyword("logsUnsubscribex\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUnsubscrib\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ogsUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("l|gsUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("lo|sUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("log|Unsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logs|nsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsU|subscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUn|ubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUns|bscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUnsu|scribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUnsub|cribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUnsubs|ribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUnsubsc|ibe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUnsubscr|be\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUnsubscri|e\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("logsUnsubscrib|\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programSubscribe\0\0\0\0\0\0\0", 16) == KEYW_WS_METHOD_PROGRAMSUBSCRIBE); + assert(fd_webserver_json_keyword("programSubscribex\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programSubscrib\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|rogramSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("p|ogramSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("pr|gramSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("pro|ramSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("prog|amSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("progr|mSubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("progra|Subscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("program|ubscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programS|bscribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programSu|scribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programSub|cribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programSubs|ribe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programSubsc|ibe\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programSubscr|be\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programSubscri|e\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programSubscrib|\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_WS_METHOD_PROGRAMUNSUBSCRIBE); + assert(fd_webserver_json_keyword("programUnsubscribex\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUnsubscrib\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|rogramUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("p|ogramUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("pr|gramUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("pro|ramUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("prog|amUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("progr|mUnsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("progra|Unsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("program|nsubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programU|subscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUn|ubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUns|bscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUnsu|scribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUnsub|cribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUnsubs|ribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUnsubsc|ibe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUnsubscr|be\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUnsubscri|e\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("programUnsubscrib|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootSubscribe\0\0\0\0\0\0\0", 13) == KEYW_WS_METHOD_ROOTSUBSCRIBE); + assert(fd_webserver_json_keyword("rootSubscribex\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootSubscrib\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ootSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("r|otSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ro|tSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("roo|Subscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("root|ubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootS|bscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootSu|scribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootSub|cribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootSubs|ribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootSubsc|ibe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootSubscr|be\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootSubscri|e\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootSubscrib|\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_WS_METHOD_ROOTUNSUBSCRIBE); + assert(fd_webserver_json_keyword("rootUnsubscribex\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUnsubscrib\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ootUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("r|otUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("ro|tUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("roo|Unsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("root|nsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootU|subscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUn|ubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUns|bscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUnsu|scribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUnsub|cribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUnsubs|ribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUnsubsc|ibe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUnsubscr|be\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUnsubscri|e\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("rootUnsubscrib|\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureSubscribe\0\0\0\0\0\0\0", 18) == KEYW_WS_METHOD_SIGNATURESUBSCRIBE); + assert(fd_webserver_json_keyword("signatureSubscribex\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureSubscrib\0\0\0\0\0\0\0", 17) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ignatureSubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("s|gnatureSubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("si|natureSubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sig|atureSubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sign|tureSubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signa|ureSubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signat|reSubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatu|eSubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatur|Subscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signature|ubscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureS|bscribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureSu|scribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureSub|cribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureSubs|ribe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureSubsc|ibe\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureSubscr|be\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureSubscri|e\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureSubscrib|\0\0\0\0\0\0\0", 18) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUnsubscribe\0\0\0\0\0\0\0", 20) == KEYW_WS_METHOD_SIGNATUREUNSUBSCRIBE); + assert(fd_webserver_json_keyword("signatureUnsubscribex\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUnsubscrib\0\0\0\0\0\0\0", 19) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|ignatureUnsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("s|gnatureUnsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("si|natureUnsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sig|atureUnsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sign|tureUnsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signa|ureUnsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signat|reUnsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatu|eUnsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatur|Unsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signature|nsubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureU|subscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUn|ubscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUns|bscribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUnsu|scribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUnsub|cribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUnsubs|ribe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUnsubsc|ibe\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUnsubscr|be\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUnsubscri|e\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("signatureUnsubscrib|\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotSubscribe\0\0\0\0\0\0\0", 13) == KEYW_WS_METHOD_SLOTSUBSCRIBE); + assert(fd_webserver_json_keyword("slotSubscribex\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotSubscrib\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|lotSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("s|otSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sl|tSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slo|Subscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slot|ubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotS|bscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotSu|scribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotSub|cribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotSubs|ribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotSubsc|ibe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotSubscr|be\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotSubscri|e\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotSubscrib|\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_WS_METHOD_SLOTUNSUBSCRIBE); + assert(fd_webserver_json_keyword("slotUnsubscribex\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUnsubscrib\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|lotUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("s|otUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sl|tUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slo|Unsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slot|nsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotU|subscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUn|ubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUns|bscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUnsu|scribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUnsub|cribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUnsubs|ribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUnsubsc|ibe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUnsubscr|be\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUnsubscri|e\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotUnsubscrib|\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_WS_METHOD_SLOTSUPDATESSUBSCRIBE); + assert(fd_webserver_json_keyword("slotsUpdatesSubscribex\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesSubscrib\0\0\0\0\0\0\0", 20) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|lotsUpdatesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("s|otsUpdatesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sl|tsUpdatesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slo|sUpdatesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slot|UpdatesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slots|pdatesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsU|datesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUp|atesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpd|tesSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpda|esSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdat|sSubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdate|Subscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdates|ubscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesS|bscribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesSu|scribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesSub|cribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesSubs|ribe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesSubsc|ibe\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesSubscr|be\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesSubscri|e\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesSubscrib|\0\0\0\0\0\0\0", 21) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_WS_METHOD_SLOTSUPDATESUNSUBSCRIBE); + assert(fd_webserver_json_keyword("slotsUpdatesUnsubscribex\0\0\0\0\0\0\0", 24) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUnsubscrib\0\0\0\0\0\0\0", 22) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|lotsUpdatesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("s|otsUpdatesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("sl|tsUpdatesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slo|sUpdatesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slot|UpdatesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slots|pdatesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsU|datesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUp|atesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpd|tesUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpda|esUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdat|sUnsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdate|Unsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdates|nsubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesU|subscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUn|ubscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUns|bscribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUnsu|scribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUnsub|cribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUnsubs|ribe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUnsubsc|ibe\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUnsubscr|be\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUnsubscri|e\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("slotsUpdatesUnsubscrib|\0\0\0\0\0\0\0", 23) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteSubscribe\0\0\0\0\0\0\0", 13) == KEYW_WS_METHOD_VOTESUBSCRIBE); + assert(fd_webserver_json_keyword("voteSubscribex\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteSubscrib\0\0\0\0\0\0\0", 12) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|oteSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("v|teSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("vo|eSubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("vot|Subscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("vote|ubscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteS|bscribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteSu|scribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteSub|cribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteSubs|ribe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteSubsc|ibe\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteSubscr|be\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteSubscri|e\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteSubscrib|\0\0\0\0\0\0\0", 13) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_WS_METHOD_VOTEUNSUBSCRIBE); + assert(fd_webserver_json_keyword("voteUnsubscribex\0\0\0\0\0\0\0", 16) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUnsubscrib\0\0\0\0\0\0\0", 14) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("|oteUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("v|teUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("vo|eUnsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("vot|Unsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("vote|nsubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteU|subscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUn|ubscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUns|bscribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUnsu|scribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUnsub|cribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUnsubs|ribe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUnsubsc|ibe\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUnsubscr|be\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUnsubscri|e\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); + assert(fd_webserver_json_keyword("voteUnsubscrib|\0\0\0\0\0\0\0", 15) == KEYW_UNKNOWN); +}