Skip to content

backport: bitcoin/bitcoin#23083: rpc: Fail to return undocumented or misdocumented JSON - #7502

Open
knst wants to merge 2 commits into
dashpay:developfrom
knst:bp-v24-23083
Open

backport: bitcoin/bitcoin#23083: rpc: Fail to return undocumented or misdocumented JSON #7502
knst wants to merge 2 commits into
dashpay:developfrom
knst:bp-v24-23083

Conversation

@knst

@knst knst commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

Backport bitcoin#23083 discovered multiple issues in our help for RPC

What was done?

AI crafted fixes for RPC help + backport bitcoin#23083

How Has This Been Tested?

Run unit / functional tests, fix multiple failures such as:

[in regressions tests]
test/rpc_tests.cpp(141): error: in "rpc_tests/rpc_rawparams": unexpected exception thrown by r = CallRPC(std::string("decoderawtransaction ")+rawtx)
unknown location(0): fatal error: in "rpc_tests/rpc_rawparams": UniValue::type_error: JSON value of type null is not of expected type object

[in functional tests]
  File "/home/knst/projects/dash-v22/test/functional/feature_governance.py", line 189, in run_test
mn_list = self.nodes[0].protx("list", "registered", True)
...
test_framework.authproxy.JSONRPCException: 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-955-g8c2b1eb467fc-dirty
Please report this issue here: https://github.com/dashpay/dash/issues

Breaking Changes

Debug builds going to crash every time when RPC help is not matching with real RPC response.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone (for repository code-owners and collaborators only)

knst and others added 2 commits July 29, 2026 21:04
…d JSON

fc892c3 rpc: Fail to return undocumented or misdocumented JSON (MarcoFalke)
f4bc4a7 rpc: Add m_skip_type_check to RPCResult (MarcoFalke)

Pull request description:

  This avoids documentation shortcomings such as the ones fixed in commit e7b6272, 138d55e, 577bd51, f8c84e0, 0ee9a00, 13f4185, or faecb2e

ACKs for top commit:
  fanquake:
    ACK fc892c3 - tested that this catches issue, i.e bitcoin#24691:

Tree-SHA512: 9d0d7e6291bfc6f67541a4ff746d374ad8751fefcff6d103d8621c0298b190ab1d209ce96cfc3a0d4a6a5460a9f9bb790eb96027b16e5ff91f2512e40c92ca84

Co-authored-by: fanquake <fanquake@gmail.com>
@knst knst added this to the 24 milestone Jul 30, 2026
@knst
knst requested review from PastaPastaPasta and UdjinM6 July 30, 2026 18:08
@knst knst added the RPC Some notable changes to RPC params/behaviour/descriptions label Jul 30, 2026
@knst
knst requested a review from thephez July 30, 2026 18:08
@thepastaclaw

thepastaclaw commented Jul 30, 2026

Copy link
Copy Markdown

⛔ Blockers found — Sonnet deferred (commit 6287883)
Canonical validated blockers: 1

@github-actions

Copy link
Copy Markdown

Potential PR merge conflicts

This is advisory only. It does not block CI, but it marks PRs that will likely need a rebase depending on merge order.

If this PR merges first

These open PRs will likely need a rebase:

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR updates RPC help schemas across core, governance, quorum, masternode, transaction, networking, and wallet commands. It adds dynamic-object descriptions, marks conditionally emitted fields optional, corrects field names and types, and documents additional response fields. RPCResult::MatchesType now validates array and object structure, supports dynamic keys, and can skip type checks for flexible result shapes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RPCCommand
  participant RPCResult
  participant UniValue
  RPCCommand->>RPCResult: provide documented result schema
  RPCResult->>UniValue: inspect returned response
  UniValue-->>RPCResult: return keys and values
  RPCResult-->>RPCCommand: validate documented response shape
Loading

Possibly related PRs

  • dashpay/dash#7340: Both changes update masternode diff RPC help schemas in src/evo/core_write.cpp.

Suggested reviewers: udjinm6, pastapastapasta

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is specific and clearly matches the main RPC help/type-checking fixes in this backport.
Description check ✅ Passed The description is directly related to the RPC help backport and the test failures it addresses.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/rpc/evo.cpp (1)

1-1: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Keep production RPCResult schemas renderable in ToSections.

