Skip to content

Commit

Permalink
various fixes to make solana explorer work better
Browse files Browse the repository at this point in the history
  • Loading branch information
asiegel-jt committed Sep 17, 2024
1 parent f55c236 commit 4253dd8
Show file tree
Hide file tree
Showing 15 changed files with 814 additions and 93 deletions.
151 changes: 112 additions & 39 deletions src/app/rpcserver/fd_block_to_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,13 +258,41 @@ fd_txn_meta_to_json( fd_webserver_t * ws,
return NULL;
}

const char*
generic_program_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw,
int * need_comma ) {
FD_SCRATCH_SCOPE_BEGIN { /* read_epoch consumes a ton of scratch space! */
if( *need_comma ) EMIT_SIMPLE(",");
EMIT_SIMPLE("{\"accounts\":[");
const uchar * instr_acc_idxs = raw + instr->acct_off;
const fd_pubkey_t * accts = (const fd_pubkey_t *)(raw + txn->acct_addr_off);
for (ushort j = 0; j < instr->acct_cnt; j++) {
char buf32[FD_BASE58_ENCODED_32_SZ];
fd_base58_encode_32((const uchar*)(accts + instr_acc_idxs[j]), NULL, buf32);
fd_web_reply_sprintf(ws, "%s\"%s\"", (j == 0 ? "" : ","), buf32);
}
EMIT_SIMPLE("],\"data\":\"");
fd_web_reply_encode_base58(ws, raw + instr->data_off, instr->data_sz);
char buf32[FD_BASE58_ENCODED_32_SZ];
fd_base58_encode_32((const uchar*)(accts + instr->program_id), NULL, buf32);
fd_web_reply_sprintf(ws, "\",\"program\":\"unknown\",\"programId\":\"%s\",\"stackHeight\":null}", buf32);
*need_comma = 1;
} FD_SCRATCH_SCOPE_END;
return NULL;
}

const char*
vote_program_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw ) {
const uchar * raw,
int * need_comma ) {
(void)txn;
FD_SCRATCH_SCOPE_BEGIN { /* read_epoch consumes a ton of scratch space! */
if( *need_comma ) EMIT_SIMPLE(",");
fd_vote_instruction_t instruction;
fd_bincode_decode_ctx_t decode = {
.data = raw + instr->data_off,
Expand All @@ -283,6 +311,7 @@ vote_program_to_json( fd_webserver_t * ws,
fd_vote_instruction_walk( json, &instruction, fd_rpc_json_walk, NULL, 0 );

EMIT_SIMPLE(",\"program\":\"vote\",\"programId\":\"Vote111111111111111111111111111111111111111\",\"stackHeight\":null}");
*need_comma = 1;
} FD_SCRATCH_SCOPE_END;
return NULL;
}
Expand All @@ -291,76 +320,118 @@ const char *
system_program_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw ) {
(void)ws;(void)txn;(void)instr;(void)raw;
FD_LOG_WARNING(( "system_program_to_json not implemented" ));
EMIT_SIMPLE("null");
const uchar * raw,
int * need_comma ) {
(void)txn;
FD_SCRATCH_SCOPE_BEGIN { /* read_epoch consumes a ton of scratch space! */
if( *need_comma ) EMIT_SIMPLE(",");
fd_system_program_instruction_t instruction;
fd_bincode_decode_ctx_t decode = {
.data = raw + instr->data_off,
.dataend = raw + instr->data_off + instr->data_sz,
.valloc = fd_scratch_virtual()
};
int decode_result = fd_system_program_instruction_decode( &instruction, &decode );
if( decode_result != FD_BINCODE_SUCCESS ) {
EMIT_SIMPLE("null");
return NULL;
}

EMIT_SIMPLE("{\"parsed\":");

fd_rpc_json_t * json = fd_rpc_json_init( fd_rpc_json_new( fd_scratch_alloc( fd_rpc_json_align(), fd_rpc_json_footprint() ) ), ws );
fd_system_program_instruction_walk( json, &instruction, fd_rpc_json_walk, NULL, 0 );

EMIT_SIMPLE(",\"program\":\"system\",\"programId\":\"11111111111111111111111111111111\",\"stackHeight\":null}");
*need_comma = 1;
} FD_SCRATCH_SCOPE_END;
return NULL;
}

const char*
config_program_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw ) {
(void)ws;(void)txn;(void)instr;(void)raw;
const uchar * raw,
int * need_comma ) {
FD_LOG_WARNING(( "config_program_to_json not implemented" ));
EMIT_SIMPLE("null");
generic_program_to_json( ws, txn, instr, raw, need_comma );
return NULL;
}

