Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1001ret += m_description.empty() ? "" : " " + m_description;1002return ret;1003}1005// NOLINTNEXTLINE(misc-no-recursion)1006void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const1007{1008// Indentation1009const std::string indent(current_indent, ' ');1010const std::string indent_next(current_indent + 2, ' ');1012// Elements in a JSON structure (dictionary or array) are separated by a comma1013const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""};1015// The key name if recursed into a dictionary1016const std::string maybe_key{1017outer_type == OuterType::OBJ ?1018"\"" + this->m_key_name + "\" : " :1019""};1021// Format description with type1022const auto Description = [&](const std::string& type) {1023return "(" + type + (this->m_optional ? ", optional" : "") + ")" +1024(this->m_description.empty() ? "" : " " + this->m_description);1025};1027// Ensure at least one visible field exists when elision is used1028const auto elision_has_description{[](const std::vector<RPCResult>& inner) {1029return std::ranges::any_of(inner, [](const auto& res) {1030return !std::holds_alternative<HelpElisionSkip>(res.m_opts.print_elision);1031});1032}};1034if (const auto* text = std::get_if<std::string>(&m_opts.print_elision)) {1035sections.PushSection({indent + "..." + maybe_separator, *text});1036return;1037}1038if (std::holds_alternative<HelpElisionSkip>(m_opts.print_elision)) {1039return;1040}1042switch (m_type) {1043case Type::ANY: {1044sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("any")});1045return;1046}1047case Type::NONE: {1048sections.PushSection({indent + "null" + maybe_separator, Description("json null")});1049return;1050}1051case Type::STR: {1052sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")});1053return;1054}1055case Type::STR_AMOUNT: {1056sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});1057return;1058}1059case Type::STR_HEX: {1060sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")});1061return;1062}1063case Type::NUM: {1064sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});1065return;1066}1067case Type::NUM_TIME: {1068sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")});1069return;1070}1071case Type::BOOL: {1072sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")});1073return;1074}1075case Type::ARR_FIXED:1076case Type::ARR: {1077sections.PushSection({indent + maybe_key + "[", Description("json array")});1078for (const auto& i : m_inner) {1079i.ToSections(sections, OuterType::ARR, current_indent + 2);1080}1081CHECK_NONFATAL(!m_inner.empty());1082CHECK_NONFATAL(elision_has_description(m_inner));1083if (m_type == Type::ARR && !std::holds_alternative<std::string>(m_inner.back().m_opts.print_elision)) {1084sections.PushSection({indent_next + "...", ""});1085} else {1086// Remove final comma, which would be invalid JSON1087sections.m_sections.back().m_left.pop_back();1088}1089sections.PushSection({indent + "]" + maybe_separator, ""});1090return;1091}1092case Type::OBJ_DYN:1093case Type::OBJ: {1094if (m_inner.empty()) {1095sections.PushSection({indent + maybe_key + "{}", Description("empty JSON object")});1096return;1097}1098CHECK_NONFATAL(elision_has_description(m_inner));1099sections.PushSection({indent + maybe_key + "{", Description("json object")});1100for (const auto& i : m_inner) {1101i.ToSections(sections, OuterType::OBJ, current_indent + 2);1102}1103if (m_type == Type::OBJ_DYN) {1104// If the dictionary keys are dynamic, use three dots for continuation1105sections.PushSection({indent_next + "...", ""});1106} else {1107// Remove final comma, which would be invalid JSON1108sections.m_sections.back().m_left.pop_back();1109}1110sections.PushSection({indent + "}" + maybe_separator, ""});1111return;1112}1113} // no default case, so the compiler can warn about missing cases1114NONFATAL_UNREACHABLE();1115}1117static std::optional<UniValue::VType> ExpectedType(RPCResult::Type type)1118{1119using Type = RPCResult::Type;1120switch (type) {1121case Type::ANY: {1122return std::nullopt;1123}1124case Type::NONE: {1125return UniValue::VNULL;1126}1127case Type::STR:1128case Type::STR_HEX: {1129return UniValue::VSTR;1130}1131case Type::NUM:1132case Type::STR_AMOUNT:1133case Type::NUM_TIME: {1134return UniValue::VNUM;1135}1136case Type::BOOL: {1137return UniValue::VBOOL;1138}1139case Type::ARR_FIXED:1140case Type::ARR: {1141return UniValue::VARR;1142}1143case Type::OBJ_DYN:1144case Type::OBJ: {1145return UniValue::VOBJ;1146}1147} // no default case, so the compiler can warn about missing cases1148NONFATAL_UNREACHABLE();1149}1151// NOLINTNEXTLINE(misc-no-recursion)1152UniValue RPCResult::MatchesType(const UniValue& result) const1153{1154if (m_opts.skip_type_check) {1155return true;1156}1158const auto exp_type = ExpectedType(m_type);1159if (!exp_type) return true; // can be any type, so nothing to check1161if (*exp_type != result.getType()) {1162return strprintf("returned type is %s, but declared as %s in doc", uvTypeName(result.getType()), uvTypeName(*exp_type));1163}1165if (UniValue::VARR == result.getType()) {1166UniValue errors(UniValue::VOBJ);1167for (size_t i{0}; i < result.get_array().size(); ++i) {1168// If there are more results than documented, reuse the last doc_inner.1169const RPCResult& doc_inner{m_inner.at(std::min(m_inner.size() - 1, i))};1170UniValue match{doc_inner.MatchesType(result.get_array()[i])};1171if (!match.isTrue()) errors.pushKV(strprintf("%d", i), std::move(match));1172}1173if (errors.empty()) return true; // empty result array is valid1174return errors;1175}1177if (UniValue::VOBJ == result.getType()) {1178UniValue errors(UniValue::VOBJ);1179if (m_type == Type::OBJ_DYN) {1180const RPCResult& doc_inner{m_inner.at(0)}; // Assume all types are the same, randomly pick the first1181for (size_t i{0}; i < result.get_obj().size(); ++i) {1182UniValue match{doc_inner.MatchesType(result.get_obj()[i])};1183if (!match.isTrue()) errors.pushKV(result.getKeys()[i], std::move(match));1184}1185if (errors.empty()) return true; // empty result obj is valid1186return errors;1187}1188std::set<std::string> doc_keys;1189for (const auto& doc_entry : m_inner) {1190doc_keys.insert(doc_entry.m_key_name);1191}1192std::map<std::string, UniValue> result_obj;1193result.getObjMap(result_obj);1194for (const auto& result_entry : result_obj) {1195if (!doc_keys.contains(result_entry.first)) {1196errors.pushKV(result_entry.first, "key returned that was not in doc");1197}1198}1200for (const auto& doc_entry : m_inner) {1201const auto result_it{result_obj.find(doc_entry.m_key_name)};1202if (result_it == result_obj.end()) {1203if (!doc_entry.m_optional) {1204errors.pushKV(doc_entry.m_key_name, "key missing, despite not being optional in doc");1205}1206continue;1207}1208UniValue match{doc_entry.MatchesType(result_it->second)};1209if (!match.isTrue()) errors.pushKV(doc_entry.m_key_name, std::move(match));1210}1211if (errors.empty()) return true;1212return errors;1213}1215return true;1216}1218void RPCResult::CheckInnerDoc() const1219{1220if (m_type == Type::OBJ) {1221// May or may not be empty1222return;1223}1224// Everything else must either be empty or not1225const bool inner_needed{m_type == Type::ARR || m_type == Type::ARR_FIXED || m_type == Type::OBJ_DYN};1226CHECK_NONFATAL(inner_needed != m_inner.empty());1227}1229// NOLINTNEXTLINE(misc-no-recursion)1230std::string RPCArg::ToStringObj(const bool oneline) const1231{1232std::string res;1233res += "\"";1234res += GetFirstName();1235if (oneline) {1236res += "\":";1237} else {1238res += "\": ";1239}1240switch (m_type) {1241case Type::STR:1242return res + "\"str\"";1243case Type::STR_HEX:1244return res + "\"hex\"";1245case Type::NUM:1246return res + "n";1247case Type::RANGE:1248return res + "n or [n,n]";1249case Type::AMOUNT:1250return res + "amount";