RPCResult::Type::ANY is only for test schemas, but adding it to real help paths such as protx list inserts an unreachable branch into the schema traversal used to emit -help documentation. Replace these production usages with renderable alternatives or make ToSections safely handle skip_type_check/dynamic shapes without relying on ANY.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rpc/evo.cpp` at line 1, Replace production RPCResult::Type::ANY usages in
the evo RPC help schemas, including protx list, with concrete renderable types
or supported dynamic-shape definitions. Ensure the affected schemas remain
traversable by ToSections and do not introduce unreachable branches; reserve ANY
for test-only schemas.
🧹 Nitpick comments (1)
src/rpc/quorums.cpp (1)

117-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a descriptive placeholder instead of "xxxx" for the dynamic quorum-hash key.

This key name is rendered verbatim in generated help text ("xxxx" : {...}) since OBJ_DYN's inner entry is emitted with its m_key_name when nested under an OBJ context. Elsewhere in this same PR, dynamic-key placeholders use a descriptive form (<outpoint>, <sig_hex>, <protx_hash>); consider <quorumHash> here for consistency and clearer generated docs.

✏️ Proposed rename
-                        {RPCResult::Type::OBJ, "xxxx", "Quorum details",
+                        {RPCResult::Type::OBJ, "<quorumHash>", "Quorum details",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rpc/quorums.cpp` around lines 117 - 127, Replace the dynamic quorum-hash
key placeholder "xxxx" in the RPC result schema with the descriptive placeholder
"<quorumHash>", preserving the existing OBJ_DYN structure and quorum detail
fields so generated help text clearly identifies the key.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/governance/core_write.cpp`:
- Around line 122-138: Replace the seven RPCResult::Type::ANY entries in the
data schema with appropriate concrete types so RPCResult::ToSections can render
help without reaching NONFATAL_UNREACHABLE(). Preserve skip_type_check=true on
the enclosing data object, which continues to allow mixed and extra proposal
fields during validation; do not modify unrelated fields such as plain or hex.

In `@src/llmq/core_write.cpp`:
- Around line 22-53: Replace the RPCResult::Type::ANY usage in the member_tally
helper with a schema-specific representation that accurately documents the
detail_level-dependent scalar and array variants without relying on ANY. Update
the six member tally fields in the surrounding DKG result schema to use this
representation while preserving their existing descriptions and runtime help
validation.

In `@src/rpc/evo.cpp`:
- Around line 1507-1515: Replace RPCResult::Type::ANY in the protx list result
declaration with typed result definitions: document the non-detailed array
elements as ProTx-hash strings and add a separate typed array shape for detailed
masternode objects, using the existing RPCResult field definitions where
available. Ensure ToSections() can recurse through both array shapes without
encountering ANY.

In `@src/rpc/rawtransaction.cpp`:
- Around line 576-578: Update the getislocks RPC summary text to state that it
returns the string "None" when no InstantSend lock is known, replacing the
inaccurate reference to Null. Keep the existing implementation and detailed
result documentation unchanged.

In `@src/wallet/rpc/transactions.cpp`:
- Around line 424-445: Update WalletTxToJSON() to filter out the internal
Dash-specific “DS” mapValue entry before serializing mapValue into the
transaction JSON. Preserve serialization of all documented entries and ensure
the RPC output conforms to its declared schema.

In `@src/wallet/rpc/wallet.cpp`:
- Line 126: Update the getwalletinfo RPC result definition for
keypoolsize_hd_internal to match its unconditional emission: remove the optional
flag and clarify that a value of zero indicates no internal keypool, or
alternatively update the producer to omit the field when the feature is inactive
while preserving the documented behavior.

---

Outside diff comments:
In `@src/rpc/evo.cpp`:
- Line 1: Replace production RPCResult::Type::ANY usages in the evo RPC help
schemas, including protx list, with concrete renderable types or supported
dynamic-shape definitions. Ensure the affected schemas remain traversable by
ToSections and do not introduce unreachable branches; reserve ANY for test-only
schemas.

---

Nitpick comments:
In `@src/rpc/quorums.cpp`:
- Around line 117-127: Replace the dynamic quorum-hash key placeholder "xxxx" in
the RPC result schema with the descriptive placeholder "<quorumHash>",
preserving the existing OBJ_DYN structure and quorum detail fields so generated
help text clearly identifies the key.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15abbeec-1c9c-4130-a5ac-badb191f7d73

📥 Commits

Reviewing files that changed from the base of the PR and between 2e52d33 and 6287883.

📒 Files selected for processing (19)
  • src/evo/core_write.cpp
  • src/governance/core_write.cpp
  • src/llmq/core_write.cpp
  • src/rpc/blockchain.cpp
  • src/rpc/coinjoin.cpp
  • src/rpc/evo.cpp
  • src/rpc/governance.cpp
  • src/rpc/masternode.cpp
  • src/rpc/mempool.cpp
  • src/rpc/mining.cpp
  • src/rpc/net.cpp
  • src/rpc/node.cpp
  • src/rpc/quorums.cpp
  • src/rpc/rawtransaction.cpp
  • src/rpc/util.cpp
  • src/rpc/util.h
  • src/wallet/rpc/backup.cpp
  • src/wallet/rpc/transactions.cpp
  • src/wallet/rpc/wallet.cpp

Comment on lines +122 to +138
// 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},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file list candidates =="
git ls-files | rg '(^src/governance/core_write\.cpp$|^src/rpc/util\.cpp$|^src/RPC\.\w+$)' || true

echo "== core_write relevant lines =="
if [ -f src/governance/core_write.cpp ]; then
  nl -ba src/governance/core_write.cpp | sed -n '100,155p'
fi

echo "== util RPCResult outline =="
if [ -f src/rpc/util.cpp ]; then
  ast-grep outline src/rpc/util.cpp --view compact | sed -n '1,220p' || true
fi

echo "== RPCResult::MatchesType =="
rg -n "bool RPCResult::MatchesType|RPCResult::ToSections|NONFATAL_UNREACHABLE|Type::ANY" src/rpc/util.cpp src include -S

echo "== show util around MatchesType/ToSections =="
nl -ba src/rpc/util.cpp | sed -n '1,260p'

Repository: dashpay/dash

Length of output: 296


🏁 Script executed:

#!/bin/bash
set -u

echo "== repo files =="
git ls-files src/governance/core_write.cpp src/rpc/util.cpp

echo "== core_write relevant lines =="
awk 'NR>=100 && NR<=155 { printf "%6d  %s\n", NR, $0 }' src/governance/core_write.cpp

echo "== util RPCResult relevant matches =="
grep -nE "bool RPCResult::MatchesType|RPCResult::ToSections|NONFATAL_UNREACHABLE|Type::ANY|skip_type_check" src/rpc/util.cpp || true

echo "== util RPCResult sections =="
awk 'NR>=1 && NR<=260 { printf "%6d  %s\n", NR, $0 }' src/rpc/util.cpp

Repository: dashpay/dash

Length of output: 15128


🏁 Script executed:

#!/bin/bash
set -u

echo "== util RPCResult ToSections/MatchesType sections =="
awk 'NR>=740 && NR<=905 { printf "%6d  %s\n", NR, $0 }' src/rpc/util.cpp

echo "== util check definitions =="
rg -n "CHECK_NONFATAL|NONFATAL_UNREACHABLE|class ExitSafe|exit_safe" include src/util src -S | head -n 80

echo "== util enum =="
rg -n "enum class Type|Type::ANY|RPCResult" src/rpc/util.h include/rpc src -S | head -n 120

Repository: dashpay/dash

Length of output: 28014


🏁 Script executed:

#!/bin/bash
set -u

echo "== check.h NONFATAL_UNREACHABLE definition =="
awk 'NR>=1 && NR<=110 { printf "%6d  %s\n", NR, $0 }' src/util/check.h

echo "== ToSections call sites =="
grep -n -C 2 "ToSections" src/rpc/util.cpp | head -n 80 || true

echo "== RPCHelpMan help generation call sites =="
grep -n -C 3 "ToSections\|GetRPCHelp|HelpExample.*Man" src/rpc/*.cpp src/rpc/*.h | head -n 120 || true

echo "== non-ANY OBJ fields under skip_type_check objects =="
python3 - <<'PY'
import pathlib, re
for path in pathlib.Path('src').rglob('*.cpp'):
    text = path.read_text(errors='replace')
    # simple brace-based scan for GetJsonHelp constructors with skip_type_check=true and Type::ANY children
    for m in re.finditer(r'RPCResult\s+(?:\{|\w+)\s*,\s*\{\s*Type::OBJ\b', text):
        start = m.end()-1
        depth = 0
        i = start
        while i < len(text) and depth < 4:
            c = text[i]
            if c == '{': depth += 1
            elif c == '}': depth -= 1
            i += 1
        block = text[start:i]
        if 'skip_type_check=true' in block:
            hits = [(block[:500].split('\n',1)[0], re.findall(r'Type::ANY,\s*"([^"]+)",', block))]
            if hits[0][1]:
                print(path)
                for line, any_fields in hits:
                    print(str(line), any_fields)
PY

Repository: dashpay/dash

Length of output: 7640


Avoid RPCResult::Type::ANY fields inside parsed object schemas.

skip_type_check=true already disables MatchesType validation for the enclosing data object, so the 7 fields do not need Type::ANY to permit mixed/extra proposal data. However, RPCResult::ToSections recurses into all object fields regardless of skip_type_check; any -help path that renders this schema recurses into those ANY entries and reaches NONFATAL_UNREACHABLE(), which propagates as a recoverable internal-bug RPC error. Use the appropriate concrete type for each field, or update ToSections/help rendering to handle ANY.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/governance/core_write.cpp` around lines 122 - 138, Replace the seven
RPCResult::Type::ANY entries in the data schema with appropriate concrete types
so RPCResult::ToSections can render help without reaching
NONFATAL_UNREACHABLE(). Preserve skip_type_check=true on the enclosing data
object, which continues to allow mixed and extra proposal fields during
validation; do not modify unrelated fields such as plain or hex.