const char*
stake_program_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw ) {
(void)ws;(void)txn;(void)instr;(void)raw;
const uchar * raw,
int * need_comma ) {
FD_LOG_WARNING(( "stake_program_to_json not implemented" ));
EMIT_SIMPLE("null");
generic_program_to_json( ws, txn, instr, raw, need_comma );
return NULL;
}

const char*
compute_budget_program_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw ) {
(void)ws;(void)txn;(void)instr;(void)raw;
FD_LOG_WARNING(( "compute_budget_program_to_json not implemented" ));
EMIT_SIMPLE("null");
const uchar * raw,
int * need_comma ) {
(void)txn;
FD_SCRATCH_SCOPE_BEGIN { /* read_epoch consumes a ton of scratch space! */
if( *need_comma ) EMIT_SIMPLE(",");
fd_compute_budget_program_instruction_t instruction;
fd_bincode_decode_ctx_t decode = {
.data = raw + instr->data_off,
.dataend = raw + instr->data_off + instr->data_sz,
.valloc = fd_scratch_virtual()
};
int decode_result = fd_compute_budget_program_instruction_decode( &instruction, &decode );
if( decode_result != FD_BINCODE_SUCCESS ) {
EMIT_SIMPLE("null");
return NULL;
}

EMIT_SIMPLE("{\"parsed\":");

fd_rpc_json_t * json = fd_rpc_json_init( fd_rpc_json_new( fd_scratch_alloc( fd_rpc_json_align(), fd_rpc_json_footprint() ) ), ws );
fd_compute_budget_program_instruction_walk( json, &instruction, fd_rpc_json_walk, NULL, 0 );

EMIT_SIMPLE(",\"program\":\"compute_budget\",\"programId\":\"ComputeBudget111111111111111111111111111111\",\"stackHeight\":null}");
*need_comma = 1;
} FD_SCRATCH_SCOPE_END;
return NULL;
}

const char*
address_lookup_table_program_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw ) {
(void)ws;(void)txn;(void)instr;(void)raw;
const uchar * raw,
int * need_comma ) {
FD_LOG_WARNING(( "address_lookup_table_program_to_json not implemented" ));
EMIT_SIMPLE("null");
generic_program_to_json( ws, txn, instr, raw, need_comma );
return NULL;
}

const char*
executor_zk_elgamal_proof_program_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw ) {
(void)ws;(void)txn;(void)instr;(void)raw;
const uchar * raw,
int * need_comma ) {
FD_LOG_WARNING(( "executor_zk_elgamal_proof_program_to_json not implemented" ));
EMIT_SIMPLE("null");
generic_program_to_json( ws, txn, instr, raw, need_comma );
return NULL;
}

const char*
bpf_loader_program_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw ) {
(void)ws;(void)txn;(void)instr;(void)raw;
const uchar * raw,
int * need_comma ) {
FD_LOG_WARNING(( "bpf_loader_program_to_json not implemented" ));
EMIT_SIMPLE("null");
generic_program_to_json( ws, txn, instr, raw, need_comma );
return NULL;
}

