diff --git a/src/rpc/handlers/AMMInfo.cpp b/src/rpc/handlers/AMMInfo.cpp index bc5ed56b..d2af7498 100644 --- a/src/rpc/handlers/AMMInfo.cpp +++ b/src/rpc/handlers/AMMInfo.cpp @@ -316,8 +316,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::value if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(asset))) input.issue1 = parseIssue(jsonObject.at(JS(asset)).as_object()); diff --git a/src/rpc/handlers/AccountChannels.cpp b/src/rpc/handlers/AccountChannels.cpp index 49f9fbe4..1fb38b4b 100644 --- a/src/rpc/handlers/AccountChannels.cpp +++ b/src/rpc/handlers/AccountChannels.cpp @@ -154,8 +154,11 @@ tag_invoke(boost::json::value_to_tag, boost::json if (jsonObject.contains(JS(destination_account))) input.destinationAccount = boost::json::value_to(jv.at(JS(destination_account))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/AccountCurrencies.cpp b/src/rpc/handlers/AccountCurrencies.cpp index 78a2ce20..d23fbe68 100644 --- a/src/rpc/handlers/AccountCurrencies.cpp +++ b/src/rpc/handlers/AccountCurrencies.cpp @@ -128,8 +128,11 @@ tag_invoke(boost::json::value_to_tag, boost::js if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/AccountInfo.cpp b/src/rpc/handlers/AccountInfo.cpp index d855fdba..8631b563 100644 --- a/src/rpc/handlers/AccountInfo.cpp +++ b/src/rpc/handlers/AccountInfo.cpp @@ -204,8 +204,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::va if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(signer_lists))) input.signerLists = boost::json::value_to(jsonObject.at(JS(signer_lists))); diff --git a/src/rpc/handlers/AccountLines.cpp b/src/rpc/handlers/AccountLines.cpp index 8b3772a9..159089b9 100644 --- a/src/rpc/handlers/AccountLines.cpp +++ b/src/rpc/handlers/AccountLines.cpp @@ -215,8 +215,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::v if (jsonObject.contains(JS(ignore_default))) input.ignoreDefault = jv.at(JS(ignore_default)).as_bool(); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/AccountMPTokenIssuances.cpp b/src/rpc/handlers/AccountMPTokenIssuances.cpp index 85c20586..85c946ec 100644 --- a/src/rpc/handlers/AccountMPTokenIssuances.cpp +++ b/src/rpc/handlers/AccountMPTokenIssuances.cpp @@ -164,8 +164,11 @@ tag_invoke(boost::json::value_to_tag, boo if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/AccountMPTokens.cpp b/src/rpc/handlers/AccountMPTokens.cpp index 3663eebc..ff5529c8 100644 --- a/src/rpc/handlers/AccountMPTokens.cpp +++ b/src/rpc/handlers/AccountMPTokens.cpp @@ -139,8 +139,11 @@ tag_invoke(boost::json::value_to_tag, boost::json if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/AccountNFTs.cpp b/src/rpc/handlers/AccountNFTs.cpp index 7d2dde8d..d16ccebe 100644 --- a/src/rpc/handlers/AccountNFTs.cpp +++ b/src/rpc/handlers/AccountNFTs.cpp @@ -157,8 +157,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::va if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(limit))) input.limit = util::integralValueAs(jsonObject.at(JS(limit))); diff --git a/src/rpc/handlers/AccountObjects.cpp b/src/rpc/handlers/AccountObjects.cpp index 74d59b44..323b3e3c 100644 --- a/src/rpc/handlers/AccountObjects.cpp +++ b/src/rpc/handlers/AccountObjects.cpp @@ -153,8 +153,11 @@ tag_invoke(boost::json::value_to_tag, boost::json: if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(type))) { input.type = diff --git a/src/rpc/handlers/AccountOffers.cpp b/src/rpc/handlers/AccountOffers.cpp index 7ded2b9f..2d987678 100644 --- a/src/rpc/handlers/AccountOffers.cpp +++ b/src/rpc/handlers/AccountOffers.cpp @@ -169,8 +169,11 @@ tag_invoke(boost::json::value_to_tag, boost::json:: if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(limit))) input.limit = util::integralValueAs(jsonObject.at(JS(limit))); diff --git a/src/rpc/handlers/AccountTx.cpp b/src/rpc/handlers/AccountTx.cpp index 043ea4fa..ad82919e 100644 --- a/src/rpc/handlers/AccountTx.cpp +++ b/src/rpc/handlers/AccountTx.cpp @@ -258,8 +258,10 @@ tag_invoke(boost::json::value_to_tag, boost::json::valu input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); if (jsonObject.contains(JS(ledger_index))) { - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); - if (not input.ledgerIndex.has_value()) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) { + input.ledgerIndex = *expectedLedgerIndex; + } else { // could not get the latest validated ledger seq here, using this flag to indicate that input.usingValidatedLedger = true; } diff --git a/src/rpc/handlers/BookChanges.cpp b/src/rpc/handlers/BookChanges.cpp index cc2edbef..003ce4ef 100644 --- a/src/rpc/handlers/BookChanges.cpp +++ b/src/rpc/handlers/BookChanges.cpp @@ -90,8 +90,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::va if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/BookOffers.cpp b/src/rpc/handlers/BookOffers.cpp index bca916f9..1383c68f 100644 --- a/src/rpc/handlers/BookOffers.cpp +++ b/src/rpc/handlers/BookOffers.cpp @@ -122,8 +122,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::val if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(taker))) input.taker = accountFromStringStrict(boost::json::value_to(jv.at(JS(taker)))); diff --git a/src/rpc/handlers/DepositAuthorized.cpp b/src/rpc/handlers/DepositAuthorized.cpp index 71d77e22..364760f5 100644 --- a/src/rpc/handlers/DepositAuthorized.cpp +++ b/src/rpc/handlers/DepositAuthorized.cpp @@ -145,8 +145,11 @@ tag_invoke(boost::json::value_to_tag, boost::js if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(credentials))) input.credentials = boost::json::value_to(jv.at(JS(credentials))); diff --git a/src/rpc/handlers/Feature.cpp b/src/rpc/handlers/Feature.cpp index 4216ba38..1a842273 100644 --- a/src/rpc/handlers/Feature.cpp +++ b/src/rpc/handlers/Feature.cpp @@ -168,8 +168,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::value if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/GatewayBalances.cpp b/src/rpc/handlers/GatewayBalances.cpp index 6bcec93e..ca95142f 100644 --- a/src/rpc/handlers/GatewayBalances.cpp +++ b/src/rpc/handlers/GatewayBalances.cpp @@ -249,8 +249,11 @@ tag_invoke(boost::json::value_to_tag, boost::json if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(hotwallet))) { if (jsonObject.at(JS(hotwallet)).is_string()) { diff --git a/src/rpc/handlers/GetAggregatePrice.cpp b/src/rpc/handlers/GetAggregatePrice.cpp index ade1b9da..f09e2f7c 100644 --- a/src/rpc/handlers/GetAggregatePrice.cpp +++ b/src/rpc/handlers/GetAggregatePrice.cpp @@ -263,8 +263,11 @@ tag_invoke(boost::json::value_to_tag, boost::js if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } for (auto const& oracle : jsonObject.at(JS(oracles)).as_array()) { input.oracles.push_back( diff --git a/src/rpc/handlers/Ledger.cpp b/src/rpc/handlers/Ledger.cpp index fc87b86e..69923636 100644 --- a/src/rpc/handlers/Ledger.cpp +++ b/src/rpc/handlers/Ledger.cpp @@ -208,8 +208,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::value c if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(transactions))) input.transactions = jv.at(JS(transactions)).as_bool(); diff --git a/src/rpc/handlers/LedgerData.cpp b/src/rpc/handlers/LedgerData.cpp index 4f9ad5dd..9b35eed9 100644 --- a/src/rpc/handlers/LedgerData.cpp +++ b/src/rpc/handlers/LedgerData.cpp @@ -210,8 +210,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::val if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(type))) input.type = util::LedgerTypes::getLedgerEntryTypeFromStr(boost::json::value_to(jv.at(JS(type)))); diff --git a/src/rpc/handlers/LedgerEntry.cpp b/src/rpc/handlers/LedgerEntry.cpp index 1a62bb34..fa173fc5 100644 --- a/src/rpc/handlers/LedgerEntry.cpp +++ b/src/rpc/handlers/LedgerEntry.cpp @@ -305,8 +305,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::va if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(binary))) input.binary = jv.at(JS(binary)).as_bool(); diff --git a/src/rpc/handlers/MPTHolders.cpp b/src/rpc/handlers/MPTHolders.cpp index 97254025..86aa3a99 100644 --- a/src/rpc/handlers/MPTHolders.cpp +++ b/src/rpc/handlers/MPTHolders.cpp @@ -124,8 +124,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::val if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = jsonObject.at(JS(ledger_hash)).as_string().c_str(); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(limit))) input.limit = util::integralValueAs(jsonObject.at(JS(limit))); diff --git a/src/rpc/handlers/NFTHistory.cpp b/src/rpc/handlers/NFTHistory.cpp index 4b941dac..9d3dc17a 100644 --- a/src/rpc/handlers/NFTHistory.cpp +++ b/src/rpc/handlers/NFTHistory.cpp @@ -215,8 +215,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::val if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(binary))) input.binary = jsonObject.at(JS(binary)).as_bool(); diff --git a/src/rpc/handlers/NFTInfo.cpp b/src/rpc/handlers/NFTInfo.cpp index d2fe5e8f..32d6e457 100644 --- a/src/rpc/handlers/NFTInfo.cpp +++ b/src/rpc/handlers/NFTInfo.cpp @@ -115,8 +115,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::value if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/NFTOffersCommon.cpp b/src/rpc/handlers/NFTOffersCommon.cpp index 76cb01e6..3e33578a 100644 --- a/src/rpc/handlers/NFTOffersCommon.cpp +++ b/src/rpc/handlers/NFTOffersCommon.cpp @@ -194,8 +194,11 @@ tag_invoke(boost::json::value_to_tag, boost::json:: if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(marker))) input.marker = boost::json::value_to(jsonObject.at(JS(marker))); diff --git a/src/rpc/handlers/NFTsByIssuer.cpp b/src/rpc/handlers/NFTsByIssuer.cpp index eb17f63e..c5f3f9c7 100644 --- a/src/rpc/handlers/NFTsByIssuer.cpp +++ b/src/rpc/handlers/NFTsByIssuer.cpp @@ -136,8 +136,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::v if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } if (jsonObject.contains(JS(limit))) input.limit = util::integralValueAs(jsonObject.at(JS(limit))); diff --git a/src/rpc/handlers/NoRippleCheck.cpp b/src/rpc/handlers/NoRippleCheck.cpp index 1c07e88d..4fe0cb04 100644 --- a/src/rpc/handlers/NoRippleCheck.cpp +++ b/src/rpc/handlers/NoRippleCheck.cpp @@ -196,8 +196,11 @@ tag_invoke(boost::json::value_to_tag, boost::json:: if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jsonObject.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/TransactionEntry.cpp b/src/rpc/handlers/TransactionEntry.cpp index e9fe7073..19d7df8a 100644 --- a/src/rpc/handlers/TransactionEntry.cpp +++ b/src/rpc/handlers/TransactionEntry.cpp @@ -109,8 +109,11 @@ tag_invoke(boost::json::value_to_tag, boost::jso if (jsonObject.contains(JS(ledger_hash))) input.ledgerHash = boost::json::value_to(jv.at(JS(ledger_hash))); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jv.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/rpc/handlers/VaultInfo.cpp b/src/rpc/handlers/VaultInfo.cpp index 277c178d..ad407459 100644 --- a/src/rpc/handlers/VaultInfo.cpp +++ b/src/rpc/handlers/VaultInfo.cpp @@ -177,8 +177,11 @@ tag_invoke(boost::json::value_to_tag, boost::json::valu if (jsonObject.contains(JS(vault_id))) input.vaultID = jsonObject.at(JS(vault_id)).as_string(); - if (jsonObject.contains(JS(ledger_index))) - input.ledgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (jsonObject.contains(JS(ledger_index))) { + auto const expectedLedgerIndex = util::getLedgerIndex(jsonObject.at(JS(ledger_index))); + if (expectedLedgerIndex.has_value()) + input.ledgerIndex = *expectedLedgerIndex; + } return input; } diff --git a/src/util/JsonUtils.hpp b/src/util/JsonUtils.hpp index 9170b677..f5c4a3df 100644 --- a/src/util/JsonUtils.hpp +++ b/src/util/JsonUtils.hpp @@ -23,10 +23,13 @@ #include #include +#include #include #include +#include #include +#include #include #include @@ -96,12 +99,11 @@ removeSecret(boost::json::object const& object) * * @tparam Type The type to cast to * @param value The JSON value to cast - * @return Value casted to the requested type - * @throws logic_error if the underlying number is neither int64 nor uint64 + * @return Value casted to the requested type or an error message */ template -Type -integralValueAs(boost::json::value const& value) +std::expected +tryIntegralValueAs(boost::json::value const& value) { if (value.is_uint64()) return static_cast(value.as_uint64()); @@ -109,29 +111,49 @@ integralValueAs(boost::json::value const& value) if (value.is_int64()) return static_cast(value.as_int64()); - throw std::logic_error("Value neither uint64 nor int64"); + return std::unexpected("Value neither uint64 nor int64"); +} + +/** + * @brief Detects the type of number stored in value and casts it back to the requested Type. + * @note This conversion can possibly cause wrapping around or UB. Use with caution. + * + * @tparam Type The type to cast to + * @param value The JSON value to cast + * @return Value casted to the requested type + * @throws logic_error if the underlying number is neither int64 nor uint64 + */ +template +Type +integralValueAs(boost::json::value const& value) +{ + auto expectedResult = tryIntegralValueAs(value); + if (expectedResult.has_value()) + return *expectedResult; + + throw std::logic_error(std::move(expectedResult).error()); } /** * @brief Extracts ledger index from a JSON value which can be either a number or a string. * * @param value The JSON value to extract ledger index from - * @return An optional containing the ledger index if it is a number; std::nullopt otherwise - * @throws logic_error comes from integralValueAs if the underlying number is neither int64 nor uint64 - * @throws std::invalid_argument or std::out_of_range if the string cannot be converted to a number + * @return The extracted ledger index or an error message */ -[[nodiscard]] inline std::optional +[[nodiscard]] inline std::expected getLedgerIndex(boost::json::value const& value) { - std::optional ledgerIndex; - if (not value.is_string()) { - ledgerIndex = util::integralValueAs(value); + return tryIntegralValueAs(value); } else if (value.as_string() != "validated") { - ledgerIndex = std::stoi(value.as_string().c_str()); + uint32_t ledgerIndex{}; + if (beast::lexicalCastChecked(ledgerIndex, value.as_string().c_str())) + return ledgerIndex; + else + return std::unexpected("Invalid ledger index string"); + } else { + return std::unexpected("'validated' ledger index is requested"); } - - return ledgerIndex; } } // namespace util diff --git a/tests/unit/JsonUtilTests.cpp b/tests/unit/JsonUtilTests.cpp index 52d68eef..3eae61f2 100644 --- a/tests/unit/JsonUtilTests.cpp +++ b/tests/unit/JsonUtilTests.cpp @@ -18,6 +18,7 @@ //============================================================================== #include "util/JsonUtils.hpp" +#include "util/NameGenerator.hpp" #include #include @@ -90,28 +91,123 @@ TEST(JsonUtils, integralValueAs) EXPECT_THROW(util::integralValueAs(stringJson), std::logic_error); } -TEST(JsonUtils, getLedgerIndex) +TEST(JsonUtils, tryIntegralValueAs) { - auto const emptyJson = boost::json::value(); - EXPECT_THROW(std::ignore = util::getLedgerIndex(emptyJson), std::logic_error); + auto const expectedResultUint64 = static_cast(std::numeric_limits::max()) + 1u; + auto const uint64Json = boost::json::value(expectedResultUint64); - auto const boolJson = boost::json::value(true); - EXPECT_THROW(std::ignore = util::getLedgerIndex(emptyJson), std::logic_error); + auto const expectedResultInt64 = static_cast(std::numeric_limits::max()) + 1u; + auto const int64Json = boost::json::value(expectedResultInt64); - auto const numberJson = boost::json::value(12345); - auto ledgerIndex = util::getLedgerIndex(numberJson); - EXPECT_TRUE(ledgerIndex.has_value()); - EXPECT_EQ(ledgerIndex.value(), 12345u); + auto checkHasValue = [&](boost::json::value const& jv, auto const& expectedValue) { + using T = std::remove_cvref_t; + auto const res = util::tryIntegralValueAs(jv); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res.value(), expectedValue); + }; - auto const validStringJson = boost::json::value("12345"); - ledgerIndex = util::getLedgerIndex(validStringJson); - EXPECT_TRUE(ledgerIndex.has_value()); - EXPECT_EQ(ledgerIndex.value(), 12345u); + auto checkError = [&](boost::json::value const& jv) { + auto res = util::tryIntegralValueAs(jv); + EXPECT_FALSE(res.has_value()); + EXPECT_EQ(res.error(), "Value neither uint64 nor int64"); + }; - auto const invalidStringJson = boost::json::value("invalid123"); - EXPECT_THROW(std::ignore = util::getLedgerIndex(invalidStringJson), std::invalid_argument); + // checks for uint64Json + checkHasValue(uint64Json, std::numeric_limits::min()); + checkHasValue(uint64Json, static_cast(expectedResultUint64)); + checkHasValue(uint64Json, static_cast(expectedResultUint64)); + checkHasValue(uint64Json, expectedResultUint64); - auto const validatedJson = boost::json::value("validated"); - ledgerIndex = util::getLedgerIndex(validatedJson); - EXPECT_FALSE(ledgerIndex.has_value()); + // checks for int64Json + checkHasValue(int64Json, std::numeric_limits::min()); + checkHasValue(int64Json, static_cast(expectedResultInt64)); + checkHasValue(int64Json, expectedResultInt64); + checkHasValue(int64Json, static_cast(expectedResultInt64)); + + // non-integral inputs + checkError(boost::json::value()); + checkError(boost::json::value(false)); + checkError(boost::json::value(3.14)); + checkError(boost::json::value("not a number")); +} + +struct GetLedgerIndexParameterTestBundle { + std::string testName; + boost::json::value jv; + std::expected expectedResult; +}; + +// parameterized test cases for parameters check +struct GetLedgerIndexParameterTest : ::testing::TestWithParam {}; + +INSTANTIATE_TEST_CASE_P( + JsonUtils, + GetLedgerIndexParameterTest, + testing::Values( + GetLedgerIndexParameterTestBundle{ + .testName = "EmptyValue", + .jv = boost::json::value(), + .expectedResult = std::unexpected{"Value neither uint64 nor int64"} + }, + GetLedgerIndexParameterTestBundle{ + .testName = "BoolValue", + .jv = boost::json::value(false), + .expectedResult = std::unexpected{"Value neither uint64 nor int64"} + }, + GetLedgerIndexParameterTestBundle{ + .testName = "NumberValue", + .jv = boost::json::value(123), + .expectedResult = 123u + }, + GetLedgerIndexParameterTestBundle{ + .testName = "StringNumberValue", + .jv = boost::json::value("123"), + .expectedResult = 123u + }, + GetLedgerIndexParameterTestBundle{ + .testName = "StringNumberWithPlusSignValue", + .jv = boost::json::value("+123"), + .expectedResult = 123u + }, + GetLedgerIndexParameterTestBundle{ + .testName = "StringEmptyValue", + .jv = boost::json::value(""), + .expectedResult = std::unexpected{"Invalid ledger index string"} + }, + GetLedgerIndexParameterTestBundle{ + .testName = "StringWithLeadingCharsValue", + .jv = boost::json::value("123invalid"), + .expectedResult = std::unexpected{"Invalid ledger index string"} + }, + GetLedgerIndexParameterTestBundle{ + .testName = "StringWithTrailingCharsValue", + .jv = boost::json::value("invalid123"), + .expectedResult = std::unexpected{"Invalid ledger index string"} + }, + GetLedgerIndexParameterTestBundle{ + .testName = "StringWithLeadingAndTrailingCharsValue", + .jv = boost::json::value("123invalid123"), + .expectedResult = std::unexpected{"Invalid ledger index string"} + }, + GetLedgerIndexParameterTestBundle{ + .testName = "ValidatedStringValue", + .jv = boost::json::value("validated"), + .expectedResult = std::unexpected{"'validated' ledger index is requested"} + } + ), + tests::util::kNAME_GENERATOR +); + +TEST_P(GetLedgerIndexParameterTest, getLedgerIndexParams) +{ + auto const& testBundle = GetParam(); + auto const ledgerIndex = util::getLedgerIndex(testBundle.jv); + + if (testBundle.expectedResult.has_value()) { + EXPECT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(ledgerIndex.value(), testBundle.expectedResult.value()); + } else { + EXPECT_FALSE(ledgerIndex.has_value()); + EXPECT_EQ(ledgerIndex.error(), testBundle.expectedResult.error()); + } }