Comment thread src/llmq/core_write.cpp
Comment on lines +22 to 53
// 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")}},
}};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
git ls-files | rg '(^|/)core_write\.cpp$|rpc.*result|rpc.*doc|llmq.*rpc' | sed -n '1,200p'

echo
echo "== core_write excerpt =="
sed -n '1,120p' src/llmq/core_write.cpp | cat -n

echo
echo "== RPCResult definitions/usages =="
rg -n "enum class Type|RPCResult::Type::ANY|NONFATAL_UNREACHABLE|ToSections|rpcresult|GetRpcResult" src -g '*.cpp' -g '*.h' | sed -n '1,240p'

Repository: dashpay/dash

Length of output: 23318


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import re, pathlib
for path in pathlib.Path('src').rglob('*'):
    if path.is_file() and path.suffix in {'.cpp','.h'}:
        text=path.read_text(errors='ignore')
        if 'RPCResult::Type::ANY' in text or 'ToSections' in text or 'NONFATAL_UNREACHABLE' in text:
            print(path)
PY

Repository: dashpay/dash

Length of output: 464


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== rpc/util.h relevant types =="
sed -n '130,175p' src/rpc/util.h | cat -n
sed -n '250,270p' src/rpc/util.h | cat -n

echo
echo "== rpc/util.cpp relevant doc handling =="
sed -n '450,540p' src/rpc/util.cpp | cat -n
sed -n '740,860p' src/rpc/util.cpp | cat -n