Expand All @@ -369,8 +440,10 @@ fd_instr_to_json( fd_webserver_t * ws,
fd_txn_t * txn,
fd_txn_instr_t * instr,
const uchar * raw,
fd_rpc_encoding_t encoding ) {
fd_rpc_encoding_t encoding,
int * need_comma ) {
if( encoding == FD_ENC_JSON ) {
if( *need_comma ) EMIT_SIMPLE(",");
EMIT_SIMPLE("{\"accounts\":[");
const uchar * instr_acc_idxs = raw + instr->acct_off;
for (ushort j = 0; j < instr->acct_cnt; j++) {
Expand All @@ -379,37 +452,37 @@ fd_instr_to_json( fd_webserver_t * ws,
EMIT_SIMPLE("],\"data\":\"");
fd_web_reply_encode_base58(ws, raw + instr->data_off, instr->data_sz);
fd_web_reply_sprintf(ws, "\",\"programIdIndex\":%u,\"stackHeight\":null}", (uint)instr->program_id);
*need_comma = 1;

} else if( encoding == FD_ENC_JSON_PARSED ) {
ushort acct_cnt = txn->acct_addr_cnt;
const fd_pubkey_t * accts = (const fd_pubkey_t *)(raw + txn->acct_addr_off);
if( instr->program_id >= acct_cnt ) {
EMIT_SIMPLE("null");
return NULL;
}
const fd_pubkey_t * prog = accts + instr->program_id;
if ( !memcmp( prog, fd_solana_vote_program_id.key, sizeof( fd_pubkey_t ) ) ) {
return vote_program_to_json( ws, txn, instr, raw );
return vote_program_to_json( ws, txn, instr, raw, need_comma );
} else if ( !memcmp( prog, fd_solana_system_program_id.key, sizeof( fd_pubkey_t ) ) ) {
return system_program_to_json( ws, txn, instr, raw );
return system_program_to_json( ws, txn, instr, raw, need_comma );
} else if ( !memcmp( prog, fd_solana_config_program_id.key, sizeof( fd_pubkey_t ) ) ) {
return config_program_to_json( ws, txn, instr, raw );
return config_program_to_json( ws, txn, instr, raw, need_comma );
} else if ( !memcmp( prog, fd_solana_stake_program_id.key, sizeof( fd_pubkey_t ) ) ) {
return stake_program_to_json( ws, txn, instr, raw );
return stake_program_to_json( ws, txn, instr, raw, need_comma );
} else if ( !memcmp( prog, fd_solana_compute_budget_program_id.key, sizeof( fd_pubkey_t ) ) ) {
return compute_budget_program_to_json( ws, txn, instr, raw );
return compute_budget_program_to_json( ws, txn, instr, raw, need_comma );
} else if( !memcmp( prog, fd_solana_address_lookup_table_program_id.key, sizeof( fd_pubkey_t ) ) ) {
return address_lookup_table_program_to_json( ws, txn, instr, raw );
return address_lookup_table_program_to_json( ws, txn, instr, raw, need_comma );
} else if( !memcmp( prog, fd_solana_zk_elgamal_proof_program_id.key, sizeof( fd_pubkey_t ) ) ) {
return executor_zk_elgamal_proof_program_to_json( ws, txn, instr, raw );
return executor_zk_elgamal_proof_program_to_json( ws, txn, instr, raw, need_comma );
} else if( !memcmp( prog, fd_solana_bpf_loader_deprecated_program_id.key, sizeof( fd_pubkey_t ))) {
return bpf_loader_program_to_json( ws, txn, instr, raw );
return bpf_loader_program_to_json( ws, txn, instr, raw, need_comma );
} else if( !memcmp( prog, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) ) {
return bpf_loader_program_to_json( ws, txn, instr, raw );
return bpf_loader_program_to_json( ws, txn, instr, raw, need_comma );
} else if( !memcmp( prog, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
return bpf_loader_program_to_json( ws, txn, instr, raw );
return bpf_loader_program_to_json( ws, txn, instr, raw, need_comma );
} else {
EMIT_SIMPLE("null");
generic_program_to_json( ws, txn, instr, raw, need_comma );
}
}
return NULL;
Expand Down Expand Up @@ -470,9 +543,9 @@ fd_txn_to_json_full( fd_webserver_t * ws,
(uint)txn->readonly_signed_cnt, (uint)txn->readonly_unsigned_cnt, (uint)txn->signature_cnt);

ushort instr_cnt = txn->instr_cnt;
int need_comma = 0;
for (ushort idx = 0; idx < instr_cnt; idx++) {
if( idx ) EMIT_SIMPLE(",");
const char * res = fd_instr_to_json( ws, txn, &txn->instr[idx], raw, encoding );
const char * res = fd_instr_to_json( ws, txn, &txn->instr[idx], raw, encoding, &need_comma );
if( res ) return res;
}

Expand Down
32 changes: 21 additions & 11 deletions src/app/rpcserver/keywords.c
Original file line number Diff line number Diff line change
Expand Up @@ -732,19 +732,28 @@ long fd_webserver_json_keyword(const char* keyw, unsigned long keyw_sz) {
}
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"
switch (keyw[0]) {
case 'e':
if (*(unsigned long*)&keyw[1] == 0x6F4E6564756C6378UL && *(unsigned long*)&keyw[9] == 0x616C75637269436EUL && *(unsigned long*)&keyw[17] == 0x6F636341676E6974UL && *(unsigned long*)&keyw[25] == 0x7473694C73746E75UL) {
return KEYW_JSON_EXCLUDENONCIRCULATINGACCOUNTSLIST; // "excludeNonCirculatingAccountsList"
}
break;
case 'g':
if ((*(unsigned long*)&keyw[1] & 0xFFFFUL) == 0x7465UL) {
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;
}
break;
}
break;
}
Expand Down Expand Up @@ -861,6 +870,7 @@ const char* un_fd_webserver_json_keyword(long id) {
case KEYW_WS_METHOD_SLOTSUPDATESUNSUBSCRIBE: return "slotsUpdatesUnsubscribe";
case KEYW_WS_METHOD_VOTESUBSCRIBE: return "voteSubscribe";
case KEYW_WS_METHOD_VOTEUNSUBSCRIBE: return "voteUnsubscribe";
case KEYW_JSON_EXCLUDENONCIRCULATINGACCOUNTSLIST: return "excludeNonCirculatingAccountsList";
}
return "???";
}
1 change: 1 addition & 0 deletions src/app/rpcserver/keywords.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
#define KEYW_WS_METHOD_SLOTSUPDATESUNSUBSCRIBE 106L
#define KEYW_WS_METHOD_VOTESUBSCRIBE 107L
#define KEYW_WS_METHOD_VOTEUNSUBSCRIBE 108L
#define KEYW_JSON_EXCLUDENONCIRCULATINGACCOUNTSLIST 109L
#ifndef KEYW_UNKNOWN
#define KEYW_UNKNOWN -1L
#endif
Expand Down
1 change: 1 addition & 0 deletions src/app/rpcserver/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,4 @@ slotsUpdatesSubscribe KEYW_WS_METHOD_SLOTSUPDATESSUBSCRIBE
slotsUpdatesUnsubscribe KEYW_WS_METHOD_SLOTSUPDATESUNSUBSCRIBE
voteSubscribe KEYW_WS_METHOD_VOTESUBSCRIBE
voteUnsubscribe KEYW_WS_METHOD_VOTEUNSUBSCRIBE
excludeNonCirculatingAccountsList KEYW_JSON_EXCLUDENONCIRCULATINGACCOUNTSLIST
36 changes: 36 additions & 0 deletions src/app/rpcserver/test_keywords.h
Original file line number Diff line number Diff line change
Expand Up @@ -2002,4 +2002,40 @@ void test_fd_webserver_json_keyword(void) {
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);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_JSON_EXCLUDENONCIRCULATINGACCOUNTSLIST);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccountsListx\0\0\0\0\0\0\0", 34) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccountsLis\0\0\0\0\0\0\0", 32) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("|xcludeNonCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("e|cludeNonCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("ex|ludeNonCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("exc|udeNonCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excl|deNonCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("exclu|eNonCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("exclud|NonCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("exclude|onCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeN|nCirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNo|CirculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNon|irculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonC|rculatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCi|culatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCir|ulatingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirc|latingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCircu|atingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCircul|tingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCircula|ingAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculat|ngAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculati|gAccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatin|AccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculating|ccountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingA|countsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAc|ountsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAcc|untsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAcco|ntsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccou|tsList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccoun|sList\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccount|List\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccounts|ist\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccountsL|st\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccountsLi|t\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
assert(fd_webserver_json_keyword("excludeNonCirculatingAccountsLis|\0\0\0\0\0\0\0", 33) == KEYW_UNKNOWN);
}
Loading

0 comments on commit 4253dd8

Please sign in to comment.