From fa9d5292a179dc4326cdd490f606cacf40508755 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 29 Jul 2026 12:39:24 +0700 Subject: [PATCH 1/3] fix: multiple return undocumented or misdocumented JSON --- src/evo/core_write.cpp | 7 +-- src/governance/core_write.cpp | 19 ++++--- src/llmq/core_write.cpp | 88 ++++++++++++--------------------- src/rpc/blockchain.cpp | 2 +- src/rpc/coinjoin.cpp | 16 +++--- src/rpc/evo.cpp | 17 +++---- src/rpc/governance.cpp | 12 ++--- src/rpc/masternode.cpp | 61 ++++++++++++++--------- src/rpc/mempool.cpp | 6 +-- src/rpc/mining.cpp | 3 +- src/rpc/net.cpp | 6 +-- src/rpc/node.cpp | 13 +++-- src/rpc/quorums.cpp | 18 ++++--- src/rpc/rawtransaction.cpp | 32 +++++++----- src/wallet/rpc/backup.cpp | 9 +++- src/wallet/rpc/transactions.cpp | 19 ++++--- src/wallet/rpc/wallet.cpp | 13 ++--- 17 files changed, 178 insertions(+), 163 deletions(-) diff --git a/src/evo/core_write.cpp b/src/evo/core_write.cpp index c668f65b0dc5..fd5917dafa42 100644 --- a/src/evo/core_write.cpp +++ b/src/evo/core_write.cpp @@ -275,9 +275,10 @@ UniValue CDeterministicMNState::ToJson(MnType nType) const // CDeterministicMNStateDiff::ToJson() defined in evo/dmnstate.cpp RPCResult CDeterministicMNStateDiff::GetJsonHelp(const std::string& key, bool optional) { + // Every field is emitted only when the diff actually carries it, version included. return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The masternode state diff", { - {RPCResult::Type::NUM, "version", "Version of the masternode state diff"}, + {RPCResult::Type::NUM, "version", /*optional=*/true, "Version of the masternode state diff"}, GetRpcResult("service", /*optional=*/true), GetRpcResult("registeredHeight", /*optional=*/true), GetRpcResult("lastPaidHeight", /*optional=*/true), @@ -468,8 +469,8 @@ RPCResult CSimplifiedMNListDiff::GetJsonHelp(const std::string& key, bool option GetRpcResult("merkleRootMNList", /*optional=*/true), GetRpcResult("merkleRootQuorums", /*optional=*/true), {RPCResult::Type::ARR, "quorumsCLSigs", "ChainLock signature details", { - {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::ARR, "", "Array of quorum indices, keyed by BLS signature", { + {RPCResult::Type::OBJ_DYN, "", "json object with the BLS signature as key", { + {RPCResult::Type::ARR, "", "Array of quorum indices signed by this BLS signature", { {RPCResult::Type::NUM, "", "Quorum index"} }}}}}}, }}; diff --git a/src/governance/core_write.cpp b/src/governance/core_write.cpp index 4d90dd269c44..9f634ba76639 100644 --- a/src/governance/core_write.cpp +++ b/src/governance/core_write.cpp @@ -119,20 +119,23 @@ RPCResult Object::GetJsonHelp(const std::string& key, bool optional) GetRpcResult("collateralHash"), {RPCResult::Type::NUM, "createdAt", "Proposal creation timestamp"}, {RPCResult::Type::NUM, "revision", "Proposal revision number"}, + // Everything but "hex" is the submitter's own JSON echoed back verbatim, so neither the + // set of keys nor their types is ours to promise: a proposal may carry extra fields and + // may encode the numeric ones as strings. Hence the type check is skipped here. {RPCResult::Type::OBJ, "data", "", { // Fields emitted through GetDataAsPlainString(), read by CProposalValidator - {RPCResult::Type::STR, "end_epoch", /*optional=*/true, "Proposal end timestamp"}, - {RPCResult::Type::STR, "name", /*optional=*/true, "Proposal name"}, - {RPCResult::Type::STR, "payment_address", /*optional=*/true, "Proposal payment address"}, - {RPCResult::Type::STR, "payment_amount", /*optional=*/true, "Proposal payment amount"}, - {RPCResult::Type::STR, "start_epoch", /*optional=*/true, "Proposal start timestamp"}, - {RPCResult::Type::STR, "type", /*optional=*/true, "Object type"}, - {RPCResult::Type::STR, "url", /*optional=*/true, "Proposal URL"}, + {RPCResult::Type::ANY, "end_epoch", /*optional=*/true, "Proposal end timestamp"}, + {RPCResult::Type::ANY, "name", /*optional=*/true, "Proposal name"}, + {RPCResult::Type::ANY, "payment_address", /*optional=*/true, "Proposal payment address"}, + {RPCResult::Type::ANY, "payment_amount", /*optional=*/true, "Proposal payment amount"}, + {RPCResult::Type::ANY, "start_epoch", /*optional=*/true, "Proposal start timestamp"}, + {RPCResult::Type::ANY, "type", /*optional=*/true, "Object type"}, + {RPCResult::Type::ANY, "url", /*optional=*/true, "Proposal URL"}, // Failure case for GetDataAsPlainString() {RPCResult::Type::STR, "plain", /*optional=*/true, "Governance object data as string"}, // Always emitted by ToJson() {RPCResult::Type::STR_HEX, "hex", "Governance object data as hex"}, - }}, + }, /*skip_type_check=*/true}, }}; } diff --git a/src/llmq/core_write.cpp b/src/llmq/core_write.cpp index 8110457e4824..243f8f1c0a3a 100644 --- a/src/llmq/core_write.cpp +++ b/src/llmq/core_write.cpp @@ -19,61 +19,37 @@ namespace llmq { // CDKGDebugSessionStatus::ToJson() defined in llmq/debug.cpp RPCResult CDKGDebugSessionStatus::GetJsonHelp(const std::string& key, bool optional) { - return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The state of a DKG session", + // A member tally is a count for detail_level = 0, an array of member indexes for + // detail_level = 1, and an array of {memberIndex, proTxHash} objects for + // detail_level = 2. RPCResult can only express such a union as ANY: conditional + // variants are only resolved for top-level results, so listing one variant per + // detail level here would make every level fail the runtime doc check. + const auto member_tally{[](const std::string& name, const std::string& what) { + return RPCResult{RPCResult::Type::ANY, name, + strprintf("Number of %s (detail_level = 0), array of quorum member indexes " + "(detail_level = 1), or array of {memberIndex, proTxHash} objects " + "(detail_level = 2)", + what)}; + }}; + return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The state of a DKG session. Empty for an unknown LLMQ type or quorum hash", { - GetRpcResult("llmqType"), - GetRpcResult("quorumHash"), - {RPCResult::Type::NUM, "quorumHeight", "Block height of the quorum"}, - {RPCResult::Type::NUM, "phase", "Active DKG phase"}, - {RPCResult::Type::BOOL, "sentContributions", "Returns true if contributions sent"}, - {RPCResult::Type::BOOL, "sentComplaint", "Returns true if complaints sent"}, - {RPCResult::Type::BOOL, "sentJustification", "Returns true if justifications sent"}, - {RPCResult::Type::BOOL, "sentPrematureCommitment", "Returns true if premature commitments sent"}, - {RPCResult::Type::BOOL, "aborted", "Returns true if DKG session aborted"}, - {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "badMembers", "Number of bad members"}}, - {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "weComplain", "Number of complaints sent"}}, - {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "receivedContributions", "Number of contributions received"}}, - {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "receivedComplaints", "Number of complaints received"}}, - {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "receivedJustifications", "Number of justifications received"}}, - {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "receivedPrematureCommitments", "Number of premature commitments received"}}, - {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "badMembers", "Array of indexes for each bad member", { - {RPCResult::Type::NUM, "", "Quorum member index"}}}}, - {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "weComplain", "Array of indexes for each complaint sent", { - {RPCResult::Type::NUM, "", "Quorum member index"}}}}, - {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "receivedContributions", "Array of indexes for each contribution received", { - {RPCResult::Type::NUM, "", "Quorum member index"}}}}, - {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "receivedComplaints", "Array of indexes for each complaint received", { - {RPCResult::Type::NUM, "", "Quorum member index"}}}}, - {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "receivedJustifications", "Array of indexes for each justification received", { - {RPCResult::Type::NUM, "", "Quorum member index"}}}}, - {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "receivedPrematureCommitments", "Array of indexes for each commitment received", { - {RPCResult::Type::NUM, "", "Quorum member index"}}}}, - {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "badMembers", "Array of objects for each bad member", { - {RPCResult::Type::OBJ, "", "", { - GetRpcResult("memberIndex"), - GetRpcResult("proTxHash", /*optional=*/true)}}}}}, - {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "weComplain", "Array of objects for each complaint sent", { - {RPCResult::Type::OBJ, "", "", { - GetRpcResult("memberIndex"), - GetRpcResult("proTxHash", /*optional=*/true)}}}}}, - {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "receivedContributions", "Array of objects for each contribution received", { - {RPCResult::Type::OBJ, "", "", { - GetRpcResult("memberIndex"), - GetRpcResult("proTxHash", /*optional=*/true)}}}}}, - {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "receivedComplaints", "Array of objects for each complaint received", { - {RPCResult::Type::OBJ, "", "", { - GetRpcResult("memberIndex"), - GetRpcResult("proTxHash", /*optional=*/true)}}}}}, - {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "receivedJustifications", "Array of objects for each justification received", { - {RPCResult::Type::OBJ, "", "", { - GetRpcResult("memberIndex"), - GetRpcResult("proTxHash", /*optional=*/true)}}}}}, - {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "receivedPrematureCommitments", "Array of objects for each commitment received", { - {RPCResult::Type::OBJ, "", "", { - GetRpcResult("memberIndex"), - GetRpcResult("proTxHash", /*optional=*/true)}}}}}, - {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "allMembers", "Array of provider registration transaction hash for all quorum members", { - GetRpcResult("proTxHash")}}}, + GetRpcResult("llmqType", /*optional=*/true), + GetRpcResult("quorumHash", /*optional=*/true), + {RPCResult::Type::NUM, "quorumHeight", /*optional=*/true, "Block height of the quorum"}, + {RPCResult::Type::NUM, "phase", /*optional=*/true, "Active DKG phase"}, + {RPCResult::Type::BOOL, "sentContributions", /*optional=*/true, "Returns true if contributions sent"}, + {RPCResult::Type::BOOL, "sentComplaint", /*optional=*/true, "Returns true if complaints sent"}, + {RPCResult::Type::BOOL, "sentJustification", /*optional=*/true, "Returns true if justifications sent"}, + {RPCResult::Type::BOOL, "sentPrematureCommitment", /*optional=*/true, "Returns true if premature commitments sent"}, + {RPCResult::Type::BOOL, "aborted", /*optional=*/true, "Returns true if DKG session aborted"}, + member_tally("badMembers", "bad members"), + member_tally("weComplain", "complaints sent"), + member_tally("receivedContributions", "contributions received"), + member_tally("receivedComplaints", "complaints received"), + member_tally("receivedJustifications", "justifications received"), + member_tally("receivedPrematureCommitments", "premature commitments received"), + {RPCResult::Type::ARR, "allMembers", /*optional=*/true, "Provider registration transaction hash for all quorum members. Only present for detail_level = 2", { + GetRpcResult("proTxHash")}}, }}; } @@ -86,7 +62,7 @@ RPCResult CDKGDebugManager::GetJsonHelp(const std::string& key, bool optional, b {RPCResult::Type::STR, "timeStr", inner_optional, "Adjusted time for the last update, human friendly"}, {RPCResult::Type::ARR, "session", inner_optional, "", { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::NUM, "llmqType", "Name of quorum"}, + {RPCResult::Type::STR, "llmqType", "Name of quorum"}, GetRpcResult("quorumIndex"), CDKGDebugSessionStatus::GetJsonHelp(/*key=*/"status", /*optional=*/false) }}, @@ -258,7 +234,7 @@ RPCResult CRecoveredSig::GetJsonHelp(const std::string& key, bool optional) { GetRpcResult("llmqType"), GetRpcResult("quorumHash"), - {RPCResult::Type::NUM, "id", "Signing session ID"}, + {RPCResult::Type::STR_HEX, "id", "Signing session ID"}, {RPCResult::Type::STR_HEX, "msgHash", "Hash of message"}, {RPCResult::Type::STR_HEX, "sig", "BLS signature recovered"}, {RPCResult::Type::STR_HEX, "hash", "Hash of the BLS signature recovered"}, diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index f568751d01c9..fd3a4cbbe2f1 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -255,7 +255,7 @@ static RPCHelpMan getbestchainlock() RPCResult{ RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR_HEX, "hash", "The block hash hex-encoded"}, + {RPCResult::Type::STR_HEX, "blockhash", "The block hash hex-encoded"}, {RPCResult::Type::NUM, "height", "The block height or index"}, {RPCResult::Type::STR_HEX, "signature", "The ChainLock's BLS signature"}, {RPCResult::Type::BOOL, "known_block", "True if the block is known by our node"}, diff --git a/src/rpc/coinjoin.cpp b/src/rpc/coinjoin.cpp index bdb91b1da51d..b9a508f0c92e 100644 --- a/src/rpc/coinjoin.cpp +++ b/src/rpc/coinjoin.cpp @@ -427,17 +427,17 @@ static RPCHelpMan getcoinjoininfo() {RPCResult::Type::NUM, "max_amount", "Target CoinJoin balance in " + CURRENCY_UNIT + ""}, {RPCResult::Type::NUM, "denoms_goal", "How many inputs of each denominated amount to target"}, {RPCResult::Type::NUM, "denoms_hardcap", "Maximum limit of how many inputs of each denominated amount to create"}, - {RPCResult::Type::NUM, "queue_size", "How many queues there are currently on the network"}, - {RPCResult::Type::BOOL, "running", "Whether mixing is currently running"}, + {RPCResult::Type::NUM, "queue_size", /*optional=*/true, "How many queues there are currently on the network"}, + {RPCResult::Type::BOOL, "running", /*optional=*/true, "Whether mixing is currently running (not returned when no wallet is loaded)"}, {RPCResult::Type::NUM, "pending_inputs", "The number of successfully mixed inputs kept locked until the transaction spending them is observed"}, - {RPCResult::Type::ARR, "sessions", "", + {RPCResult::Type::ARR, "sessions", /*optional=*/true, "Not returned when no wallet is loaded", { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR_HEX, "protxhash", "The ProTxHash of the masternode"}, - GetRpcResult("outpoint"), - {RPCResult::Type::STR, "service", "The IP address and port of the masternode (DEPRECATED, returned only if config option -deprecatedrpc=service is passed)"}, - {RPCResult::Type::ARR, "addrs_core_p2p", "Network addresses of the masternode used for protocol P2P", + {RPCResult::Type::STR_HEX, "protxhash", /*optional=*/true, "The ProTxHash of the masternode (only while connected to one)"}, + GetRpcResult("outpoint", /*optional=*/true), + {RPCResult::Type::STR, "service", /*optional=*/true, "The IP address and port of the masternode (DEPRECATED, returned only if config option -deprecatedrpc=service is passed)"}, + {RPCResult::Type::ARR, "addrs_core_p2p", /*optional=*/true, "Network addresses of the masternode used for protocol P2P", { {RPCResult::Type::STR, "address", ""}, } @@ -448,7 +448,7 @@ static RPCHelpMan getcoinjoininfo() }}, }}, {RPCResult::Type::NUM, "keys_left", /*optional=*/true, "How many new keys are left since last automatic backup (if applicable)"}, - {RPCResult::Type::STR, "warnings", "Warnings if any"}, + {RPCResult::Type::STR, "warnings", /*optional=*/true, "Warnings if any (not returned when no wallet is loaded)"}, }}, RPCResult{"for masternodes", RPCResult::Type::OBJ, "", "", diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index e15bc74a18ea..cdbc976919bd 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -1513,12 +1513,11 @@ static RPCHelpMan protx_list() RPCResult{ RPCResult::Type::ARR, "", "List of masternodes", { - RPCResult{"when detailed=false", RPCResult::Type::STR, "", "ProTx hash"}, - RPCResult{"when detailed=true", RPCResult::Type::OBJ, "", "", - { - // TODO: document fields of the detailed entry - {RPCResult::Type::ELISION, "", ""} - }}, + // Array elements are matched against this doc by index, so the two shapes + // cannot be listed as alternatives here - the first entry would be applied + // to element 0 only and the second to every element after it. + // TODO: document fields of the detailed entry + {RPCResult::Type::ANY, "", "The ProTx hash when detailed=false, otherwise an object describing the masternode"}, }}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue @@ -1772,11 +1771,11 @@ static RPCHelpMan protx_listdiff() {RPCResult::Type::NUM, "blockHeight", "Height of target (ending) block"}, {RPCResult::Type::ARR, "addedMNs", "Added masternodes", {CDeterministicMN::GetJsonHelp(/*key=*/"", /*optional=*/false)}}, - {RPCResult::Type::ARR, "removedMns", "Removed masternodes", + {RPCResult::Type::ARR, "removedMNs", "Removed masternodes", {{RPCResult::Type::STR_HEX, "protx", "ProTx of removed masternode"}}}, {RPCResult::Type::ARR, "updatedMNs", "Updated masternodes", - {{RPCResult::Type::OBJ, "", "", - {CDeterministicMNStateDiff::GetJsonHelp(/*key=*/"", /*optional=*/false)}}}}, + {{RPCResult::Type::OBJ_DYN, "", "json object with ProTx hash as keys", + {CDeterministicMNStateDiff::GetJsonHelp(/*key=*/"", /*optional=*/false)}}}}, }, }, RPCExamples{""}, diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index a4a97f6596cb..08818c4f5cc0 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -499,7 +499,7 @@ const RPCResult vote_results{ RPCResult::Type::OBJ, "", "", { {RPCResult::Type::STR, "overall", "Total number of successful and failed votes"}, - {RPCResult::Type::OBJ, "detail", "Detailed information for each vote", + {RPCResult::Type::OBJ_DYN, "detail", "Detailed information for each vote, keyed by the ProTx of the voting masternode", { {RPCResult::Type::OBJ, "protx", "ProTx of masternode for voting", { @@ -679,7 +679,7 @@ static RPCHelpMan gobject_list_helper(const bool make_a_diff) }, { RPCResult{"If request is valid", - RPCResult::Type::OBJ, "hash", "Object details", {ListObjectsHelp()}, + RPCResult::Type::OBJ_DYN, "", "json object with governance object hash as keys", {ListObjectsHelp()}, }, RPCResult{"If request is invalid", RPCResult::Type::STR, "", "Error string" @@ -726,10 +726,10 @@ static RPCResult gobject_get_help() { auto ret = CGovernanceObject::GetStateJsonHelp(/*key=*/"", /*optional=*/false, /*local_valid_key=*/"fLocalValidity"); auto mod_inner = ret.m_inner; - mod_inner.push_back({RPCResult::Type::OBJ, "FundingResult", "Funding vote details", {CGovernanceObject::GetVotesJsonHelp(/*key=*/"", /*optional=*/false)}}); - mod_inner.push_back({RPCResult::Type::OBJ, "ValidResult", "Object validity vote details", {CGovernanceObject::GetVotesJsonHelp(/*key=*/"", /*optional=*/false)}}); - mod_inner.push_back({RPCResult::Type::OBJ, "DeleteResult", "Delete vote details", {CGovernanceObject::GetVotesJsonHelp(/*key=*/"", /*optional=*/false)}}); - mod_inner.push_back({RPCResult::Type::OBJ, "EndorsedResult", "Endorsed vote details", {CGovernanceObject::GetVotesJsonHelp(/*key=*/"", /*optional=*/false)}}); + mod_inner.push_back(CGovernanceObject::GetVotesJsonHelp(/*key=*/"FundingResult", /*optional=*/false)); + mod_inner.push_back(CGovernanceObject::GetVotesJsonHelp(/*key=*/"ValidResult", /*optional=*/false)); + mod_inner.push_back(CGovernanceObject::GetVotesJsonHelp(/*key=*/"DeleteResult", /*optional=*/false)); + mod_inner.push_back(CGovernanceObject::GetVotesJsonHelp(/*key=*/"EndorsedResult", /*optional=*/false)); return RPCResult{ret.m_type, ret.m_key_name, ret.m_description, mod_inner}; } diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 9417fee601b8..8d77072e3777 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -95,8 +95,7 @@ static RPCHelpMan masternode_count() { {RPCResult::Type::NUM, "total", "Total number of Masternodes"}, {RPCResult::Type::NUM, "enabled", "Number of enabled Masternodes"}, - {RPCResult::Type::OBJ, "details", "Breakdown of masternodes by type", - {{RPCResult::Type::OBJ, "", "", + {RPCResult::Type::OBJ, "detailed", "Breakdown of masternodes by type", { {RPCResult::Type::OBJ, "regular", "Details for regular masternodes", { @@ -108,7 +107,6 @@ static RPCHelpMan masternode_count() {RPCResult::Type::NUM, "total", "Total number of Evo nodes"}, {RPCResult::Type::NUM, "enabled", "Number of enabled Evo nodes"} }}, - }}, }} } }, @@ -364,13 +362,19 @@ static RPCHelpMan masternode_payments() {RPCResult::Type::NUM, "amount", "Amount received in this block by all masternodes"}, {RPCResult::Type::ARR, "masternodes", "Masternodes that received payments in this block", { - {RPCResult::Type::STR_HEX, "proTxHash", "The hash of the corresponding ProRegTx"}, - {RPCResult::Type::NUM, "amount", "Amount received by this masternode"}, - {RPCResult::Type::ARR, "payees", "Payees who received a share of this payment", + {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR, "address", "Payee address"}, - {RPCResult::Type::STR_HEX, "script", "Payee scriptPubKey"}, - {RPCResult::Type::NUM, "amount", "Amount received by this payee"}, + {RPCResult::Type::STR_HEX, "proTxHash", "The hash of the corresponding ProRegTx"}, + {RPCResult::Type::NUM, "amount", "Amount received by this masternode"}, + {RPCResult::Type::ARR, "payees", "Payees who received a share of this payment", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "Payee address"}, + {RPCResult::Type::STR_HEX, "script", "Payee scriptPubKey"}, + {RPCResult::Type::NUM, "amount", "Amount received by this payee"}, + }}, + }}, }}, }}, }}, @@ -553,12 +557,15 @@ static RPCHelpMan masternodelist_helper(bool is_composite) {"mode", RPCArg::Type::STR, RPCArg::DefaultHint{"json"}, "The mode to run list in"}, {"filter", RPCArg::Type::STR, RPCArg::Default{""}, "Filter results. Partial match by outpoint by default in all modes, additional matches in some modes are also available"}, }, - RPCResult{ - RPCResult::Type::OBJ, "", "", { - RPCResult{"for mode = addr", RPCResult::Type::STR, "
", "Flattened list of all addresses registered to masternode"}, - RPCResult{"for mode = full", RPCResult::Type::STR, "", "Flattened list of a masternode's status, payee address, last paid block's timestamp, height and service addresses"}, - RPCResult{"for mode = info", RPCResult::Type::STR, "", "Flattened list of a masternode's status, payee address and service addresses"}, - RPCResult{"for mode = evo, json or recent", RPCResult::Type::OBJ, "", "", { + { + RPCResult{"for mode = addr", RPCResult::Type::OBJ_DYN, "", "json object with masternode outpoint as keys", + {{RPCResult::Type::STR, "", "Flattened list of all addresses registered to masternode"}}}, + RPCResult{"for mode = full", RPCResult::Type::OBJ_DYN, "", "json object with masternode outpoint as keys", + {{RPCResult::Type::STR, "", "Flattened list of a masternode's status, payee address, last paid block's timestamp, height and service addresses"}}}, + RPCResult{"for mode = info", RPCResult::Type::OBJ_DYN, "", "json object with masternode outpoint as keys", + {{RPCResult::Type::STR, "", "Flattened list of a masternode's status, payee address and service addresses"}}}, + RPCResult{"for mode = evo, json or recent", RPCResult::Type::OBJ_DYN, "", "json object with masternode outpoint as keys", + {{RPCResult::Type::OBJ, "", "", { GetRpcResult("proTxHash"), GetRpcResult("service", /*optional=*/true, /*override_name=*/"address"), GetRpcResult("addresses"), @@ -576,15 +583,21 @@ static RPCHelpMan masternodelist_helper(bool is_composite) GetRpcResult("votingAddress", /*optional=*/false, /*override_name=*/"votingaddress"), GetRpcResult("collateralAddress", /*optional=*/false, /*override_name=*/"collateraladdress"), GetRpcResult("pubKeyOperator", /*optional=*/false, /*override_name=*/"pubkeyoperator"), - }}, - RPCResult{"for mode = lastpaidblock", RPCResult::Type::NUM, "", "Height masternode was last paid"}, - RPCResult{"for mode = lastpaidtime", RPCResult::Type::NUM, "