echo
echo "== RPCResult::ANY occurrences with surrounding call context =="
for file in src/rpc/util.cpp src/rpc/server.cpp src/rpc/mempool.cpp src/rpc/node.cpp src/wallet/rpc/transactions.cpp src/wallet/rpc/backup.cpp src/rqt/test/rpcnestedtests.cpp src/governance/core_write.cpp src/llmq/core_write.cpp; do
  [ -f "$file" ] || continue
  echo "--- $file"
  grep -n 'RPCResult::Type::ANY' "$file"
done

echo
echo "== Check call path for GetJsonHelp in rpc/quorums and related llmq rpc =="
fd -a 'quorum|quorums|debug|core_write' src | sort | sed -n '1,120p'
rg -n 'CDKGDebugSessionStatus::GetJsonHelp|CDKGDebugManager::GetJsonHelp|GetJsonHelp|RpcResult|RPCResult' src/llmq src/rpc -g '*.cpp' -g '*.h' | sed -n '1,240p'

Repository: dashpay/dash

Length of output: 12758


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== RPCResult implementation =="
sed -n '148,182p' src/rpc/util.h | cat -n
sed -n '660,760p' src/rpc/util.cpp | cat -n

echo
echo "== DKG help call paths =="
rg -n 'CDKGDebugSessionStatus::GetJsonHelp|CDKGDebugManager::GetJsonHelp|GetJsonHelp|RPCHelpMan|GetRpcResult|DEFAULT_RPC_DOC_CHECK|rpcdoccheck' src/rpc src/llmq src/llmq -g '*.cpp' -g '*.h' | sed -n '1,260p'

echo
echo "== RPCResult::ANY usages and matching logic context =="
sed -n '470,505p' src/rpc/server.cpp | cat -n
sed -n '700,760p' src/rpc/util.cpp | cat -n
sed -n '760,860p' src/rpc/util.cpp | cat -n

Repository: dashpay/dash

Length of output: 35544


Avoid RPCResult::Type::ANY for production DKG help schemas.

