diff options
Diffstat (limited to 'src/rpc/util.cpp')
-rw-r--r-- | src/rpc/util.cpp | 54 |
1 files changed, 48 insertions, 6 deletions
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 7c859268be..9c9e6e9f11 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -774,7 +774,7 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const // Elements in a JSON structure (dictionary or array) are separated by a comma const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""}; - // The key name if recursed into an dictionary + // The key name if recursed into a dictionary const std::string maybe_key{ outer_type == OuterType::OBJ ? "\"" + this->m_key_name + "\" : " : @@ -865,10 +865,11 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const bool RPCResult::MatchesType(const UniValue& result) const { - switch (m_type) { - case Type::ELISION: { - return false; + if (m_skip_type_check) { + return true; } + switch (m_type) { + case Type::ELISION: case Type::ANY: { return true; } @@ -889,11 +890,52 @@ bool RPCResult::MatchesType(const UniValue& result) const } case Type::ARR_FIXED: case Type::ARR: { - return UniValue::VARR == result.getType(); + if (UniValue::VARR != result.getType()) return false; + for (size_t i{0}; i < result.get_array().size(); ++i) { + // If there are more results than documented, re-use the last doc_inner. + const RPCResult& doc_inner{m_inner.at(std::min(m_inner.size() - 1, i))}; + if (!doc_inner.MatchesType(result.get_array()[i])) return false; + } + return true; // empty result array is valid } case Type::OBJ_DYN: case Type::OBJ: { - return UniValue::VOBJ == result.getType(); + if (UniValue::VOBJ != result.getType()) return false; + if (!m_inner.empty() && m_inner.at(0).m_type == Type::ELISION) return true; + if (m_type == Type::OBJ_DYN) { + const RPCResult& doc_inner{m_inner.at(0)}; // Assume all types are the same, randomly pick the first + for (size_t i{0}; i < result.get_obj().size(); ++i) { + if (!doc_inner.MatchesType(result.get_obj()[i])) { + return false; + } + } + return true; // empty result obj is valid + } + std::set<std::string> doc_keys; + for (const auto& doc_entry : m_inner) { + doc_keys.insert(doc_entry.m_key_name); + } + std::map<std::string, UniValue> result_obj; + result.getObjMap(result_obj); + for (const auto& result_entry : result_obj) { + if (doc_keys.find(result_entry.first) == doc_keys.end()) { + return false; // missing documentation + } + } + + for (const auto& doc_entry : m_inner) { + const auto result_it{result_obj.find(doc_entry.m_key_name)}; + if (result_it == result_obj.end()) { + if (!doc_entry.m_optional) { + return false; // result is missing a required key + } + continue; + } + if (!doc_entry.MatchesType(result_it->second)) { + return false; // wrong type + } + } + return true; } } // no default case, so the compiler can warn about missing cases CHECK_NONFATAL(false); |