Inside Bitcoin
Opening the selected file…
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.
251UniValue OpenRPCResultSchema(const RPCResult& result);253UniValue MakeObject(std::initializer_list<std::pair<std::string, UniValue>> entries)254{255UniValue obj{UniValue::VOBJ};256for (const auto& [key, value] : entries) {257obj.pushKV(key, value);258}259return obj;260}262void PushUniqueSchema(UniValue& schemas, std::unordered_set<std::string>& seen, UniValue schema)263{264const std::string serialized{schema.write()};265if (seen.insert(serialized).second) schemas.push_back(std::move(schema));266}268// NOLINTNEXTLINE(misc-no-recursion)269UniValue DedupArrayItemsSchema(std::span<const RPCArg> inner, bool include_hidden, bool in_skip_type_check)270{271if (inner.empty()) return UniValue{UniValue::VOBJ};272if (inner.size() == 1) return OpenRPCArgSchema(inner.front(), include_hidden, in_skip_type_check);274UniValue one_of{UniValue::VARR};275std::unordered_set<std::string> seen;276for (const auto& item : inner) {277PushUniqueSchema(one_of, seen, OpenRPCArgSchema(item, include_hidden, in_skip_type_check));278}280if (one_of.size() == 1) return one_of[0];282UniValue items{UniValue::VOBJ};283items.pushKV(in_skip_type_check ? "anyOf" : "oneOf", std::move(one_of));284return items;285}287// NOLINTNEXTLINE(misc-no-recursion)288UniValue DedupArrayItemsSchema(std::span<const RPCResult> inner)289{290if (inner.empty()) return UniValue{UniValue::VOBJ};291if (inner.size() == 1) return OpenRPCResultSchema(inner.front());293UniValue one_of{UniValue::VARR};294std::unordered_set<std::string> seen;295for (const auto& item : inner) {296PushUniqueSchema(one_of, seen, OpenRPCResultSchema(item));297}299if (one_of.size() == 1) return one_of[0];301UniValue items{UniValue::VOBJ};302items.pushKV("oneOf", std::move(one_of));303return items;304}306void ApplyTypeStrOverride(UniValue& schema, const RPCArg& arg)307{308if (arg.m_opts.type_str.size() != 2) return;309const std::string& type_label{arg.m_opts.type_str[1]};310if (type_label.empty()) return;312static const std::unordered_set<std::string> number_or_string{313"integer / string",314"string or numeric",315};316if (number_or_string.contains(type_label)) {317UniValue one_of{UniValue::VARR};318one_of.push_back(MakeObject({{"type", "integer"}}));319one_of.push_back(MakeObject({{"type", "string"}}));320schema = UniValue{UniValue::VOBJ};321schema.pushKV("oneOf", std::move(one_of));322} else {323schema.pushKV("x-bitcoin-type-override", type_label);324}325}327void ApplyArgFallback(UniValue& schema, const RPCArg& arg)328{329std::visit(util::Overloaded{330[&](const RPCArg::Default& def) { schema.pushKV("default", def); },331[&](const RPCArg::DefaultHint& hint) { schema.pushKV("x-bitcoin-default-hint", hint); },332[](const RPCArg::Optional&) {},333},334arg.m_fallback);335}337// NOLINTNEXTLINE(misc-no-recursion)338UniValue OpenRPCArgSchema(const RPCArg& arg, bool include_hidden, bool in_skip_type_check)339{340UniValue schema{UniValue::VOBJ};341if (arg.m_opts.skip_type_check) {342ApplyTypeStrOverride(schema, arg);343if (schema.empty() && arg.m_type == RPCArg::Type::ARR) {344UniValue items{UniValue::VOBJ};345items.pushKV("type", "array");346items.pushKV("items", DedupArrayItemsSchema(arg.m_inner, include_hidden, /*in_skip_type_check=*/true));348UniValue one_of{UniValue::VARR};349one_of.push_back(std::move(items));350one_of.push_back(MakeObject({{"type", "object"}}));351schema.pushKV("oneOf", std::move(one_of));352}353ApplyArgFallback(schema, arg);354return schema;355}357switch (arg.m_type) {358case RPCArg::Type::STR:359schema = MakeObject({{"type", "string"}});360break;361case RPCArg::Type::STR_HEX:362schema = MakeObject({{"type", "string"}, {"pattern", "^([0-9a-fA-F]{2})*$"}});363break;364case RPCArg::Type::NUM:365schema = MakeObject({{"type", "number"}});366break;367case RPCArg::Type::BOOL:368schema = MakeObject({{"type", "boolean"}});369break;370case RPCArg::Type::AMOUNT: {371UniValue one_of{UniValue::VARR};372one_of.push_back(MakeObject({{"type", "number"}}));373one_of.push_back(MakeObject({{"type", "string"}}));374schema.pushKV("oneOf", std::move(one_of));375break;376}377case RPCArg::Type::RANGE: {378UniValue items{UniValue::VARR};379items.push_back(MakeObject({{"type", "number"}}));380items.push_back(MakeObject({{"type", "number"}}));381UniValue range_schema{UniValue::VOBJ};382range_schema.pushKV("type", "array");383range_schema.pushKV("items", std::move(items));384range_schema.pushKV("additionalItems", false);385range_schema.pushKV("minItems", 2);386range_schema.pushKV("maxItems", 2);387UniValue one_of{UniValue::VARR};388one_of.push_back(MakeObject({{"type", "number"}}));389one_of.push_back(std::move(range_schema));390schema.pushKV("oneOf", std::move(one_of));391break;392}393case RPCArg::Type::ARR: {394UniValue items{DedupArrayItemsSchema(arg.m_inner, include_hidden, in_skip_type_check)};395schema.pushKV("type", "array");396schema.pushKV("items", std::move(items));397break;398}399case RPCArg::Type::OBJ:400case RPCArg::Type::OBJ_NAMED_PARAMS: {401UniValue properties{UniValue::VOBJ};402UniValue required{UniValue::VARR};403for (const auto& inner : arg.m_inner) {404if (!include_hidden && inner.m_opts.hidden) continue;405UniValue prop{OpenRPCArgSchema(inner, include_hidden, in_skip_type_check)};406if (!inner.m_description.empty()) prop.pushKV("description", inner.m_description);407if (inner.m_opts.placeholder) prop.pushKV("x-bitcoin-placeholder", true);408if (inner.m_opts.also_positional) prop.pushKV("x-bitcoin-also-positional", true);409properties.pushKV(inner.GetFirstName(), std::move(prop));410if (!inner.IsOptional()) required.push_back(inner.GetFirstName());411}412schema.pushKV("type", "object");413schema.pushKV("properties", std::move(properties));414schema.pushKV("additionalProperties", false);415if (!required.empty()) schema.pushKV("required", std::move(required));416break;417}418case RPCArg::Type::OBJ_USER_KEYS: {419schema.pushKV("type", "object");420if (!arg.m_inner.empty()) {421schema.pushKV("additionalProperties", OpenRPCArgSchema(arg.m_inner[0], include_hidden, in_skip_type_check));422if (!arg.m_inner[0].m_description.empty()) {423schema.pushKV("description", arg.m_inner[0].m_description);424}425} else {426schema.pushKV("additionalProperties", true);427}428break;429}430} // no default case, so the compiler can warn about missing cases431ApplyTypeStrOverride(schema, arg);432ApplyArgFallback(schema, arg);433return schema;434}436// NOLINTNEXTLINE(misc-no-recursion)437UniValue OpenRPCResultSchema(const RPCResult& result)438{439if (result.m_opts.skip_type_check) {440RPCResultOptions opts{result.m_opts};441opts.skip_type_check = false;442if (result.m_type == RPCResult::Type::OBJ) {443UniValue obj_schema{OpenRPCResultSchema(RPCResult{result, std::move(opts)})};444if (result.m_key_name.empty()) return obj_schema;446UniValue one_of{UniValue::VARR};447one_of.push_back(std::move(obj_schema));448one_of.push_back(MakeObject({{"const", false}}));449UniValue schema{UniValue::VOBJ};450schema.pushKV("oneOf", std::move(one_of));451return schema;452}453if (result.m_type == RPCResult::Type::ARR) return OpenRPCResultSchema(RPCResult{result, std::move(opts)});454return UniValue{UniValue::VOBJ};455}457switch (result.m_type) {458case RPCResult::Type::STR:459return MakeObject({{"type", "string"}});460case RPCResult::Type::STR_AMOUNT:461return MakeObject({{"type", "number"}, {"x-bitcoin-unit", "amount"}});462case RPCResult::Type::STR_HEX:463return MakeObject({{"type", "string"}, {"pattern", "^([0-9a-fA-F]{2})*$"}});464case RPCResult::Type::NUM:465return MakeObject({{"type", "number"}});466case RPCResult::Type::NUM_TIME: {467UniValue schema{UniValue::VOBJ};468schema.pushKV("type", "number");469schema.pushKV("x-bitcoin-unit", "unix-time");470return schema;471}472case RPCResult::Type::BOOL:473return MakeObject({{"type", "boolean"}});474case RPCResult::Type::NONE:475return MakeObject({{"type", "null"}});476case RPCResult::Type::ARR: {477UniValue items{DedupArrayItemsSchema(result.m_inner)};478UniValue schema{UniValue::VOBJ};479schema.pushKV("type", "array");480schema.pushKV("items", std::move(items));481return schema;482}483case RPCResult::Type::ARR_FIXED: {484UniValue items{UniValue::VARR};485for (const auto& inner : result.m_inner) {486items.push_back(OpenRPCResultSchema(inner));487}488UniValue schema{UniValue::VOBJ};489schema.pushKV("type", "array");490schema.pushKV("items", std::move(items));491schema.pushKV("additionalItems", false);492schema.pushKV("minItems", uint64_t(result.m_inner.size()));493schema.pushKV("maxItems", uint64_t(result.m_inner.size()));494return schema;495}496case RPCResult::Type::OBJ: {497UniValue properties{UniValue::VOBJ};498UniValue required{UniValue::VARR};499for (const auto& inner : result.m_inner) {500if (inner.m_key_name.empty()) continue;