RPCResult::ANY is documented and handled as “for testing only” in ToSections and RPC result printing. Since the nested help path for DKG session results will produce this, this needs a schema/helper-specific solution rather than leaving ANY in production help fields. [code_integrity_and_integration]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/llmq/core_write.cpp` around lines 22 - 53, Replace the
RPCResult::Type::ANY usage in the member_tally helper with a schema-specific
representation that accurately documents the detail_level-dependent scalar and
array variants without relying on ANY. Update the six member tally fields in the
surrounding DKG result schema to use this representation while preserving their
existing descriptions and runtime help validation.

Comment thread src/rpc/evo.cpp
Comment on lines 1507 to 1515
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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate relevant files =="
git ls-files | rg '(^|/)rpc/evo\.cpp$|rpcdoc|RPCResult|GetHelp|ToSections' | head -200

echo
echo "== Search for RPCResult::ANY and ToSections implementation/usages =="
rg -n "RPCResult::Type::ANY|enum class Type|static.*ToSections|ToSections|NONFATAL_UNREACHABLE" src include -S | head -250

echo
echo "== Inspect protx list RPCResult docs around target lines =="
wc -l src/rpc/evo.cpp
sed -n '1460,1545p' src/rpc/evo.cpp

echo
echo "== Inspect RPC docs implementation context candidates =="
for f in $(git ls-files | rg '(^src/rpc/|(^src/.*rpc.*))' | rg '(doc|help|result|util)' | head -50); do
  echo "--- $f ---"
  wc -l "$f"
done

Repository: dashpay/dash

Length of output: 3889


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Relevant RPCResult implementation slices =="
sed -n '245,290p' src/rpc/util.h
sed -n '475,510p' src/rpc/util.cpp
sed -n '745,845p' src/rpc/util.cpp
sed -n '960,985p' src/rpc/util.cpp

echo
echo "== Search help generation entry points and protx list command =="
rg -n "help\\(|cmd->GetHelp|GetHelp|HelpText|protx list|protoc\\.list|rpc_commands|command.*=" src/rpc src/node src/init -S | head -250

echo
echo "== Evo file command outline =="
ast-grep outline src/rpc/evo.cpp --view compact 2>/dev/null | sed -n '1,220p' || true
rg -n "protx list|list.*protx|\"protx list\"|help_for.*protx|protx" src/rpc/evo.cpp -S | head -120

echo
echo "== Search any Any typed docs outside tests =="
python3 - <<'PY'
import re
from pathlib import Path
for p in sorted(Path(".").rglob("*.cpp")):
    s=p.read_text(errors='replace')
    if "RPCResult::Type::ANY" in s:
        # print surrounding doc entries only if type before name.
        for m in re.finditer(r'\{RPCResult::Type::ANY\s*,\s*"([^"]*)"', s):
            line=s[:m.start()].count("\n")+1
            txt=m.group(1)
            if "test" not in txt.lower() and line not in (474, 514, 866, 1007, 1107, 233, 1514, 1170):
                print(f"{p}:{line}: {txt}")
PY

Repository: dashpay/dash

Length of output: 21440


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Inspect help implementation path =="
sed -n '75,165p' src/rpc/server.cpp
sed -n '145,205p' src/rpc/server.cpp

echo
echo "== Inspect RPCHelpMan result generation and protx list results =="
rg -n "class RPCHelpMan|struct RPCHelpMan|RPCHelpMan::DescribeCommand|RPCHelpMan::HelpToString|GetHelp|ToDescriptionString\\(" src/rpc src/support -S | head -200
sed -n '1,260p' src/rpc/util.h
rg -n "DescribeCommand|HelpToString|ToDescriptionString\\(|Describe|result" src/rpc/util.cpp -S | head -200

echo
echo "== Read-only parser probe for protx list RPCResult shape from source =="
python3 - <<'PY'
from pathlib import Path
p=Path("src/rpc/evo.cpp")
text=p.read_text()
needle='return RPCHelpMan{"protx list"'
i=text.find(needle)
print("protx list start token at:", i)
out=[]
depth=0
for line_no,line in enumerate(text.splitlines(),1):
    if i!=-1 and len("\n".join(text.splitlines()[:line_no]))>=i:
        out.append((line_no,line))
        depth+=line.count('{')-line.count('}')
        if depth<=0 and any(s in line for s in ['};', '},']):
            break
for row in out[:80]:
    print(f"{row[0]}: {row[1]}")

# Find ToSections handler occurrences and report whether ANY is skipped or unreachable.
util=Path("src/rpc/util.cpp").read_text()
for needle in ["RPCResults::ToDescriptionString", "case Type::ANY", "case Type::ARR:"]:
    idx=util.find(needle)
    print(f"\n--- {needle} at {idx} ---")
    if idx>=0:
        snippet=util[max(0,idx-800):idx+800]
        print("\n".join(f"{n+1}: {ln}" for n,ln in enumerate(snippet.splitlines(), 0) if "ANY" in ln or ("ToDescriptionString" in sn if (sn:=snippet)>=0 else False) or ("case Type::ARR" in ln)))
PY

Repository: dashpay/dash

Length of output: 24362


Use a typed result for protx list instead of RPCResult::Type::ANY.

The array result already handles detailed=false as strings; document that shape and add a separate detailed-array shape if needed. RPCResult::Type::ANY is for testing/type-check bypass only, and recursing through an ARR into ANY in ToSections() hits NONFATAL_UNREACHABLE().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rpc/evo.cpp` around lines 1507 - 1515, Replace RPCResult::Type::ANY in
the protx list result declaration with typed result definitions: document the
non-detailed array elements as ProTx-hash strings and add a separate typed array
shape for detailed masternode objects, using the existing RPCResult field
definitions where available. Ensure ToSections() can recurse through both array
shapes without encountering ANY.

