Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/evo/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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, "<sig_hex>", "Array of quorum indices, keyed by BLS signature", {
{RPCResult::Type::OBJ_DYN, "", "json object with the BLS signature as key", {
{RPCResult::Type::ARR, "<sig_hex>", "Array of quorum indices signed by this BLS signature", {
{RPCResult::Type::NUM, "", "Quorum index"}
}}}}}},
}};
Expand Down
19 changes: 11 additions & 8 deletions src/governance/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Place the RPCResult API backport before its callers

Commit 3344adb uses the new RPCResult constructor argument ending in /skip_type_check=/true here and in rawtransaction.cpp, but that commit's parent only has constructors accepting the old argument shapes. The required m_skip_type_check API is introduced by the following commit f2e13f0, so the first PR commit cannot compile independently even though the final head can. Reorder the API backport before its callers or squash the dependent changes so the permanent history remains buildable and bisectable.

source: ['codex']

}};
}

Expand Down
88 changes: 32 additions & 56 deletions src/llmq/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)};
Comment thread
knst marked this conversation as resolved.
}};
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")}},
}};
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

Expand All @@ -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)
}},
Expand Down Expand Up @@ -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"},
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down
20 changes: 11 additions & 9 deletions src/rpc/coinjoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Comment on lines 427 to 429

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Mark pending_inputs optional for no-wallet CoinJoin info

Making pending_inputs optional fixes the wallet-enabled path where no wallet is selected, but builds without ENABLE_WALLET still fail result checking. getcoinjoininfo remains registered in that configuration, while the entire regular-node producer at lines 476-502 is compiled out, so the handler returns {}. The regular-node schema still requires enabled through denoms_hardcap, and the masternode schema also requires fields absent from {}, so -rpcdoccheck turns this supported reduced response into an internal documentation-check error. Emit the base options outside the wallet guard, make these base fields optional, or add a distinct empty-object result variant.

Suggested change
{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::BOOL, "enabled", /*optional=*/true, "Whether mixing functionality is enabled"},
{RPCResult::Type::BOOL, "multisession", /*optional=*/true, "Whether CoinJoin Multisession option is enabled"},
{RPCResult::Type::NUM, "max_sessions", /*optional=*/true, "How many parallel mixing sessions can there be at once"},
{RPCResult::Type::NUM, "max_rounds", /*optional=*/true, "How many rounds to mix"},
{RPCResult::Type::NUM, "max_amount", /*optional=*/true, "Target CoinJoin balance in " + CURRENCY_UNIT + ""},
{RPCResult::Type::NUM, "denoms_goal", /*optional=*/true, "How many inputs of each denominated amount to target"},
{RPCResult::Type::NUM, "denoms_hardcap", /*optional=*/true, "Maximum limit of how many inputs of each denominated amount to create"},

source: ['codex']

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed reproducing for non-wallet build:

src/dash-cli -regtest getcoinjoininfo
error code: -1
error message:
Internal bug detected: std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [&ret](const RPCResult& res) { return res.MatchesType(ret); })
rpc/util.cpp:533 (HandleRequest)
Dash Core v23.1.7-1031-gb77fd4ac7748-dirty
Please report this issue here: https://github.com/dashpay/dash/issues

Though, please note that bitcoin core originally firstly merged this linter, and after that fixed one-by-one all appearing issues, see:

So, if there's any more special corner cases will be found, they no need to be included in this PR and any more blocked.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in ab89fbeMark pending_inputs optional for no-wallet CoinJoin info no longer present.

Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.

{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, "pending_inputs", "The number of successfully mixed inputs kept locked until the transaction spending them is observed"},
{RPCResult::Type::ARR, "sessions", "",
{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", /*optional=*/true, "The number of successfully mixed inputs kept locked until the transaction spending them is observed (not returned when no wallet is loaded)"},
{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", ""},
}
Expand All @@ -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, "", "",
Expand All @@ -458,6 +458,8 @@ static RPCHelpMan getcoinjoininfo()
{RPCResult::Type::STR_HEX, "state", "Current state of the mixing session"},
{RPCResult::Type::NUM, "entries_count", "The number of entries in the mixing session"},
}},
RPCResult{"for non-masternodes without wallet support",
RPCResult::Type::OBJ, "", /*optional=*/false, "", {}},
},
RPCExamples{
HelpExampleCli("getcoinjoininfo", "")
Expand Down
17 changes: 8 additions & 9 deletions src/rpc/evo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Render nested ANY results before using them in help

When users request help for protx list, this nested RPCResult::Type::ANY is still rendered by RPCResult::ToSections(), whose ANY case calls NONFATAL_UNREACHABLE(); RPCResults::ToDescriptionString() only skips top-level ANY results. This pattern is repeated by the other new nested ANY entries, so those command help paths return an internal-bug error instead of help text. Teach ToSections() how to print ANY, or avoid using it in public nested result docs.

Useful? React with 👍 / 👎.

}},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
Expand Down Expand Up @@ -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, "<protx_hash>", "",
{CDeterministicMNStateDiff::GetJsonHelp(/*key=*/"", /*optional=*/false)}}}},
{{RPCResult::Type::OBJ_DYN, "", "json object with ProTx hash as keys",
{CDeterministicMNStateDiff::GetJsonHelp(/*key=*/"<protx_hash>", /*optional=*/false)}}}},
},
},
RPCExamples{""},
Expand Down
Loading
Loading