diff --git a/src/rpc/common/Validators.cpp b/src/rpc/common/Validators.cpp index 1ff1edf6..a2e2f5a5 100644 --- a/src/rpc/common/Validators.cpp +++ b/src/rpc/common/Validators.cpp @@ -17,10 +17,12 @@ */ //============================================================================== +#include #include #include +#include #include namespace RPCng::validation { @@ -86,4 +88,42 @@ CustomValidator::verify(boost::json::value const& value, std::string_view key) return validator_(value.as_object().at(key.data()), key); } +[[nodiscard]] bool +checkIsU32Numeric(std::string_view sv) +{ + uint32_t unused; + auto [_, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), unused); + return ec == std::errc(); +} + +CustomValidator LedgerHashValidator = CustomValidator{ + [](boost::json::value const& value, std::string_view key) -> MaybeError { + if (!value.is_string()) + { + return Error{RPC::Status{ + RPC::RippledError::rpcINVALID_PARAMS, "ledgerHashNotString"}}; + } + ripple::uint256 ledgerHash; + if (!ledgerHash.parseHex(value.as_string().c_str())) + return Error{RPC::Status{ + RPC::RippledError::rpcINVALID_PARAMS, "ledgerHashMalformed"}}; + return MaybeError{}; + }}; + +CustomValidator LedgerIndexValidator = CustomValidator{ + [](boost::json::value const& value, std::string_view key) -> MaybeError { + auto err = Error{RPC::Status{ + RPC::RippledError::rpcINVALID_PARAMS, "ledgerIndexMalformed"}}; + if (!value.is_string() && !(value.is_uint64() || value.is_int64())) + { + return err; + } + if (value.is_string() && value.as_string() != "validated" && + !checkIsU32Numeric(value.as_string().c_str())) + { + return err; + } + return MaybeError{}; + }}; + } // namespace RPCng::validation diff --git a/src/rpc/common/Validators.h b/src/rpc/common/Validators.h index dea427b1..603b2173 100644 --- a/src/rpc/common/Validators.h +++ b/src/rpc/common/Validators.h @@ -340,4 +340,24 @@ public: verify(boost::json::value const& value, std::string_view key) const; }; +/** + * @brief Helper function to check if sv is an uint32 number or not + */ +[[nodiscard]] bool +checkIsU32Numeric(std::string_view sv); + +/** + * @brief Provide a common used validator for ledger hash + * LedgerHash must be a string and hex + */ +extern CustomValidator LedgerIndexValidator; + +/** + * @brief Provide a common used validator for ledger index + * LedgerIndex must be a string or int + * If the specified LedgerIndex is a string, it's value must be either + * "validated" or a valid integer value represented as a string. + */ +extern CustomValidator LedgerHashValidator; + } // namespace RPCng::validation diff --git a/unittests/rpc/BaseTests.cpp b/unittests/rpc/BaseTests.cpp index a1865210..798f06cd 100644 --- a/unittests/rpc/BaseTests.cpp +++ b/unittests/rpc/BaseTests.cpp @@ -242,3 +242,41 @@ TEST_F(RPCBaseTest, CustomValidator) auto failingInput = json::parse(R"({ "taker": "wrongformat" })"); ASSERT_FALSE(spec.validate(failingInput)); } + +TEST_F(RPCBaseTest, LedgerHashValidator) +{ + auto spec = RpcSpec{ + {"ledgerHash", LedgerHashValidator}, + }; + auto passingInput = json::parse( + R"({ "ledgerHash": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" })"); + ASSERT_TRUE(spec.validate(passingInput)); + + auto failingInput = json::parse(R"({ "ledgerHash": "wrongformat" })"); + ASSERT_FALSE(spec.validate(failingInput)); + + failingInput = json::parse(R"({ "ledgerHash": 256 })"); + auto err = spec.validate(failingInput); + ASSERT_FALSE(err); + ASSERT_EQ(err.error().message, "ledgerHashNotString"); +} + +TEST_F(RPCBaseTest, LedgerIndexValidator) +{ + auto spec = RpcSpec{ + {"ledgerIndex", LedgerIndexValidator}, + }; + auto passingInput = json::parse(R"({ "ledgerIndex": "validated" })"); + ASSERT_TRUE(spec.validate(passingInput)); + + passingInput = json::parse(R"({ "ledgerIndex": "256" })"); + ASSERT_TRUE(spec.validate(passingInput)); + + passingInput = json::parse(R"({ "ledgerIndex": 256 })"); + ASSERT_TRUE(spec.validate(passingInput)); + + auto failingInput = json::parse(R"({ "ledgerIndex": "wrongformat" })"); + auto err = spec.validate(failingInput); + ASSERT_FALSE(err); + ASSERT_EQ(err.error().message, "ledgerIndexMalformed"); +}