Comment on lines +576 to +578
// An element is the plain string "None" when no InstantSend Lock is known for
// that txid, so the element type cannot be checked against this object.
/*skip_type_check=*/true},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Update the getislocks summary to document the string "None".

The implementation returns "None" at Line 619, and this change now explicitly documents that string. However, the RPC summary still says it returns Null, which describes the wrong JSON value type. Update the summary text accordingly.

Proposed documentation fix
-        "\nReturns the raw InstantSend lock data for each txids. Returns Null if there is no known IS yet.",
+        "\nReturns the raw InstantSend lock data for each txids. Returns the string \"None\" if there is no known IS yet.",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// An element is the plain string "None" when no InstantSend Lock is known for
// that txid, so the element type cannot be checked against this object.
/*skip_type_check=*/true},
"\nReturns the raw InstantSend lock data for each txids. Returns the string \"None\" if there is no known IS yet.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rpc/rawtransaction.cpp` around lines 576 - 578, Update the getislocks RPC
summary text to state that it returns the string "None" when no InstantSend lock
is known, replacing the inaccurate reference to Null. Keep the existing
implementation and detailed result documentation unchanged.

Comment on lines +424 to +445
{RPCResult::Type::BOOL, "instantlock_internal", "Current internal transaction lock state. Available for 'send' and 'receive' category of transactions"},
{RPCResult::Type::BOOL, "chainlock", "The state of the corresponding block ChainLock"},
{RPCResult::Type::BOOL, "generated", /*optional=*/true, "Only present if the transaction's only input is a coinbase one."},
{RPCResult::Type::BOOL, "platform-transfer", /*optional=*/true, "Only present if the transaction is a Platform Transfer."},
{RPCResult::Type::BOOL, "trusted", /*optional=*/true, "Whether we consider the transaction to be trusted and safe to spend from.\n"
"Only present when the transaction has 0 confirmations (or negative confirmations, if conflicted)."},
{RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block hash containing the transaction. Available for 'send' and 'receive'\n"
"category of transactions."},
{RPCResult::Type::STR_HEX, "blockheight", /*optional=*/true, "The block height containing the transaction."},
{RPCResult::Type::NUM, "blockheight", /*optional=*/true, "The block height containing the transaction."},
{RPCResult::Type::NUM, "blockindex", /*optional=*/true, "The index of the transaction in the block that includes it. Available for 'send' and 'receive'\n"
"category of transactions."},
{RPCResult::Type::NUM_TIME, "blocktime", /*optional=*/true, "The block time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR_HEX, "txid", "The transaction id. Available for 'send' and 'receive' category of transactions."},
{RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
}},
{RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + ". Available \n"
"for 'send' and 'receive' category of transactions."},
{RPCResult::Type::STR, "comment", /*optional=*/true, "If a comment is associated with the transaction."}};
{RPCResult::Type::STR, "comment", /*optional=*/true, "If a comment is associated with the transaction."},
{RPCResult::Type::STR, "to", /*optional=*/true, "If a comment to is associated with the transaction."}};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate target file and relevant functions"
fd -a 'transactions\.cpp$|transactions\.h$' src/wallet/rpc 2>/dev/null || true

echo
echo "Target snippet around lines 424-445"
if [ -f src/wallet/rpc/transactions.cpp ]; then
  sed -n '380,520p' src/wallet/rpc/transactions.cpp | nl -ba -v380
