From f1ec955dc923b738d79f2bd4879830d0abaa5241 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 19 Nov 2024 23:02:14 -0500 Subject: [PATCH 1/5] refactor LedgerEntry --- src/test/rpc/LedgerRPC_test.cpp | 338 +++--- src/xrpld/net/detail/RPCCall.cpp | 18 +- src/xrpld/rpc/detail/RPCHelpers.cpp | 10 +- src/xrpld/rpc/handlers/LedgerEntry.cpp | 1512 ++++++++++++------------ 4 files changed, 992 insertions(+), 886 deletions(-) diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index 41657468666..41b0239fb50 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -1877,160 +1877,164 @@ class LedgerRPC_test : public beast::unit_test::suite env(pay(gw, alice, USD(97))); env.close(); - std::string const ledgerHash{to_string(env.closed()->info().hash)}; - { - // Request the trust line using the accounts and currency. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = alice.human(); - jvParams[jss::ripple_state][jss::accounts][1u] = gw.human(); - jvParams[jss::ripple_state][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - BEAST_EXPECT( - jrr[jss::node][sfBalance.jsonName][jss::value] == "-97"); - BEAST_EXPECT( - jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999"); - } - { - // ripple_state is not an object. - Json::Value jvParams; - jvParams[jss::ripple_state] = "ripple_state"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state.currency is missing. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = alice.human(); - jvParams[jss::ripple_state][jss::accounts][1u] = gw.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state accounts is not an array. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = 2; - jvParams[jss::ripple_state][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state one of the accounts is missing. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = alice.human(); - jvParams[jss::ripple_state][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state more than 2 accounts. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = alice.human(); - jvParams[jss::ripple_state][jss::accounts][1u] = gw.human(); - jvParams[jss::ripple_state][jss::accounts][2u] = alice.human(); - jvParams[jss::ripple_state][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } + // check both aliases + for (auto const& fieldName : {jss::ripple_state, jss::state}) { - // ripple_state account[0] is not a string. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = 44; - jvParams[jss::ripple_state][jss::accounts][1u] = gw.human(); - jvParams[jss::ripple_state][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state account[1] is not a string. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = alice.human(); - jvParams[jss::ripple_state][jss::accounts][1u] = 21; - jvParams[jss::ripple_state][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state account[0] == account[1]. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = alice.human(); - jvParams[jss::ripple_state][jss::accounts][1u] = alice.human(); - jvParams[jss::ripple_state][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state malformed account[0]. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = - makeBadAddress(alice.human()); - jvParams[jss::ripple_state][jss::accounts][1u] = gw.human(); - jvParams[jss::ripple_state][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); - } - { - // ripple_state malformed account[1]. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = alice.human(); - jvParams[jss::ripple_state][jss::accounts][1u] = - makeBadAddress(gw.human()); - jvParams[jss::ripple_state][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); - } - { - // ripple_state malformed currency. - Json::Value jvParams; - jvParams[jss::ripple_state] = Json::objectValue; - jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue; - jvParams[jss::ripple_state][jss::accounts][0u] = alice.human(); - jvParams[jss::ripple_state][jss::accounts][1u] = gw.human(); - jvParams[jss::ripple_state][jss::currency] = "USDollars"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedCurrency", ""); + std::string const ledgerHash{to_string(env.closed()->info().hash)}; + { + // Request the trust line using the accounts and currency. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = alice.human(); + jvParams[fieldName][jss::accounts][1u] = gw.human(); + jvParams[fieldName][jss::currency] = "USD"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + BEAST_EXPECT( + jrr[jss::node][sfBalance.jsonName][jss::value] == "-97"); + BEAST_EXPECT( + jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999"); + } + { + // ripple_state is not an object. + Json::Value jvParams; + jvParams[fieldName] = "ripple_state"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedRequest", ""); + } + { + // ripple_state.currency is missing. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = alice.human(); + jvParams[fieldName][jss::accounts][1u] = gw.human(); + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedRequest", ""); + } + { + // ripple_state accounts is not an array. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = 2; + jvParams[fieldName][jss::currency] = "USD"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedRequest", ""); + } + { + // ripple_state one of the accounts is missing. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = alice.human(); + jvParams[fieldName][jss::currency] = "USD"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedRequest", ""); + } + { + // ripple_state more than 2 accounts. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = alice.human(); + jvParams[fieldName][jss::accounts][1u] = gw.human(); + jvParams[fieldName][jss::accounts][2u] = alice.human(); + jvParams[fieldName][jss::currency] = "USD"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedRequest", ""); + } + { + // ripple_state account[0] is not a string. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = 44; + jvParams[fieldName][jss::accounts][1u] = gw.human(); + jvParams[fieldName][jss::currency] = "USD"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedRequest", ""); + } + { + // ripple_state account[1] is not a string. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = alice.human(); + jvParams[fieldName][jss::accounts][1u] = 21; + jvParams[fieldName][jss::currency] = "USD"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedRequest", ""); + } + { + // ripple_state account[0] == account[1]. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = alice.human(); + jvParams[fieldName][jss::accounts][1u] = alice.human(); + jvParams[fieldName][jss::currency] = "USD"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedRequest", ""); + } + { + // ripple_state malformed account[0]. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = + makeBadAddress(alice.human()); + jvParams[fieldName][jss::accounts][1u] = gw.human(); + jvParams[fieldName][jss::currency] = "USD"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedAddress", ""); + } + { + // ripple_state malformed account[1]. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = alice.human(); + jvParams[fieldName][jss::accounts][1u] = + makeBadAddress(gw.human()); + jvParams[fieldName][jss::currency] = "USD"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedAddress", ""); + } + { + // ripple_state malformed currency. + Json::Value jvParams; + jvParams[fieldName] = Json::objectValue; + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = alice.human(); + jvParams[fieldName][jss::accounts][1u] = gw.human(); + jvParams[fieldName][jss::currency] = "USDollars"; + jvParams[jss::ledger_hash] = ledgerHash; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue(jrr, "malformedCurrency", ""); + } } } @@ -3055,6 +3059,33 @@ class LedgerRPC_test : public beast::unit_test::suite } } + void + testLedgerEntryCLI() + { + testcase("ledger_entry command-line"); + using namespace test::jtx; + + Env env{*this}; + Account const alice{"alice"}; + env.fund(XRP(10000), alice); + env.close(); + + auto const checkId = keylet::check(env.master, env.seq(env.master)); + + env(check::create(env.master, alice, XRP(100))); + env.close(); + + std::string const ledgerHash{to_string(env.closed()->info().hash)}; + { + // Request a check. + Json::Value const jrr = + env.rpc("ledger_entry", to_string(checkId.key))[jss::result]; + BEAST_EXPECT( + jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check); + BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000"); + } + } + public: void run() override @@ -3085,6 +3116,7 @@ class LedgerRPC_test : public beast::unit_test::suite testInvalidOracleLedgerEntry(); testOracleLedgerEntry(); testLedgerEntryMPT(); + testLedgerEntryCLI(); forAllApiVersions(std::bind_front( &LedgerRPC_test::testLedgerEntryInvalidParams, this)); diff --git a/src/xrpld/net/detail/RPCCall.cpp b/src/xrpld/net/detail/RPCCall.cpp index b92f4b1a205..cd960c2fedc 100644 --- a/src/xrpld/net/detail/RPCCall.cpp +++ b/src/xrpld/net/detail/RPCCall.cpp @@ -668,6 +668,21 @@ class RPCParser return jvRequest; } + // ledger_entry [id] [] + Json::Value + parseLedgerEntry(Json::Value const& jvParams) + { + Json::Value jvRequest{Json::objectValue}; + + jvRequest[jss::index] = jvParams[0u].asString(); + + if (jvParams.size() == 2 && + !jvParseLedger(jvRequest, jvParams[1u].asString())) + return rpcError(rpcLGR_IDX_MALFORMED); + + return jvRequest; + } + // log_level: Get log levels // log_level : Set master log level to the // specified severity log_level : Set specified @@ -1181,8 +1196,7 @@ class RPCParser {"ledger_accept", &RPCParser::parseAsIs, 0, 0}, {"ledger_closed", &RPCParser::parseAsIs, 0, 0}, {"ledger_current", &RPCParser::parseAsIs, 0, 0}, - // { "ledger_entry", &RPCParser::parseLedgerEntry, - // -1, -1 }, + {"ledger_entry", &RPCParser::parseLedgerEntry, 1, 2}, {"ledger_header", &RPCParser::parseLedgerId, 1, 1}, {"ledger_request", &RPCParser::parseLedgerId, 1, 1}, {"log_level", &RPCParser::parseLogLevel, 0, 2}, diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index af204eaedf7..0f74acc258c 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -945,20 +945,20 @@ chooseLedgerEntryType(Json::Value const& params) {jss::escrow, ltESCROW}, {jss::fee, ltFEE_SETTINGS}, {jss::hashes, ltLEDGER_HASHES}, - {jss::nunl, ltNEGATIVE_UNL}, - {jss::oracle, ltORACLE}, + {jss::mpt_issuance, ltMPTOKEN_ISSUANCE}, + {jss::mptoken, ltMPTOKEN}, {jss::nft_offer, ltNFTOKEN_OFFER}, {jss::nft_page, ltNFTOKEN_PAGE}, + {jss::nunl, ltNEGATIVE_UNL}, {jss::offer, ltOFFER}, + {jss::oracle, ltORACLE}, {jss::payment_channel, ltPAYCHAN}, {jss::signer_list, ltSIGNER_LIST}, {jss::state, ltRIPPLE_STATE}, {jss::ticket, ltTICKET}, {jss::xchain_owned_claim_id, ltXCHAIN_OWNED_CLAIM_ID}, {jss::xchain_owned_create_account_claim_id, - ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID}, - {jss::mpt_issuance, ltMPTOKEN_ISSUANCE}, - {jss::mptoken, ltMPTOKEN}}}; + ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID}}}; auto const& p = params[jss::type]; if (!p.isString()) diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 5d03bbb189d..d3e9af4c299 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace ripple { @@ -66,768 +67,819 @@ parseAuthorizeCredentials(Json::Value const& jv) return arr; } -// { -// ledger_hash : -// ledger_index : -// ... -// } -Json::Value -doLedgerEntry(RPC::JsonContext& context) +std::optional +parseIndex(Json::Value const& params, Json::Value& jvResult) { - std::shared_ptr lpLedger; - auto jvResult = RPC::lookupLedger(lpLedger, context); + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(params.asString())) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + return uNodeIndex; +} - if (!lpLedger) - return jvResult; +std::optional +parseAccountRoot(Json::Value const& params, Json::Value& jvResult) +{ + auto const account = parseBase58(params.asString()); + if (!account || account->isZero()) + { + jvResult[jss::error] = "malformedAddress"; + return std::nullopt; + } + return keylet::account(*account).key; +} +std::optional +parseCheck(Json::Value const& params, Json::Value& jvResult) +{ uint256 uNodeIndex; - bool bNodeBinary = false; - LedgerEntryType expectedType = ltANY; + if (!uNodeIndex.parseHex(params.asString())) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + return uNodeIndex; +} - try +std::optional +parseDepositPreauth(Json::Value const& dp, Json::Value& jvResult) +{ + if (!dp.isObject()) { - if (context.params.isMember(jss::index)) + uint256 uNodeIndex; + if (!dp.isString() || !uNodeIndex.parseHex(dp.asString())) { - if (!uNodeIndex.parseHex(context.params[jss::index].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::account_root)) + return uNodeIndex; + } + // clang-format off + if ( + (!dp.isMember(jss::owner) || !dp[jss::owner].isString()) || + (dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)) || + (dp.isMember(jss::authorized) && !dp[jss::authorized].isString()) || + (dp.isMember(jss::authorized_credentials) && !dp[jss::authorized_credentials].isArray()) + ) + // clang-format on + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + + auto const owner = parseBase58(dp[jss::owner].asString()); + if (!owner) + { + jvResult[jss::error] = "malformedOwner"; + return std::nullopt; + } + if (dp.isMember(jss::authorized)) + { + auto const authorized = + parseBase58(dp[jss::authorized].asString()); + if (!authorized) { - expectedType = ltACCOUNT_ROOT; - auto const account = parseBase58( - context.params[jss::account_root].asString()); - if (!account || account->isZero()) - jvResult[jss::error] = "malformedAddress"; - else - uNodeIndex = keylet::account(*account).key; + jvResult[jss::error] = "malformedAuthorized"; + return std::nullopt; } - else if (context.params.isMember(jss::check)) + return keylet::depositPreauth(*owner, *authorized).key; + } + + auto const& ac(dp[jss::authorized_credentials]); + STArray const arr = parseAuthorizeCredentials(ac); + + if (arr.empty() || (arr.size() > maxCredentialsArraySize)) + { + jvResult[jss::error] = "malformedAuthorizedCredentials"; + return std::nullopt; + } + auto sorted = credentials::makeSorted(arr); + if (sorted.empty()) + { + jvResult[jss::error] = "malformedAuthorizedCredentials"; + return std::nullopt; + } + + return keylet::depositPreauth(*owner, sorted).key; +} + +std::optional +parseDirectory(Json::Value const& params, Json::Value& jvResult) +{ + if (params.isNull()) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + + if (!params.isObject()) + { + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(params.asString())) { - expectedType = ltCHECK; - if (!uNodeIndex.parseHex(context.params[jss::check].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::deposit_preauth)) - { - expectedType = ltDEPOSIT_PREAUTH; - auto const& dp = context.params[jss::deposit_preauth]; + return uNodeIndex; + } - if (!dp.isObject()) - { - if (!dp.isString() || !uNodeIndex.parseHex(dp.asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - // clang-format off - else if ( - (!dp.isMember(jss::owner) || !dp[jss::owner].isString()) || - (dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)) || - (dp.isMember(jss::authorized) && !dp[jss::authorized].isString()) || - (dp.isMember(jss::authorized_credentials) && !dp[jss::authorized_credentials].isArray()) - ) - // clang-format on - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - auto const owner = - parseBase58(dp[jss::owner].asString()); - if (!owner) - { - jvResult[jss::error] = "malformedOwner"; - } - else if (dp.isMember(jss::authorized)) - { - auto const authorized = - parseBase58(dp[jss::authorized].asString()); - if (!authorized) - jvResult[jss::error] = "malformedAuthorized"; - else - uNodeIndex = - keylet::depositPreauth(*owner, *authorized).key; - } - else - { - auto const& ac(dp[jss::authorized_credentials]); - STArray const arr = parseAuthorizeCredentials(ac); - - if (arr.empty() || (arr.size() > maxCredentialsArraySize)) - jvResult[jss::error] = "malformedAuthorizedCredentials"; - else - { - auto sorted = credentials::makeSorted(arr); - if (sorted.empty()) - jvResult[jss::error] = - "malformedAuthorizedCredentials"; - else - uNodeIndex = - keylet::depositPreauth(*owner, sorted).key; - } - } - } + if (params.isMember(jss::sub_index) && !params[jss::sub_index].isIntegral()) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + + std::uint64_t uSubIndex = + params.isMember(jss::sub_index) ? params[jss::sub_index].asUInt() : 0; + + if (params.isMember(jss::dir_root)) + { + uint256 uDirRoot; + + if (params.isMember(jss::owner)) + { + // May not specify both dir_root and owner. + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::directory)) + if (!uDirRoot.parseHex(params[jss::dir_root].asString())) { - expectedType = ltDIR_NODE; - if (context.params[jss::directory].isNull()) - { - jvResult[jss::error] = "malformedRequest"; - } - else if (!context.params[jss::directory].isObject()) - { - if (!uNodeIndex.parseHex( - context.params[jss::directory].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - context.params[jss::directory].isMember(jss::sub_index) && - !context.params[jss::directory][jss::sub_index].isIntegral()) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - std::uint64_t uSubIndex = - context.params[jss::directory].isMember(jss::sub_index) - ? context.params[jss::directory][jss::sub_index].asUInt() - : 0; - - if (context.params[jss::directory].isMember(jss::dir_root)) - { - uint256 uDirRoot; - - if (context.params[jss::directory].isMember(jss::owner)) - { - // May not specify both dir_root and owner. - jvResult[jss::error] = "malformedRequest"; - } - else if (!uDirRoot.parseHex( - context.params[jss::directory][jss::dir_root] - .asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - else - { - uNodeIndex = keylet::page(uDirRoot, uSubIndex).key; - } - } - else if (context.params[jss::directory].isMember(jss::owner)) - { - auto const ownerID = parseBase58( - context.params[jss::directory][jss::owner].asString()); - - if (!ownerID) - { - jvResult[jss::error] = "malformedAddress"; - } - else - { - uNodeIndex = - keylet::page(keylet::ownerDir(*ownerID), uSubIndex) - .key; - } - } - else - { - jvResult[jss::error] = "malformedRequest"; - } - } + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::escrow)) + return keylet::page(uDirRoot, uSubIndex).key; + } + + if (params.isMember(jss::owner)) + { + auto const ownerID = + parseBase58(params[jss::owner].asString()); + + if (!ownerID) { - expectedType = ltESCROW; - if (!context.params[jss::escrow].isObject()) - { - if (!uNodeIndex.parseHex( - context.params[jss::escrow].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - !context.params[jss::escrow].isMember(jss::owner) || - !context.params[jss::escrow].isMember(jss::seq) || - !context.params[jss::escrow][jss::seq].isIntegral()) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - auto const id = parseBase58( - context.params[jss::escrow][jss::owner].asString()); - if (!id) - jvResult[jss::error] = "malformedOwner"; - else - uNodeIndex = - keylet::escrow( - *id, context.params[jss::escrow][jss::seq].asUInt()) - .key; - } + jvResult[jss::error] = "malformedAddress"; + return std::nullopt; } - else if (context.params.isMember(jss::offer)) + return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key; + } + + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; +} + +std::optional +parseEscrow(Json::Value const& params, Json::Value& jvResult) +{ + if (!params.isObject()) + { + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(params.asString())) { - expectedType = ltOFFER; - if (!context.params[jss::offer].isObject()) - { - if (!uNodeIndex.parseHex(context.params[jss::offer].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - !context.params[jss::offer].isMember(jss::account) || - !context.params[jss::offer].isMember(jss::seq) || - !context.params[jss::offer][jss::seq].isIntegral()) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - auto const id = parseBase58( - context.params[jss::offer][jss::account].asString()); - if (!id) - jvResult[jss::error] = "malformedAddress"; - else - uNodeIndex = - keylet::offer( - *id, context.params[jss::offer][jss::seq].asUInt()) - .key; - } + uNodeIndex = beast::zero; + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::payment_channel)) + return uNodeIndex; + } + + if (!params.isMember(jss::owner) || !params.isMember(jss::seq) || + !params[jss::seq].isIntegral()) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + + auto const id = parseBase58(params[jss::owner].asString()); + + if (!id) + { + jvResult[jss::error] = "malformedOwner"; + return std::nullopt; + } + + return keylet::escrow(*id, params[jss::seq].asUInt()).key; +} + +std::optional +parseOffer(Json::Value const& params, Json::Value& jvResult) +{ + if (!params.isObject()) + { + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(params.asString())) { - expectedType = ltPAYCHAN; + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + return uNodeIndex; + } + if (!params.isMember(jss::account) || !params.isMember(jss::seq) || + !params[jss::seq].isIntegral()) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } - if (!uNodeIndex.parseHex( - context.params[jss::payment_channel].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } + auto const id = parseBase58(params[jss::account].asString()); + if (!id) + { + jvResult[jss::error] = "malformedAddress"; + return std::nullopt; + } + + return keylet::offer(*id, params[jss::seq].asUInt()).key; +} + +std::optional +parsePaymentChannel(Json::Value const& params, Json::Value& jvResult) +{ + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(params.asString())) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + return uNodeIndex; +} + +std::optional +parseRippleState(Json::Value const& jvRippleState, Json::Value& jvResult) +{ + Currency uCurrency; + + if (!jvRippleState.isObject() || !jvRippleState.isMember(jss::currency) || + !jvRippleState.isMember(jss::accounts) || + !jvRippleState[jss::accounts].isArray() || + 2 != jvRippleState[jss::accounts].size() || + !jvRippleState[jss::accounts][0u].isString() || + !jvRippleState[jss::accounts][1u].isString() || + (jvRippleState[jss::accounts][0u].asString() == + jvRippleState[jss::accounts][1u].asString())) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + + auto const id1 = + parseBase58(jvRippleState[jss::accounts][0u].asString()); + auto const id2 = + parseBase58(jvRippleState[jss::accounts][1u].asString()); + if (!id1 || !id2) + { + jvResult[jss::error] = "malformedAddress"; + return std::nullopt; + } + + if (!to_currency(uCurrency, jvRippleState[jss::currency].asString())) + { + jvResult[jss::error] = "malformedCurrency"; + return std::nullopt; + } + + return keylet::line(*id1, *id2, uCurrency).key; +} + +std::optional +parseTicket(Json::Value const& params, Json::Value& jvResult) +{ + if (!params.isObject()) + { + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(params.asString())) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::ripple_state)) + return uNodeIndex; + } + + if (!params.isMember(jss::account) || !params.isMember(jss::ticket_seq) || + !params[jss::ticket_seq].isIntegral()) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + + auto const id = parseBase58(params[jss::account].asString()); + if (!id) + { + jvResult[jss::error] = "malformedAddress"; + return std::nullopt; + } + + return getTicketIndex(*id, params[jss::ticket_seq].asUInt()); +} + +std::optional +parseNFTokenPage(Json::Value const& params, Json::Value& jvResult) +{ + if (params.isString()) + { + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(params.asString())) { - expectedType = ltRIPPLE_STATE; - Currency uCurrency; - Json::Value jvRippleState = context.params[jss::ripple_state]; - - if (!jvRippleState.isObject() || - !jvRippleState.isMember(jss::currency) || - !jvRippleState.isMember(jss::accounts) || - !jvRippleState[jss::accounts].isArray() || - 2 != jvRippleState[jss::accounts].size() || - !jvRippleState[jss::accounts][0u].isString() || - !jvRippleState[jss::accounts][1u].isString() || - (jvRippleState[jss::accounts][0u].asString() == - jvRippleState[jss::accounts][1u].asString())) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - auto const id1 = parseBase58( - jvRippleState[jss::accounts][0u].asString()); - auto const id2 = parseBase58( - jvRippleState[jss::accounts][1u].asString()); - if (!id1 || !id2) - { - jvResult[jss::error] = "malformedAddress"; - } - else if (!to_currency( - uCurrency, - jvRippleState[jss::currency].asString())) - { - jvResult[jss::error] = "malformedCurrency"; - } - else - { - uNodeIndex = keylet::line(*id1, *id2, uCurrency).key; - } - } + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::ticket)) + return uNodeIndex; + } + + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; +} + +std::optional +parseAMM(Json::Value const& params, Json::Value& jvResult) +{ + if (!params.isObject()) + { + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(params.asString())) { - expectedType = ltTICKET; - if (!context.params[jss::ticket].isObject()) - { - if (!uNodeIndex.parseHex( - context.params[jss::ticket].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - !context.params[jss::ticket].isMember(jss::account) || - !context.params[jss::ticket].isMember(jss::ticket_seq) || - !context.params[jss::ticket][jss::ticket_seq].isIntegral()) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - auto const id = parseBase58( - context.params[jss::ticket][jss::account].asString()); - if (!id) - jvResult[jss::error] = "malformedAddress"; - else - uNodeIndex = getTicketIndex( - *id, - context.params[jss::ticket][jss::ticket_seq].asUInt()); - } + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::nft_page)) + return uNodeIndex; + } + + if (!params.isMember(jss::asset) || !params.isMember(jss::asset2)) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + + try + { + auto const issue = issueFromJson(params[jss::asset]); + auto const issue2 = issueFromJson(params[jss::asset2]); + return keylet::amm(issue, issue2).key; + } + catch (std::runtime_error const&) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } +} + +std::optional +parseBridge(Json::Value const& params, Json::Value& jvResult) +{ + // return the keylet for the specified bridge or nullopt if the + // request is malformed + auto const maybeKeylet = [&]() -> std::optional { + try { - expectedType = ltNFTOKEN_PAGE; + if (!params.isMember(jss::bridge_account)) + return std::nullopt; - if (context.params[jss::nft_page].isString()) + auto const& jsBridgeAccount = params[jss::bridge_account]; + if (!jsBridgeAccount.isString()) { - if (!uNodeIndex.parseHex( - context.params[jss::nft_page].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } + return std::nullopt; } - else + auto const account = + parseBase58(jsBridgeAccount.asString()); + if (!account || account->isZero()) { - jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } + + // This may throw and is the reason for the `try` block. The + // try block has a larger scope so the `bridge` variable + // doesn't need to be an optional. + STXChainBridge const bridge(params[jss::bridge]); + STXChainBridge::ChainType const chainType = + STXChainBridge::srcChain(account == bridge.lockingChainDoor()); + if (account != bridge.door(chainType)) + return std::nullopt; + + return keylet::bridge(bridge, chainType); } - else if (context.params.isMember(jss::amm)) + catch (...) { - expectedType = ltAMM; - if (!context.params[jss::amm].isObject()) - { - if (!uNodeIndex.parseHex(context.params[jss::amm].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - !context.params[jss::amm].isMember(jss::asset) || - !context.params[jss::amm].isMember(jss::asset2)) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - try - { - auto const issue = - issueFromJson(context.params[jss::amm][jss::asset]); - auto const issue2 = - issueFromJson(context.params[jss::amm][jss::asset2]); - uNodeIndex = keylet::amm(issue, issue2).key; - } - catch (std::runtime_error const&) - { - jvResult[jss::error] = "malformedRequest"; - } - } + return std::nullopt; } - else if (context.params.isMember(jss::bridge)) + }(); + + if (maybeKeylet) + { + return maybeKeylet->key; + } + + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; +} + +std::optional +parseXChainOwnedClaimID(Json::Value const& claim_id, Json::Value& jvResult) +{ + if (claim_id.isString()) + { + uint256 uNodeIndex; + // we accept a node id as specifier of a xchain claim id + if (!uNodeIndex.parseHex(claim_id.asString())) { - expectedType = ltBRIDGE; + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + return uNodeIndex; + } - // return the keylet for the specified bridge or nullopt if the - // request is malformed - auto const maybeKeylet = [&]() -> std::optional { - try - { - if (!context.params.isMember(jss::bridge_account)) - return std::nullopt; - - auto const& jsBridgeAccount = - context.params[jss::bridge_account]; - if (!jsBridgeAccount.isString()) - { - return std::nullopt; - } - auto const account = - parseBase58(jsBridgeAccount.asString()); - if (!account || account->isZero()) - { - return std::nullopt; - } - - // This may throw and is the reason for the `try` block. The - // try block has a larger scope so the `bridge` variable - // doesn't need to be an optional. - STXChainBridge const bridge(context.params[jss::bridge]); - STXChainBridge::ChainType const chainType = - STXChainBridge::srcChain( - account == bridge.lockingChainDoor()); - if (account != bridge.door(chainType)) - return std::nullopt; - - return keylet::bridge(bridge, chainType); - } - catch (...) - { - return std::nullopt; - } - }(); + if (!claim_id.isObject() || + !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) && + claim_id[sfIssuingChainDoor.getJsonName()].isString()) || + !(claim_id.isMember(sfLockingChainDoor.getJsonName()) && + claim_id[sfLockingChainDoor.getJsonName()].isString()) || + !claim_id.isMember(sfIssuingChainIssue.getJsonName()) || + !claim_id.isMember(sfLockingChainIssue.getJsonName()) || + !claim_id.isMember(jss::xchain_owned_claim_id)) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } - if (maybeKeylet) - { - uNodeIndex = maybeKeylet->key; - } - else - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } + // if not specified with a node id, a claim_id is specified by + // four strings defining the bridge (locking_chain_door, + // locking_chain_issue, issuing_chain_door, issuing_chain_issue) + // and the claim id sequence number. + auto lockingChainDoor = parseBase58( + claim_id[sfLockingChainDoor.getJsonName()].asString()); + auto issuingChainDoor = parseBase58( + claim_id[sfIssuingChainDoor.getJsonName()].asString()); + Issue lockingChainIssue, issuingChainIssue; + bool valid = lockingChainDoor && issuingChainDoor; + if (valid) + { + try + { + lockingChainIssue = + issueFromJson(claim_id[sfLockingChainIssue.getJsonName()]); + issuingChainIssue = + issueFromJson(claim_id[sfIssuingChainIssue.getJsonName()]); } - else if (context.params.isMember(jss::xchain_owned_claim_id)) + catch (std::runtime_error const& ex) { - expectedType = ltXCHAIN_OWNED_CLAIM_ID; - auto& claim_id = context.params[jss::xchain_owned_claim_id]; - if (claim_id.isString()) - { - // we accept a node id as specifier of a xchain claim id - if (!uNodeIndex.parseHex(claim_id.asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - !claim_id.isObject() || - !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) && - claim_id[sfIssuingChainDoor.getJsonName()].isString()) || - !(claim_id.isMember(sfLockingChainDoor.getJsonName()) && - claim_id[sfLockingChainDoor.getJsonName()].isString()) || - !claim_id.isMember(sfIssuingChainIssue.getJsonName()) || - !claim_id.isMember(sfLockingChainIssue.getJsonName()) || - !claim_id.isMember(jss::xchain_owned_claim_id)) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - // if not specified with a node id, a claim_id is specified by - // four strings defining the bridge (locking_chain_door, - // locking_chain_issue, issuing_chain_door, issuing_chain_issue) - // and the claim id sequence number. - auto lockingChainDoor = parseBase58( - claim_id[sfLockingChainDoor.getJsonName()].asString()); - auto issuingChainDoor = parseBase58( - claim_id[sfIssuingChainDoor.getJsonName()].asString()); - Issue lockingChainIssue, issuingChainIssue; - bool valid = lockingChainDoor && issuingChainDoor; - if (valid) - { - try - { - lockingChainIssue = issueFromJson( - claim_id[sfLockingChainIssue.getJsonName()]); - issuingChainIssue = issueFromJson( - claim_id[sfIssuingChainIssue.getJsonName()]); - } - catch (std::runtime_error const& ex) - { - valid = false; - jvResult[jss::error] = "malformedRequest"; - } - } + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + } - if (valid && claim_id[jss::xchain_owned_claim_id].isIntegral()) - { - auto seq = claim_id[jss::xchain_owned_claim_id].asUInt(); - - STXChainBridge bridge_spec( - *lockingChainDoor, - lockingChainIssue, - *issuingChainDoor, - issuingChainIssue); - Keylet keylet = keylet::xChainClaimID(bridge_spec, seq); - uNodeIndex = keylet.key; - } - } + if (valid && claim_id[jss::xchain_owned_claim_id].isIntegral()) + { + auto seq = claim_id[jss::xchain_owned_claim_id].asUInt(); + + STXChainBridge bridge_spec( + *lockingChainDoor, + lockingChainIssue, + *issuingChainDoor, + issuingChainIssue); + Keylet keylet = keylet::xChainClaimID(bridge_spec, seq); + return keylet.key; + } + + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; +} + +std::optional +parseXChainOwnedCreateAccountClaimID( + Json::Value const& claim_id, + Json::Value& jvResult) +{ + if (claim_id.isString()) + { + uint256 uNodeIndex; + // we accept a node id as specifier of a xchain create account + // claim_id + if (!uNodeIndex.parseHex(claim_id.asString())) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember( - jss::xchain_owned_create_account_claim_id)) + return uNodeIndex; + } + + if (!claim_id.isObject() || + !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) && + claim_id[sfIssuingChainDoor.getJsonName()].isString()) || + !(claim_id.isMember(sfLockingChainDoor.getJsonName()) && + claim_id[sfLockingChainDoor.getJsonName()].isString()) || + !claim_id.isMember(sfIssuingChainIssue.getJsonName()) || + !claim_id.isMember(sfLockingChainIssue.getJsonName()) || + !claim_id.isMember(jss::xchain_owned_create_account_claim_id)) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + + // if not specified with a node id, a create account claim_id is + // specified by four strings defining the bridge + // (locking_chain_door, locking_chain_issue, issuing_chain_door, + // issuing_chain_issue) and the create account claim id sequence + // number. + auto lockingChainDoor = parseBase58( + claim_id[sfLockingChainDoor.getJsonName()].asString()); + auto issuingChainDoor = parseBase58( + claim_id[sfIssuingChainDoor.getJsonName()].asString()); + Issue lockingChainIssue, issuingChainIssue; + bool valid = lockingChainDoor && issuingChainDoor; + if (valid) + { + try { - // see object definition in LedgerFormats.cpp - expectedType = ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID; - auto& claim_id = - context.params[jss::xchain_owned_create_account_claim_id]; - if (claim_id.isString()) - { - // we accept a node id as specifier of a xchain create account - // claim_id - if (!uNodeIndex.parseHex(claim_id.asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - !claim_id.isObject() || - !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) && - claim_id[sfIssuingChainDoor.getJsonName()].isString()) || - !(claim_id.isMember(sfLockingChainDoor.getJsonName()) && - claim_id[sfLockingChainDoor.getJsonName()].isString()) || - !claim_id.isMember(sfIssuingChainIssue.getJsonName()) || - !claim_id.isMember(sfLockingChainIssue.getJsonName()) || - !claim_id.isMember(jss::xchain_owned_create_account_claim_id)) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - // if not specified with a node id, a create account claim_id is - // specified by four strings defining the bridge - // (locking_chain_door, locking_chain_issue, issuing_chain_door, - // issuing_chain_issue) and the create account claim id sequence - // number. - auto lockingChainDoor = parseBase58( - claim_id[sfLockingChainDoor.getJsonName()].asString()); - auto issuingChainDoor = parseBase58( - claim_id[sfIssuingChainDoor.getJsonName()].asString()); - Issue lockingChainIssue, issuingChainIssue; - bool valid = lockingChainDoor && issuingChainDoor; - if (valid) - { - try - { - lockingChainIssue = issueFromJson( - claim_id[sfLockingChainIssue.getJsonName()]); - issuingChainIssue = issueFromJson( - claim_id[sfIssuingChainIssue.getJsonName()]); - } - catch (std::runtime_error const& ex) - { - valid = false; - jvResult[jss::error] = "malformedRequest"; - } - } + lockingChainIssue = + issueFromJson(claim_id[sfLockingChainIssue.getJsonName()]); + issuingChainIssue = + issueFromJson(claim_id[sfIssuingChainIssue.getJsonName()]); + } + catch (std::runtime_error const& ex) + { + valid = false; + jvResult[jss::error] = "malformedRequest"; + } + } - if (valid && - claim_id[jss::xchain_owned_create_account_claim_id] - .isIntegral()) - { - auto seq = - claim_id[jss::xchain_owned_create_account_claim_id] - .asUInt(); - - STXChainBridge bridge_spec( - *lockingChainDoor, - lockingChainIssue, - *issuingChainDoor, - issuingChainIssue); - Keylet keylet = - keylet::xChainCreateAccountClaimID(bridge_spec, seq); - uNodeIndex = keylet.key; - } - } + if (valid && + claim_id[jss::xchain_owned_create_account_claim_id].isIntegral()) + { + auto seq = claim_id[jss::xchain_owned_create_account_claim_id].asUInt(); + + STXChainBridge bridge_spec( + *lockingChainDoor, + lockingChainIssue, + *issuingChainDoor, + issuingChainIssue); + Keylet keylet = keylet::xChainCreateAccountClaimID(bridge_spec, seq); + return keylet.key; + } + + return std::nullopt; +} + +std::optional +parseDID(Json::Value const& params, Json::Value& jvResult) +{ + auto const account = parseBase58(params.asString()); + if (!account || account->isZero()) + { + jvResult[jss::error] = "malformedAddress"; + return std::nullopt; + } + + return keylet::did(*account).key; +} + +std::optional +parseOracle(Json::Value const& params, Json::Value& jvResult) +{ + if (!params.isObject()) + { + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(params.asString())) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::did)) + return uNodeIndex; + } + else if ( + !params.isMember(jss::oracle_document_id) || + !params.isMember(jss::account)) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + else + { + auto const& oracle = params; + auto const documentID = [&]() -> std::optional { + auto const& id = oracle[jss::oracle_document_id]; + if (id.isUInt() || (id.isInt() && id.asInt() >= 0)) + return std::make_optional(id.asUInt()); + else if (id.isString()) + { + std::uint32_t v; + if (beast::lexicalCastChecked(v, id.asString())) + return std::make_optional(v); + } + return std::nullopt; + }(); + + auto const account = + parseBase58(oracle[jss::account].asString()); + if (!account || account->isZero()) { - expectedType = ltDID; - auto const account = - parseBase58(context.params[jss::did].asString()); - if (!account || account->isZero()) - jvResult[jss::error] = "malformedAddress"; - else - uNodeIndex = keylet::did(*account).key; + jvResult[jss::error] = "malformedAddress"; + return std::nullopt; } - else if (context.params.isMember(jss::oracle)) + + if (!documentID) { - expectedType = ltORACLE; - if (!context.params[jss::oracle].isObject()) - { - if (!uNodeIndex.parseHex( - context.params[jss::oracle].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - !context.params[jss::oracle].isMember( - jss::oracle_document_id) || - !context.params[jss::oracle].isMember(jss::account)) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - uNodeIndex = beast::zero; - auto const& oracle = context.params[jss::oracle]; - auto const documentID = [&]() -> std::optional { - auto const& id = oracle[jss::oracle_document_id]; - if (id.isUInt() || (id.isInt() && id.asInt() >= 0)) - return std::make_optional(id.asUInt()); - else if (id.isString()) - { - std::uint32_t v; - if (beast::lexicalCastChecked(v, id.asString())) - return std::make_optional(v); - } - return std::nullopt; - }(); - auto const account = - parseBase58(oracle[jss::account].asString()); - if (!account || account->isZero()) - jvResult[jss::error] = "malformedAddress"; - else if (!documentID) - jvResult[jss::error] = "malformedDocumentID"; - else - uNodeIndex = keylet::oracle(*account, *documentID).key; - } + jvResult[jss::error] = "malformedDocumentID"; + return std::nullopt; } - else if (context.params.isMember(jss::credential)) + + return keylet::oracle(*account, *documentID).key; + } +} + +std::optional +parseCredential(Json::Value const& cred, Json::Value& jvResult) +{ + if (cred.isString()) + { + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(cred.asString())) { - expectedType = ltCREDENTIAL; - auto const& cred = context.params[jss::credential]; + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + return uNodeIndex; + } - if (cred.isString()) - { - if (!uNodeIndex.parseHex(cred.asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - (!cred.isMember(jss::subject) || - !cred[jss::subject].isString()) || - (!cred.isMember(jss::issuer) || - !cred[jss::issuer].isString()) || - (!cred.isMember(jss::credential_type) || - !cred[jss::credential_type].isString())) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - auto const subject = - parseBase58(cred[jss::subject].asString()); - auto const issuer = - parseBase58(cred[jss::issuer].asString()); - auto const credType = - strUnHex(cred[jss::credential_type].asString()); - if (!subject || subject->isZero() || !issuer || - issuer->isZero() || !credType || credType->empty()) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - uNodeIndex = keylet::credential( - *subject, - *issuer, - Slice(credType->data(), credType->size())) - .key; - } - } + if ((!cred.isMember(jss::subject) || !cred[jss::subject].isString()) || + (!cred.isMember(jss::issuer) || !cred[jss::issuer].isString()) || + (!cred.isMember(jss::credential_type) || + !cred[jss::credential_type].isString())) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + auto const subject = parseBase58(cred[jss::subject].asString()); + auto const issuer = parseBase58(cred[jss::issuer].asString()); + auto const credType = strUnHex(cred[jss::credential_type].asString()); + if (!subject || subject->isZero() || !issuer || issuer->isZero() || + !credType || credType->empty()) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + + return keylet::credential( + *subject, *issuer, Slice(credType->data(), credType->size())) + .key; +} + +std::optional +parseMPTokenIssuance( + Json::Value const& unparsedMPTIssuanceID, + Json::Value& jvResult) +{ + if (unparsedMPTIssuanceID.isString()) + { + uint192 mptIssuanceID; + if (!mptIssuanceID.parseHex(unparsedMPTIssuanceID.asString())) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::mpt_issuance)) + + return keylet::mptIssuance(mptIssuanceID).key; + } + + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; +} + +std::optional +parseMPToken(Json::Value const& mptJson, Json::Value& jvResult) +{ + if (!mptJson.isObject()) + { + uint256 uNodeIndex; + if (!uNodeIndex.parseHex(mptJson.asString())) { - expectedType = ltMPTOKEN_ISSUANCE; - auto const unparsedMPTIssuanceID = - context.params[jss::mpt_issuance]; - if (unparsedMPTIssuanceID.isString()) - { - uint192 mptIssuanceID; - if (!mptIssuanceID.parseHex(unparsedMPTIssuanceID.asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - else - uNodeIndex = keylet::mptIssuance(mptIssuanceID).key; - } - else - { - jvResult[jss::error] = "malformedRequest"; - } + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; } - else if (context.params.isMember(jss::mptoken)) + return uNodeIndex; + } + + if (!mptJson.isMember(jss::mpt_issuance_id) || + !mptJson.isMember(jss::account)) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } + try + { + auto const mptIssuanceIdStr = mptJson[jss::mpt_issuance_id].asString(); + + uint192 mptIssuanceID; + if (!mptIssuanceID.parseHex(mptIssuanceIdStr)) + Throw("Cannot parse mpt_issuance_id"); + + auto const account = + parseBase58(mptJson[jss::account].asString()); + + if (!account || account->isZero()) { - expectedType = ltMPTOKEN; - auto const& mptJson = context.params[jss::mptoken]; - if (!mptJson.isObject()) - { - if (!uNodeIndex.parseHex(mptJson.asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - !mptJson.isMember(jss::mpt_issuance_id) || - !mptJson.isMember(jss::account)) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - try - { - auto const mptIssuanceIdStr = - mptJson[jss::mpt_issuance_id].asString(); - - uint192 mptIssuanceID; - if (!mptIssuanceID.parseHex(mptIssuanceIdStr)) - Throw( - "Cannot parse mpt_issuance_id"); - - auto const account = parseBase58( - mptJson[jss::account].asString()); - - if (!account || account->isZero()) - jvResult[jss::error] = "malformedAddress"; - else - uNodeIndex = - keylet::mptoken(mptIssuanceID, *account).key; - } - catch (std::runtime_error const&) - { - jvResult[jss::error] = "malformedRequest"; - } - } + jvResult[jss::error] = "malformedAddress"; + return std::nullopt; } - else + + return keylet::mptoken(mptIssuanceID, *account).key; + } + catch (std::runtime_error const&) + { + jvResult[jss::error] = "malformedRequest"; + return std::nullopt; + } +} + +using FunctionType = + std::function(Json::Value const&, Json::Value&)>; + +struct LedgerEntry +{ + Json::StaticString fieldName; + FunctionType parseFunction; + LedgerEntryType expectedType; +}; + +// { +// ledger_hash : +// ledger_index : +// ... +// } +Json::Value +doLedgerEntry(RPC::JsonContext& context) +{ + std::shared_ptr lpLedger; + auto jvResult = RPC::lookupLedger(lpLedger, context); + + if (!lpLedger) + return jvResult; + + static LedgerEntry ledgerEntryParsers[] = { + {jss::index, parseIndex, ltANY}, + {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, + // TODO: add amendments + {jss::amm, parseAMM, ltAMM}, + {jss::bridge, parseBridge, ltBRIDGE}, + {jss::check, parseCheck, ltCHECK}, + {jss::credential, parseCredential, ltCREDENTIAL}, + {jss::deposit_preauth, parseDepositPreauth, ltDEPOSIT_PREAUTH}, + {jss::did, parseDID, ltDID}, + {jss::directory, parseDirectory, ltDIR_NODE}, + {jss::escrow, parseEscrow, ltESCROW}, + // TODO: add fee, hashes + {jss::mpt_issuance, parseMPTokenIssuance, ltMPTOKEN_ISSUANCE}, + {jss::mptoken, parseMPToken, ltMPTOKEN}, + // TODO: add NFT Offers + {jss::nft_page, parseNFTokenPage, ltNFTOKEN_PAGE}, + // TODO: add NegativeUNL + {jss::offer, parseOffer, ltOFFER}, + {jss::oracle, parseOracle, ltORACLE}, + {jss::payment_channel, parsePaymentChannel, ltPAYCHAN}, + {jss::ripple_state, parseRippleState, ltRIPPLE_STATE}, + // This is an alias, since the `ledger_data` filter uses jss::state + {jss::state, parseRippleState, ltRIPPLE_STATE}, + {jss::ticket, parseTicket, ltTICKET}, + {jss::xchain_owned_claim_id, + parseXChainOwnedClaimID, + ltXCHAIN_OWNED_CLAIM_ID}, + {jss::xchain_owned_create_account_claim_id, + parseXChainOwnedCreateAccountClaimID, + ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID}, + }; + + uint256 uNodeIndex; + LedgerEntryType expectedType = ltANY; + + try + { + bool found = false; + for (const auto& ledgerEntry : ledgerEntryParsers) { - if (context.params.isMember("params") && - context.params["params"].isArray() && - context.params["params"].size() == 1 && - context.params["params"][0u].isString()) + if (context.params.isMember(ledgerEntry.fieldName)) { - if (!uNodeIndex.parseHex( - context.params["params"][0u].asString())) + expectedType = ledgerEntry.expectedType; + // `Bridge` is the only type that involves two fields at the + // `ledger_entry` param level. + // So that parser needs to have the whole `params` field. + // All other parsers only need the one field name's info. + Json::Value const& params = ledgerEntry.fieldName == jss::bridge + ? context.params + : context.params[ledgerEntry.fieldName]; + uNodeIndex = ledgerEntry.parseFunction(params, jvResult) + .value_or(beast::zero); + if (jvResult.isMember(jss::error)) { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; + return jvResult; } + found = true; } + } + if (!found) + { + if (context.apiVersion < 2u) + jvResult[jss::error] = "unknownOption"; else - { - if (context.apiVersion < 2u) - jvResult[jss::error] = "unknownOption"; - else - jvResult[jss::error] = "invalidParams"; - } + jvResult[jss::error] = "invalidParams"; + return jvResult; } } catch (Json::error& e) @@ -837,43 +889,51 @@ doLedgerEntry(RPC::JsonContext& context) // For apiVersion 2 onwards, any parsing failures that throw // this // exception return an invalidParam error. - uNodeIndex = beast::zero; jvResult[jss::error] = "invalidParams"; + return jvResult; } else throw; } - if (uNodeIndex.isNonZero()) + if (uNodeIndex.isZero()) { - auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex)); - if (context.params.isMember(jss::binary)) - bNodeBinary = context.params[jss::binary].asBool(); + jvResult[jss::error] = "entryNotFound"; + return jvResult; + } - if (!sleNode) - { - // Not found. - jvResult[jss::error] = "entryNotFound"; - } - else if ( - (expectedType != ltANY) && (expectedType != sleNode->getType())) - { - jvResult[jss::error] = "unexpectedLedgerType"; - } - else if (bNodeBinary) - { - Serializer s; + auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex)); - sleNode->add(s); + bool bNodeBinary = false; + if (context.params.isMember(jss::binary)) + bNodeBinary = context.params[jss::binary].asBool(); - jvResult[jss::node_binary] = strHex(s.peekData()); - jvResult[jss::index] = to_string(uNodeIndex); - } - else - { - jvResult[jss::node] = sleNode->getJson(JsonOptions::none); - jvResult[jss::index] = to_string(uNodeIndex); - } + if (!sleNode) + { + // Not found. + jvResult[jss::error] = "entryNotFound"; + return jvResult; + } + + if ((expectedType != ltANY) && (expectedType != sleNode->getType())) + { + jvResult[jss::error] = "unexpectedLedgerType"; + return jvResult; + } + + if (bNodeBinary) + { + Serializer s; + + sleNode->add(s); + + jvResult[jss::node_binary] = strHex(s.peekData()); + jvResult[jss::index] = to_string(uNodeIndex); + } + else + { + jvResult[jss::node] = sleNode->getJson(JsonOptions::none); + jvResult[jss::index] = to_string(uNodeIndex); } return jvResult; From 3d5ef0fb972cf519f9c890126e2364e5316a014e Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 22 Nov 2024 13:52:52 -0500 Subject: [PATCH 2/5] respond to comments --- src/xrpld/rpc/handlers/LedgerEntry.cpp | 148 ++++++++++++++----------- 1 file changed, 83 insertions(+), 65 deletions(-) diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index d3e9af4c299..467e94952a5 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -76,6 +76,7 @@ parseIndex(Json::Value const& params, Json::Value& jvResult) jvResult[jss::error] = "malformedRequest"; return std::nullopt; } + return uNodeIndex; } @@ -88,6 +89,7 @@ parseAccountRoot(Json::Value const& params, Json::Value& jvResult) jvResult[jss::error] = "malformedAddress"; return std::nullopt; } + return keylet::account(*account).key; } @@ -100,6 +102,7 @@ parseCheck(Json::Value const& params, Json::Value& jvResult) jvResult[jss::error] = "malformedRequest"; return std::nullopt; } + return uNodeIndex; } @@ -116,6 +119,7 @@ parseDepositPreauth(Json::Value const& dp, Json::Value& jvResult) } return uNodeIndex; } + // clang-format off if ( (!dp.isMember(jss::owner) || !dp[jss::owner].isString()) || @@ -135,6 +139,7 @@ parseDepositPreauth(Json::Value const& dp, Json::Value& jvResult) jvResult[jss::error] = "malformedOwner"; return std::nullopt; } + if (dp.isMember(jss::authorized)) { auto const authorized = @@ -155,7 +160,8 @@ parseDepositPreauth(Json::Value const& dp, Json::Value& jvResult) jvResult[jss::error] = "malformedAuthorizedCredentials"; return std::nullopt; } - auto sorted = credentials::makeSorted(arr); + + auto const& sorted = credentials::makeSorted(arr); if (sorted.empty()) { jvResult[jss::error] = "malformedAuthorizedCredentials"; @@ -275,6 +281,7 @@ parseOffer(Json::Value const& params, Json::Value& jvResult) } return uNodeIndex; } + if (!params.isMember(jss::account) || !params.isMember(jss::seq) || !params[jss::seq].isIntegral()) { @@ -301,6 +308,7 @@ parsePaymentChannel(Json::Value const& params, Json::Value& jvResult) jvResult[jss::error] = "malformedRequest"; return std::nullopt; } + return uNodeIndex; } @@ -439,6 +447,7 @@ parseBridge(Json::Value const& params, Json::Value& jvResult) { return std::nullopt; } + auto const account = parseBase58(jsBridgeAccount.asString()); if (!account || account->isZero()) @@ -452,6 +461,7 @@ parseBridge(Json::Value const& params, Json::Value& jvResult) STXChainBridge const bridge(params[jss::bridge]); STXChainBridge::ChainType const chainType = STXChainBridge::srcChain(account == bridge.lockingChainDoor()); + if (account != bridge.door(chainType)) return std::nullopt; @@ -504,12 +514,13 @@ parseXChainOwnedClaimID(Json::Value const& claim_id, Json::Value& jvResult) // four strings defining the bridge (locking_chain_door, // locking_chain_issue, issuing_chain_door, issuing_chain_issue) // and the claim id sequence number. - auto lockingChainDoor = parseBase58( + auto const lockingChainDoor = parseBase58( claim_id[sfLockingChainDoor.getJsonName()].asString()); - auto issuingChainDoor = parseBase58( + auto const issuingChainDoor = parseBase58( claim_id[sfIssuingChainDoor.getJsonName()].asString()); Issue lockingChainIssue, issuingChainIssue; bool valid = lockingChainDoor && issuingChainDoor; + if (valid) { try @@ -528,7 +539,7 @@ parseXChainOwnedClaimID(Json::Value const& claim_id, Json::Value& jvResult) if (valid && claim_id[jss::xchain_owned_claim_id].isIntegral()) { - auto seq = claim_id[jss::xchain_owned_claim_id].asUInt(); + auto const seq = claim_id[jss::xchain_owned_claim_id].asUInt(); STXChainBridge bridge_spec( *lockingChainDoor, @@ -579,9 +590,9 @@ parseXChainOwnedCreateAccountClaimID( // (locking_chain_door, locking_chain_issue, issuing_chain_door, // issuing_chain_issue) and the create account claim id sequence // number. - auto lockingChainDoor = parseBase58( + auto const lockingChainDoor = parseBase58( claim_id[sfLockingChainDoor.getJsonName()].asString()); - auto issuingChainDoor = parseBase58( + auto const issuingChainDoor = parseBase58( claim_id[sfIssuingChainDoor.getJsonName()].asString()); Issue lockingChainIssue, issuingChainIssue; bool valid = lockingChainDoor && issuingChainDoor; @@ -604,7 +615,8 @@ parseXChainOwnedCreateAccountClaimID( if (valid && claim_id[jss::xchain_owned_create_account_claim_id].isIntegral()) { - auto seq = claim_id[jss::xchain_owned_create_account_claim_id].asUInt(); + auto const seq = + claim_id[jss::xchain_owned_create_account_claim_id].asUInt(); STXChainBridge bridge_spec( *lockingChainDoor, @@ -644,45 +656,45 @@ parseOracle(Json::Value const& params, Json::Value& jvResult) } return uNodeIndex; } - else if ( - !params.isMember(jss::oracle_document_id) || + + if (!params.isMember(jss::oracle_document_id) || !params.isMember(jss::account)) { jvResult[jss::error] = "malformedRequest"; return std::nullopt; } - else - { - auto const& oracle = params; - auto const documentID = [&]() -> std::optional { - auto const& id = oracle[jss::oracle_document_id]; - if (id.isUInt() || (id.isInt() && id.asInt() >= 0)) - return std::make_optional(id.asUInt()); - else if (id.isString()) - { - std::uint32_t v; - if (beast::lexicalCastChecked(v, id.asString())) - return std::make_optional(v); - } - return std::nullopt; - }(); - auto const account = - parseBase58(oracle[jss::account].asString()); - if (!account || account->isZero()) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } + auto const& oracle = params; + auto const documentID = [&]() -> std::optional { + auto const id = oracle[jss::oracle_document_id]; + if (id.isUInt() || (id.isInt() && id.asInt() >= 0)) + return std::make_optional(id.asUInt()); - if (!documentID) + if (id.isString()) { - jvResult[jss::error] = "malformedDocumentID"; - return std::nullopt; + std::uint32_t v; + if (beast::lexicalCastChecked(v, id.asString())) + return std::make_optional(v); } - return keylet::oracle(*account, *documentID).key; + return std::nullopt; + }(); + + auto const account = + parseBase58(oracle[jss::account].asString()); + if (!account || account->isZero()) + { + jvResult[jss::error] = "malformedAddress"; + return std::nullopt; + } + + if (!documentID) + { + jvResult[jss::error] = "malformedDocumentID"; + return std::nullopt; } + + return keylet::oracle(*account, *documentID).key; } std::optional @@ -707,9 +719,11 @@ parseCredential(Json::Value const& cred, Json::Value& jvResult) jvResult[jss::error] = "malformedRequest"; return std::nullopt; } + auto const subject = parseBase58(cred[jss::subject].asString()); auto const issuer = parseBase58(cred[jss::issuer].asString()); auto const credType = strUnHex(cred[jss::credential_type].asString()); + if (!subject || subject->isZero() || !issuer || issuer->isZero() || !credType || credType->empty()) { @@ -763,6 +777,7 @@ parseMPToken(Json::Value const& mptJson, Json::Value& jvResult) jvResult[jss::error] = "malformedRequest"; return std::nullopt; } + try { auto const mptIssuanceIdStr = mptJson[jss::mpt_issuance_id].asString(); @@ -790,7 +805,7 @@ parseMPToken(Json::Value const& mptJson, Json::Value& jvResult) } using FunctionType = - std::function(Json::Value const&, Json::Value&)>; + std::optional (*)(Json::Value const&, Json::Value&); struct LedgerEntry { @@ -813,37 +828,41 @@ doLedgerEntry(RPC::JsonContext& context) if (!lpLedger) return jvResult; - static LedgerEntry ledgerEntryParsers[] = { - {jss::index, parseIndex, ltANY}, - {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, + static std::array ledgerEntryParsers = { + LedgerEntry{jss::index, parseIndex, ltANY}, + LedgerEntry{jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, // TODO: add amendments - {jss::amm, parseAMM, ltAMM}, - {jss::bridge, parseBridge, ltBRIDGE}, - {jss::check, parseCheck, ltCHECK}, - {jss::credential, parseCredential, ltCREDENTIAL}, - {jss::deposit_preauth, parseDepositPreauth, ltDEPOSIT_PREAUTH}, - {jss::did, parseDID, ltDID}, - {jss::directory, parseDirectory, ltDIR_NODE}, - {jss::escrow, parseEscrow, ltESCROW}, + LedgerEntry{jss::amm, parseAMM, ltAMM}, + LedgerEntry{jss::bridge, parseBridge, ltBRIDGE}, + LedgerEntry{jss::check, parseCheck, ltCHECK}, + LedgerEntry{jss::credential, parseCredential, ltCREDENTIAL}, + LedgerEntry{ + jss::deposit_preauth, parseDepositPreauth, ltDEPOSIT_PREAUTH}, + LedgerEntry{jss::did, parseDID, ltDID}, + LedgerEntry{jss::directory, parseDirectory, ltDIR_NODE}, + LedgerEntry{jss::escrow, parseEscrow, ltESCROW}, // TODO: add fee, hashes - {jss::mpt_issuance, parseMPTokenIssuance, ltMPTOKEN_ISSUANCE}, - {jss::mptoken, parseMPToken, ltMPTOKEN}, + LedgerEntry{ + jss::mpt_issuance, parseMPTokenIssuance, ltMPTOKEN_ISSUANCE}, + LedgerEntry{jss::mptoken, parseMPToken, ltMPTOKEN}, // TODO: add NFT Offers - {jss::nft_page, parseNFTokenPage, ltNFTOKEN_PAGE}, + LedgerEntry{jss::nft_page, parseNFTokenPage, ltNFTOKEN_PAGE}, // TODO: add NegativeUNL - {jss::offer, parseOffer, ltOFFER}, - {jss::oracle, parseOracle, ltORACLE}, - {jss::payment_channel, parsePaymentChannel, ltPAYCHAN}, - {jss::ripple_state, parseRippleState, ltRIPPLE_STATE}, + LedgerEntry{jss::offer, parseOffer, ltOFFER}, + LedgerEntry{jss::oracle, parseOracle, ltORACLE}, + LedgerEntry{jss::payment_channel, parsePaymentChannel, ltPAYCHAN}, + LedgerEntry{jss::ripple_state, parseRippleState, ltRIPPLE_STATE}, // This is an alias, since the `ledger_data` filter uses jss::state - {jss::state, parseRippleState, ltRIPPLE_STATE}, - {jss::ticket, parseTicket, ltTICKET}, - {jss::xchain_owned_claim_id, - parseXChainOwnedClaimID, - ltXCHAIN_OWNED_CLAIM_ID}, - {jss::xchain_owned_create_account_claim_id, - parseXChainOwnedCreateAccountClaimID, - ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID}, + LedgerEntry{jss::state, parseRippleState, ltRIPPLE_STATE}, + LedgerEntry{jss::ticket, parseTicket, ltTICKET}, + LedgerEntry{ + jss::xchain_owned_claim_id, + parseXChainOwnedClaimID, + ltXCHAIN_OWNED_CLAIM_ID}, + LedgerEntry{ + jss::xchain_owned_create_account_claim_id, + parseXChainOwnedCreateAccountClaimID, + ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID}, }; uint256 uNodeIndex; @@ -886,8 +905,7 @@ doLedgerEntry(RPC::JsonContext& context) { if (context.apiVersion > 1u) { - // For apiVersion 2 onwards, any parsing failures that throw - // this + // For apiVersion 2 onwards, any parsing failures that throw this // exception return an invalidParam error. jvResult[jss::error] = "invalidParams"; return jvResult; @@ -964,7 +982,7 @@ doLedgerEntryGrpc( return {response, errorStatus}; } - auto key = uint256::fromVoidChecked(request.key()); + auto const key = uint256::fromVoidChecked(request.key()); if (!key) { grpc::Status errorStatus{ From 04fd6e3ff0c7e89b023c55b008638cb9c03ca2f5 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 22 Nov 2024 14:09:01 -0500 Subject: [PATCH 3/5] add more spacing --- src/xrpld/rpc/handlers/LedgerEntry.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 467e94952a5..aab04b39ec8 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -210,6 +210,7 @@ parseDirectory(Json::Value const& params, Json::Value& jvResult) jvResult[jss::error] = "malformedRequest"; return std::nullopt; } + if (!uDirRoot.parseHex(params[jss::dir_root].asString())) { jvResult[jss::error] = "malformedRequest"; @@ -228,6 +229,7 @@ parseDirectory(Json::Value const& params, Json::Value& jvResult) jvResult[jss::error] = "malformedAddress"; return std::nullopt; } + return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key; } @@ -243,10 +245,10 @@ parseEscrow(Json::Value const& params, Json::Value& jvResult) uint256 uNodeIndex; if (!uNodeIndex.parseHex(params.asString())) { - uNodeIndex = beast::zero; jvResult[jss::error] = "malformedRequest"; return std::nullopt; } + return uNodeIndex; } From c255d460b42ac22e26ad29f94bd80dea97847c49 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 22 Nov 2024 15:38:40 -0500 Subject: [PATCH 4/5] more cleanup --- src/xrpld/rpc/handlers/LedgerEntry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index aab04b39ec8..cf42aab4eac 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -830,7 +830,7 @@ doLedgerEntry(RPC::JsonContext& context) if (!lpLedger) return jvResult; - static std::array ledgerEntryParsers = { + static std::array ledgerEntryParsers = { LedgerEntry{jss::index, parseIndex, ltANY}, LedgerEntry{jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, // TODO: add amendments From ee270ab4ba78e68cc510b6ffe7e256e75375a857 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 22 Nov 2024 16:42:59 -0500 Subject: [PATCH 5/5] use std::to_array --- src/xrpld/rpc/handlers/LedgerEntry.cpp | 58 ++++++++++++-------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index cf42aab4eac..18d6fac7bda 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -830,42 +830,38 @@ doLedgerEntry(RPC::JsonContext& context) if (!lpLedger) return jvResult; - static std::array ledgerEntryParsers = { - LedgerEntry{jss::index, parseIndex, ltANY}, - LedgerEntry{jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, + static auto ledgerEntryParsers = std::to_array({ + {jss::index, parseIndex, ltANY}, + {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, // TODO: add amendments - LedgerEntry{jss::amm, parseAMM, ltAMM}, - LedgerEntry{jss::bridge, parseBridge, ltBRIDGE}, - LedgerEntry{jss::check, parseCheck, ltCHECK}, - LedgerEntry{jss::credential, parseCredential, ltCREDENTIAL}, - LedgerEntry{ - jss::deposit_preauth, parseDepositPreauth, ltDEPOSIT_PREAUTH}, - LedgerEntry{jss::did, parseDID, ltDID}, - LedgerEntry{jss::directory, parseDirectory, ltDIR_NODE}, - LedgerEntry{jss::escrow, parseEscrow, ltESCROW}, + {jss::amm, parseAMM, ltAMM}, + {jss::bridge, parseBridge, ltBRIDGE}, + {jss::check, parseCheck, ltCHECK}, + {jss::credential, parseCredential, ltCREDENTIAL}, + {jss::deposit_preauth, parseDepositPreauth, ltDEPOSIT_PREAUTH}, + {jss::did, parseDID, ltDID}, + {jss::directory, parseDirectory, ltDIR_NODE}, + {jss::escrow, parseEscrow, ltESCROW}, // TODO: add fee, hashes - LedgerEntry{ - jss::mpt_issuance, parseMPTokenIssuance, ltMPTOKEN_ISSUANCE}, - LedgerEntry{jss::mptoken, parseMPToken, ltMPTOKEN}, + {jss::mpt_issuance, parseMPTokenIssuance, ltMPTOKEN_ISSUANCE}, + {jss::mptoken, parseMPToken, ltMPTOKEN}, // TODO: add NFT Offers - LedgerEntry{jss::nft_page, parseNFTokenPage, ltNFTOKEN_PAGE}, + {jss::nft_page, parseNFTokenPage, ltNFTOKEN_PAGE}, // TODO: add NegativeUNL - LedgerEntry{jss::offer, parseOffer, ltOFFER}, - LedgerEntry{jss::oracle, parseOracle, ltORACLE}, - LedgerEntry{jss::payment_channel, parsePaymentChannel, ltPAYCHAN}, - LedgerEntry{jss::ripple_state, parseRippleState, ltRIPPLE_STATE}, + {jss::offer, parseOffer, ltOFFER}, + {jss::oracle, parseOracle, ltORACLE}, + {jss::payment_channel, parsePaymentChannel, ltPAYCHAN}, + {jss::ripple_state, parseRippleState, ltRIPPLE_STATE}, // This is an alias, since the `ledger_data` filter uses jss::state - LedgerEntry{jss::state, parseRippleState, ltRIPPLE_STATE}, - LedgerEntry{jss::ticket, parseTicket, ltTICKET}, - LedgerEntry{ - jss::xchain_owned_claim_id, - parseXChainOwnedClaimID, - ltXCHAIN_OWNED_CLAIM_ID}, - LedgerEntry{ - jss::xchain_owned_create_account_claim_id, - parseXChainOwnedCreateAccountClaimID, - ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID}, - }; + {jss::state, parseRippleState, ltRIPPLE_STATE}, + {jss::ticket, parseTicket, ltTICKET}, + {jss::xchain_owned_claim_id, + parseXChainOwnedClaimID, + ltXCHAIN_OWNED_CLAIM_ID}, + {jss::xchain_owned_create_account_claim_id, + parseXChainOwnedCreateAccountClaimID, + ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID}, + }); uint256 uNodeIndex; LedgerEntryType expectedType = ltANY;