fi

echo
echo "Search WalletTxToJSON and RPCResult validation references"
rg -n "WalletTxToJSON|mapValue|RPCResult::MatchesType|DS|CoinJoin|DS_INQUEUE|DS_FINAL" src -S | head -200

echo
echo "Files containing RPCResult::MatchesType"
rg -n "MatchesType|RPCResult::Type::STR|RPCResult::Type::OBJ" src test -S | grep -E "(rpc|wallet)" | head -200

Repository: dashpay/dash

Length of output: 321


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Target file availability"
[ -f src/wallet/rpc/transactions.cpp ] && echo "src/wallet/rpc/transactions.cpp present"

echo
echo "Target file size"
wc -l src/wallet/rpc/transactions.cpp

echo
echo "Target snippet around lines 424-445"
sed -n '380,520p' src/wallet/rpc/transactions.cpp

echo
echo "Search WalletTxToJSON, mapValue, RPCResult, DS"
rg -n "WalletTxToJSON|mapValue|RPCResult::MatchesType|RPCResult|STR_HEX|DS_INQUEUE|DS_FINAL|DS_|CoinJoin" src/wallet/rpc src/wallet src/main src/primitives src -S | head -300

Repository: dashpay/dash

Length of output: 49490


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "WalletTxToJSON implementation"
sed -n '1,90p' src/wallet/rpc/transactions.cpp

echo
echo "Listtransactions CoinJoin DS reading section"
sed -n '330,375p' src/wallet/rpc/transactions.cpp

echo
echo "listtransactions schema wrapper"
sed -n '436,530p' src/wallet/rpc/transactions.cpp

echo
echo "listsinceblock/gettransaction schema wrapper references"
sed -n '540,615p' src/wallet/rpc/transactions.cpp
sed -n '700,755p' src/wallet/rpc/transactions.cpp

echo
echo "Find RPCResult definition and MatchesType"
rg -n "class RPCHelpMan|struct RPCResult|RPCResult::.*Type::|MatchesType|static.*RPCHelpMan" src/wallet/rpc src/wallet src/ -S | head -250

Repository: dashpay/dash

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate RPCResult implementation"
rg -n "struct RPCResult|class RPCResult|RPCHelpMan|MatchesType" src/wallet/rpc src/rpc src -S --glob '*.h' --glob '*.cpp' | head -150

echo
echo "Inspect RPCResult header/source candidates"
for f in $(rg -l "struct RPCResult|class RPCResult|static void RPCHelpManCheck|static bool MatchesType" src -S --glob '*.h' --glob '*.cpp'); do
  echo "--- $f"
  wc -l "$f"
  rg -n "struct RPCResult|class RPCResult|RPCHelpManCheck|static bool MatchesType|static void RPCHelpManCheck" "$f" -C 12
done

echo
echo "Read candidate file ranges"
for f in $(rg -l "struct RPCResult|class RPCResult" src -S --glob '*.h' --glob '*.cpp' | head -5); do
  echo "--- $f"
  sed -n '1,220p' "$f"
done

Repository: dashpay/dash

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "RPCResult implementation"
sed -n '250,580p' src/rpc/util.h
sed -n '1,260p' src/rpc/util.cpp

echo
echo "Call sites for RpcHelpManagerCheck/HelpValidate and result checks"
rg -n "HelpValidate|HelpValidateAfter\(|Check.*Result|RPCResult.*result|RPCResults|CallRPC|RPCHelpManCheck|HelpCheck|Result" src/rpc/src src/wallet/rpc -S --glob '*.h' --glob '*.cpp' | head -200

echo
echo "Search exact RPCResult struct definition text"
sed -n '262,390p' src/rpc/util.h

echo
echo "Search RPC help result validation exact methods"
rg -n "RPCResult::.*MatchesType|Checks RPC result|result.*matches|Check.*rpc result" src/rpc src/wallet/rpc -S

Repository: dashpay/dash

Length of output: 42112


Drop undocumented mapValue keys before RPC serialization.

WalletTxToJSON() pushes every wtx.mapValue entry into the transaction JSON. The CoinJoin classification uses a Dash-specific DS entry, but the RPC result schemas do not expose it; remove internal/wallet metadata before looping over mapValue or add the entry to the schema with a regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/wallet/rpc/transactions.cpp` around lines 424 - 445, Update
WalletTxToJSON() to filter out the internal Dash-specific “DS” mapValue entry
before serializing mapValue into the transaction JSON. Preserve serialization of
all documented entries and ensure the RPC output conforms to its declared
schema.

Comment thread src/wallet/rpc/wallet.cpp
{RPCResult::Type::NUM_TIME, "timefirstkey", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " of the oldest known key in the wallet. Legacy wallets only"},
{RPCResult::Type::NUM_TIME, "keypoololdest", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only"},
{RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
{RPCResult::Type::NUM, "keypoolsize_hd_internal", /*optional=*/ true, "how many new keys are pre-generated for internal use (used for change outputs and mobile coinjoin, only appears if the wallet is using this feature, otherwise external keys are used)"},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not mark keypoolsize_hd_internal optional while always emitting it.

getwalletinfo() unconditionally adds this field, including when no internal keypool feature is active. Keep it required and clarify that zero means no internal pool, or change the producer to omit it under the documented condition.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/wallet/rpc/wallet.cpp` at line 126, Update the getwalletinfo RPC result
definition for keypoolsize_hd_internal to match its unconditional emission:
remove the optional flag and clarify that a value of zero indicates no internal
keypool, or alternatively update the producer to omit the field when the feature
is inactive while preserving the documented behavior.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Preliminary review — Codex only

The upstream backport and most Dash-specific schema adaptations are coherent, but the newly introduced nested ANY entries violate the existing help-renderer contract and make several RPC help pages throw NonFatalCheckError. The revised mempool result documentation also omits the normal "false" value for instantlock, so changes are required before merge.

Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed), gpt-5.6-sol — backport-reviewer (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 1 blocking | 💬 1 nitpick(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/llmq/core_write.cpp`:
- [BLOCKING] src/llmq/core_write.cpp:28-32: Nested ANY result types make RPC help throw
  `RPCResult::Type::ANY` is only skipped when it is the top-level result. Nested entries are passed to `RPCResult::ToSections()`, whose `Type::ANY` branch calls `NONFATAL_UNREACHABLE()` and throws `NonFatalCheckError`. Therefore, rendering `help quorum dkgstatus` traverses this member-tally entry and fails instead of returning help. The same PR-introduced problem affects the nested `ANY` entries in `src/governance/core_write.cpp:127-133`, `src/rpc/rawtransaction.cpp:474`, `src/rpc/evo.cpp:1514`, and `src/wallet/rpc/transactions.cpp:868`. The new `m_skip_type_check` flag only affects `MatchesType()` and does not prevent help rendering. Add a displayable representation for nested `ANY`, or use displayable result types with type checking skipped.

Comment thread src/llmq/core_write.cpp
Comment on lines +28 to +32
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)};

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: Nested ANY result types make RPC help throw

RPCResult::Type::ANY is only skipped when it is the top-level result. Nested entries are passed to RPCResult::ToSections(), whose Type::ANY branch calls NONFATAL_UNREACHABLE() and throws NonFatalCheckError. Therefore, rendering help quorum dkgstatus traverses this member-tally entry and fails instead of returning help. The same PR-introduced problem affects the nested ANY entries in src/governance/core_write.cpp:127-133, src/rpc/rawtransaction.cpp:474, src/rpc/evo.cpp:1514, and src/wallet/rpc/transactions.cpp:868. The new m_skip_type_check flag only affects MatchesType() and does not prevent help rendering. Add a displayable representation for nested ANY, or use displayable result types with type checking skipped.

source: ['codex']

Comment thread src/rpc/mempool.cpp
RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
RPCResult{RPCResult::Type::BOOL, "instantsend", "True if this transaction was locked via InstantSend"},
RPCResult{RPCResult::Type::STR, "instantlock", "\"true\" if this transaction was locked via InstantSend, \"unknown\" if InstantSend is unavailable"},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💬 Nitpick: Document the unlocked instantlock value

entryToJSON() serializes isman->IsLocked(...) through util::to_string(bool), which returns either "true" or "false"; it returns "unknown" only when the InstantSend manager is unavailable. The revised documentation omits the normal "false" response value, leaving this result incompletely documented in a PR specifically correcting RPC result schemas.

Suggested change
RPCResult{RPCResult::Type::STR, "instantlock", "\"true\" if this transaction was locked via InstantSend, \"unknown\" if InstantSend is unavailable"},
RPCResult{RPCResult::Type::STR, "instantlock", "\"true\" if this transaction was locked via InstantSend, \"false\" if it was not locked, and \"unknown\" if InstantSend is unavailable"},

source: ['codex']

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RPC Some notable changes to RPC params/behaviour/descriptions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants