Skip to content

rpc: port RPCHelpMan infrastructure from Bitcoin Core v26.0#2923

Merged
jamescowens merged 3 commits into
gridcoin-community:developmentfrom
PrestackI:rpc/rpchelpman-infrastructure
May 14, 2026
Merged

rpc: port RPCHelpMan infrastructure from Bitcoin Core v26.0#2923
jamescowens merged 3 commits into
gridcoin-community:developmentfrom
PrestackI:rpc/rpchelpman-infrastructure

Conversation

@PrestackI

Copy link
Copy Markdown
Contributor

Summary

Infrastructure PR for #2922 — ports the help-text rendering and argument-count validation subset of Bitcoin Core v26.0's RPCHelpMan machinery into src/rpc/util.{h,cpp} so that the ~165 existing RPC commands can be incrementally converted to structured help documentation.

No command conversions are included in this PR. Tier 1/2/3 conversions will follow in separate per-command PRs using the pattern:

UniValue somecommand(const UniValue& params, bool fHelp)
{
    static const RPCHelpMan help{
        "somecommand", "Description.",
        { ... RPCArg specs ... },
        RPCResult{ ... },
        RPCExamples{ ... },
    };
    if (fHelp || !help.IsValidNumArgs(params.size()))
        throw std::runtime_error(help.ToString());
    // ... command logic ...
}

The existing (const UniValue& params, bool fHelp) RPC signature is untouched.

What's ported

  • RPCHelpMan with ToString(), IsValidNumArgs(), GetArgNames()
  • RPCArg / RPCArgOptions with all v26.0 Type variants and the Fallback variant (Optional / DefaultHint / Default)
  • RPCResult / RPCResults with all v26.0 Type variants and the four constructor overloads
  • RPCExamples
  • Sections internal pretty-printer (byte-for-byte v26.0)
  • HelpExampleCli / HelpExampleCliNamed / HelpExampleRpc / HelpExampleRpcNamed (adapted to emit gridcoinresearch and port 9332)
  • UNIX_EPOCH_TIME, EXAMPLE_ADDRESS[2] (the latter with placeholder strings + a TODO(#2922) for the first Tier 1 PR to replace with real Gridcoin addresses)

What's deliberately omitted

The v26.0 dispatch-path features — HandleRequest, the RPCMethodImpl lambda-overload constructor, GetArgMap, Arg<R> / MaybeArg<R> typed accessors, RPCArg::MatchesType, RPCResult::MatchesType — all depend on upstream's JSONRPCRequest struct, which Gridcoin does not have. Dragging it in would require changing rpcfn_type from UniValue(const UniValue&, bool) to UniValue(const JSONRPCRequest&) and mechanically rewriting every RPC command file, which would conflict with the Tier 2/3 conversion PRs the #2922 plan calls for.

The landed API is a strict subset of the v26.0 public surface. A future modernization PR can grow it into the full dispatch-path shape without touching any call site added in the interim — nothing gets re-ported later, only extended.

Commands whose help text or argument list depends on runtime state (addkey by protocol version, addpoll by enabled poll types, walletpassphrase by wallet lock state) can construct RPCHelpMan per-call rather than as static const. A dedicated unit test exercises this pattern.

Drive-by fixes

Two latent bugs surfaced while landing the port and are fixed here because they block it:

  1. PACKAGE_BUGREPORT was never plumbed through. CHECK_NONFATAL in src/util/check.h references PACKAGE_BUGREPORT in its error message, but the macro was never defined in src/config/gridcoin-config.h.cmake.in. CHECK_NONFATAL has been dormant dead code — this port is the first Gridcoin consumer, so the dangling reference would have caused any future use to fail. Added the definition pointing at the GitHub issues page.

  2. src/rpc/ files used #include "util.h" (quoted form) which searches the including file's directory first. With no util.h previously under src/rpc/, those quoted includes fell through to src/util.h (Gridcoin's general utility header with LogPrintf, gArgs, LogInstance, etc.). Adding src/rpc/util.h caused blockchain.h, client.cpp, dataacq.cpp, misc.cpp, protocol.cpp, and server.cpp to start resolving to the new RPCHelpMan header instead. Switched those 6 files to #include <util.h> which unambiguously resolves via the -I src include path. Other files outside src/rpc/ are unaffected.

Test plan

  • CMake Quality Gate passes (lint + sanitizers build clean)
  • CMake Compatibility Builds pass
  • CMake Production Builds pass
  • CMake Distribution Native Builds pass
  • New rpchelpman_tests suite exercises ToString, IsValidNumArgs boundaries, GetArgNames, HelpExample* formatting, every RPCArg::Type scalar, RPCResult::ToDescriptionString for OBJ/ARR/OBJ_DYN/STR_HEX, per-call (non-static) construction, and the Tier 1 throw pattern
  • Existing test suite unchanged (no regressions; this PR adds new files only, plus one-line additions to src/CMakeLists.txt and src/test/CMakeLists.txt, plus the include-form fix in the 6 src/rpc/ files)
  • Follow-up: Tier 1/2/3 conversion PRs can start immediately against this infrastructure

Refs: #2922

Introduce src/rpc/util.{h,cpp} with the reduced-subset RPCHelpMan machinery
(help-text rendering and argument-count validation) needed to convert the ~165
existing RPC commands to structured help documentation as tracked in gridcoin-community#2922.
This is the infrastructure PR; no command conversions are included.

## What's ported

Ported verbatim from bitcoin/bitcoin@v26.0 src/rpc/util.{h,cpp}:

- RPCHelpMan class with the single (name, description, args, results, examples)
  constructor, plus ToString(), IsValidNumArgs(), and GetArgNames()
- RPCArg / RPCArgOptions with all v26.0 Type enum values (OBJ, ARR, STR, NUM,
  BOOL, OBJ_NAMED_PARAMS, OBJ_USER_KEYS, AMOUNT, STR_HEX, RANGE), Fallback
  variant (Optional / DefaultHint / Default), IsOptional, ToString (oneline
  and multiline), ToStringObj, ToDescriptionString
- RPCResult with all v26.0 Type enum values (OBJ, ARR, STR, NUM, BOOL, NONE,
  ANY, STR_AMOUNT, STR_HEX, OBJ_DYN, ARR_FIXED, NUM_TIME, ELISION), the four
  constructors (with/without cond, with/without explicit optional), ToSections,
  ToStringObj, ToDescriptionString, CheckInnerDoc
- RPCResults and RPCExamples wrappers
- Sections internal pretty-printer (unchanged; v26.0 behaviour byte-for-byte)
- HelpExampleCli, HelpExampleCliNamed, HelpExampleRpc, HelpExampleRpcNamed
  (adapted to use `gridcoinresearch` and Gridcoin's RPC port 9332 in place of
  the Bitcoin Core equivalents)
- UNIX_EPOCH_TIME constant
- EXAMPLE_ADDRESS[2] constant (with placeholder strings + a TODO(gridcoin-community#2922) to
  replace with real Gridcoin example addresses in the first Tier 1 PR that
  references them)

## What's deliberately omitted

The following v26.0 features are dispatch-path features that depend on
upstream's JSONRPCRequest struct, which Gridcoin does not yet have:

- RPCHelpMan::HandleRequest
- The second RPCHelpMan constructor taking an RPCMethodImpl lambda
- RPCHelpMan::GetArgMap
- Arg<R> / MaybeArg<R> / ArgValue<R> typed accessor templates
- RPCArg::MatchesType / RPCResult::MatchesType (runtime type validation,
  only called from HandleRequest)
- m_fun, m_req, GetParamIndex

Dragging JSONRPCRequest in would require a wide signature sweep across every
RPC command file (changing rpcfn_type from
`UniValue(const UniValue&, bool fHelp)` to `UniValue(const JSONRPCRequest&)`)
which would conflict with the expected Tier 2/3 conversion PRs for gridcoin-community#2922.

The API landed here is a **strict subset** of the v26.0 public surface.  A
future modernization PR can grow it into the full dispatch-path shape without
touching any call site added in the interim — nothing gets re-ported later,
only extended.

## Call-site pattern for Tier 1/2/3 conversions

The existing `(const UniValue& params, bool fHelp)` RPC signature is
untouched.  Conversions use the upstream idiom directly:

    UniValue somecommand(const UniValue& params, bool fHelp)
    {
        static const RPCHelpMan help{
            "somecommand", "Description of the command.",
            { ... RPCArg specs ... },
            RPCResult{ ... },
            RPCExamples{ ... },
        };
        if (fHelp || !help.IsValidNumArgs(params.size()))
            throw std::runtime_error(help.ToString());
        // ... command logic ...
    }

For commands whose help text or argument list depends on runtime state
(addkey varying by protocol version, addpoll varying by enabled poll types,
walletpassphrase varying by wallet lock state), RPCHelpMan may be constructed
per-call instead of as `static const`.  Construction is cheap — all fields
are held by value and moved in.  A dedicated unit test exercises this
dynamic-construction pattern.

## Drive-by fixes

Two latent bugs surfaced while landing this port and are fixed here because
they block it:

1. **PACKAGE_BUGREPORT was never plumbed through.**  CHECK_NONFATAL in
   src/util/check.h references PACKAGE_BUGREPORT in its error message, but
   the macro was never defined in src/config/gridcoin-config.h.cmake.in.
   CHECK_NONFATAL has been dormant dead code — this port is the first
   Gridcoin consumer, so the dangling reference would have caused any future
   use to fail.  Adds the definition pointing at the GitHub issues page.

2. **src/rpc/ files used #include "util.h" (quotes)** which searches the
   directory of the including file first.  Before this branch, there was no
   util.h in src/rpc/, so the quoted include fell through to src/util.h
   (Gridcoin's general utility header with LogPrintf, gArgs, LogInstance,
   etc.).  Adding src/rpc/util.h caused blockchain.h, client.cpp, dataacq.cpp,
   misc.cpp, protocol.cpp, and server.cpp to start resolving that include
   to the new RPCHelpMan header and lose access to the logging/args symbols.
   Switch those 6 files to `#include <util.h>` which unambiguously resolves
   via the `-I src` include path.  Other files (src/wallet/*, src/qt/*,
   etc.) are unaffected because their own directories do not contain a
   colliding util.h.

## Tests

Adds src/test/rpchelpman_tests.cpp covering:

- RPCHelpMan::ToString() output for a trivial spec (description, arguments
  section, result section, examples section)
- IsValidNumArgs() boundary cases: zero required, all-optional, mixed
  required/optional, exact match, underflow, overflow
- GetArgNames() for aliased arg names
- HelpExampleCli / HelpExampleCliNamed / HelpExampleRpc formatting (including
  the `gridcoinresearch` cli name and port 9332)
- RPCArg::ToString() for every scalar Type variant (STR, STR_HEX, NUM,
  AMOUNT, RANGE, BOOL)
- RPCResults::ToDescriptionString() for STR_HEX, OBJ, ARR, and OBJ_DYN
  result shapes
- Per-call (non-static) RPCHelpMan construction with runtime-computed args
  and description (validates the addkey/addpoll dynamic-help pattern)
- The `throw std::runtime_error(help.ToString())` pattern that Tier 1
  conversions will use when fHelp is set or arg count is wrong

Refs: gridcoin-community#2922

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Ports a subset of Bitcoin Core v26.0’s RPCHelpMan infrastructure into Gridcoin to enable structured, consistent RPC help text and argument-count validation, while keeping the existing (const UniValue& params, bool fHelp) RPC function signature.

Changes:

  • Add src/rpc/util.{h,cpp} implementing RPCHelpMan, RPCArg, RPCResult(s), RPCExamples, and help example helpers.
  • Add a new rpchelpman_tests suite and wire it into the test build.
  • Fix build plumbing and include resolution issues: define PACKAGE_BUGREPORT and switch src/rpc/* includes from "util.h" to <util.h> to avoid the new src/rpc/util.h collision.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/rpc/util.h Introduces the new public RPCHelpMan/RPCArg/RPCResult APIs and constants.
src/rpc/util.cpp Implements help rendering, argument-count validation, and example formatting helpers.
src/test/rpchelpman_tests.cpp Adds unit tests for help rendering, arg-count validation, arg names, and example formatting.
src/test/CMakeLists.txt Registers the new rpchelpman_tests.cpp in the test target.
src/CMakeLists.txt Adds rpc/util.cpp to the gridcoin_util static library.
src/rpc/{blockchain.h,client.cpp,dataacq.cpp,misc.cpp,protocol.cpp,server.cpp} Changes #include "util.h" to #include <util.h> to prevent wrong-header resolution.
src/config/gridcoin-config.h.cmake.in Defines PACKAGE_BUGREPORT for CHECK_NONFATAL messages.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/rpc/util.cpp
Comment on lines +143 to +173
switch (arg.m_type) {
case RPCArg::Type::STR_HEX:
case RPCArg::Type::STR:
case RPCArg::Type::NUM:
case RPCArg::Type::AMOUNT:
case RPCArg::Type::RANGE:
case RPCArg::Type::BOOL:
case RPCArg::Type::OBJ_NAMED_PARAMS: {
if (is_top_level_arg) return; // Nothing more to do for non-recursive types on first recursion
auto left = indent;
if (arg.m_opts.type_str.size() != 0 && push_name) {
left += "\"" + arg.GetName() + "\": " + arg.m_opts.type_str.at(0);
} else {
left += push_name ? arg.ToStringObj(/*oneline=*/false) : arg.ToString(/*oneline=*/false);
}
left += ",";
PushSection({left, arg.ToDescriptionString(/*is_named_arg=*/push_name)});
break;
}
case RPCArg::Type::OBJ:
case RPCArg::Type::OBJ_USER_KEYS: {
const auto right = is_top_level_arg ? "" : arg.ToDescriptionString(/*is_named_arg=*/push_name);
PushSection({indent + (push_name ? "\"" + arg.GetName() + "\": " : "") + "{", right});
for (const auto& arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::OBJ);
}
if (arg.m_type != RPCArg::Type::OBJ) {
PushSection({indent_next + "...", ""});
}
PushSection({indent + "}" + (is_top_level_arg ? "" : ","), ""});
break;

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

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

RPCArg::Type::OBJ_NAMED_PARAMS is treated as a non-recursive scalar in Sections::Push(), so a top-level options object won't have its { ... } structure rendered in the Arguments section (the function returns early on the first recursion), and nested OBJ_NAMED_PARAMS values would be rendered via ToStringObj()/ToString() even though this type is constructed with m_inner. Consider handling OBJ_NAMED_PARAMS in the same branch as OBJ (emit {}, recurse into m_inner, and add ... as appropriate) so the help output correctly documents the options object structure in all contexts.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not a bug — leaving as-is. OBJ_NAMED_PARAMS intentionally lands in the non-recursive branch here. Top-level OBJ_NAMED_PARAMS args are rendered by the separate named_only_sections loop in RPCHelpMan::ToString() (~util.cpp:524-528), which spins up its own Sections for the inner keys. Folding it into the OBJ branch would double-render — once under "Arguments:", once under "Named Arguments:". This matches upstream Bitcoin Core v26.0 behavior.

Comment thread src/rpc/util.cpp
Comment on lines +632 to +634
case Type::OBJ_USER_KEYS:
// Currently unused, so avoid writing dead code
CHECK_NONFATAL(false);

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

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

RPCArg::ToStringObj() hard-aborts for Type::OBJ, Type::OBJ_NAMED_PARAMS, and Type::OBJ_USER_KEYS ("Currently unused"), but RPCArg::ToString() calls ToStringObj() on every m_inner element when rendering object-type arguments. This will fail for nested object fields (i.e., an object containing another object), which is a supported shape in upstream RPCHelpMan docs. Implement object-type handling in ToStringObj() (or avoid routing object inners through ToStringObj()), so nested objects can be represented without triggering CHECK_NONFATAL(false).

Suggested change
case Type::OBJ_USER_KEYS:
// Currently unused, so avoid writing dead code
CHECK_NONFATAL(false);
case Type::OBJ_USER_KEYS: {
const std::string inner = Join(m_inner, std::string(","), [&](const RPCArg& i) { return i.ToStringObj(oneline); });
if (m_type == Type::OBJ) {
return res + "{" + inner + "}";
} else {
return res + "{" + inner + ",...}";
}
}

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Real footgun, but the suggested patch isn't quite right and the scope here is "strict subset of v26.0". ToStringObj() is verbatim from upstream v26.0, which has the same NONFATAL_UNREACHABLE() and the same de facto "no nested objects in m_inner" constraint — tolerated upstream because no v26.0 command nests objects two deep. Addressed in c98f5d0 by documenting it instead of changing behavior: a warning comment + TODO(#2922) above ToStringObj() tells spec authors to avoid nested object-typed args until the implementation is extended, and the first Tier 1/2/3 conversion that needs it will implement the real rendering. No behavior change in an infrastructure-only PR.

Comment thread src/rpc/util.cpp

// The key name if recursed into a dictionary
const std::string maybe_key{
outer_type == OuterType::OBJ ? "\"" + this->m_key_name + "\" : " : ""};

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

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

RPCResult::ToSections() formats dictionary keys as "key" : (note the space before the colon). This is inconsistent with the other JSON-like snippets emitted by this renderer (and with typical JSON formatting), and makes the help output harder to compare to upstream/expected JSON. Consider changing it to "key": .

Suggested change
outer_type == OuterType::OBJ ? "\"" + this->m_key_name + "\" : " : ""};
outer_type == OuterType::OBJ ? "\"" + this->m_key_name + "\": " : ""};

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cosmetic and inherited — leaving as-is. The "\" : " literal is verbatim from v26.0 util.cpp:954. The rest of the renderer uses "\": ", so output is internally inconsistent, but Bitcoin Core has shipped this across four releases without complaint and this PR's stated scope is a strict subset of the v26.0 public surface. Changing it would be a gratuitous deviation from upstream; better left for a dedicated cosmetic-sync pass if upstream ever fixes it.

Comment thread src/rpc/util.cpp
case RPCArg::Type::OBJ_NAMED_PARAMS: {
if (is_top_level_arg) return; // Nothing more to do for non-recursive types on first recursion
auto left = indent;
if (arg.m_opts.type_str.size() != 0 && push_name) {

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

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

RPCArgOptions::type_str is documented as having length 0 or 2, but the code only checks size() != 0 before indexing with .at(0)/.at(1) (here and in RPCArg::ToDescriptionString). If a caller accidentally supplies a vector of length 1, help rendering will throw at runtime. Consider enforcing the invariant with CHECK_NONFATAL(type_str.size() == 2) (or switching to a fixed-size type like std::array<std::string,2>/a small struct) before indexing.

Suggested change
if (arg.m_opts.type_str.size() != 0 && push_name) {
if (!arg.m_opts.type_str.empty()) {
CHECK_NONFATAL(arg.m_opts.type_str.size() == 2);
}
if (arg.m_opts.type_str.size() == 2 && push_name) {

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Partially valid — the length-1 std::vector::at out-of-range throw is real, but the right enforcement point is the RPCArg constructors, not the use sites. Fixed in c98f5d0: a CHECK_NONFATAL(m_opts.type_str.empty() || m_opts.type_str.size() == 2) in both RPCArg constructor bodies catches a misshapen spec at program init rather than at first help <cmd> call. Cheap insurance, and it fails fast at the source instead of deep in the renderer.

@jamescowens jamescowens left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Code review

Reviewed at HEAD 2fbf4af486fae5de1a73d82329c778b8c2e592b7. Also cross-checked Copilot's four inline comments. Verdicts below, then three additions.

Copilot comment triage

util.cpp:153 (type_str length) — partially valid. The length-1 std::vector::at out-of-range throw path is real, but it's inherited verbatim from Bitcoin Core v26.0 and the contract is caller-side. The right enforcement point is the RPCArg constructors in util.h (~164, ~181) rather than the use sites Copilot suggested — a one-line CHECK_NONFATAL(type_str.empty() || type_str.size() == 2) in each constructor body catches the bad spec at program init instead of at first help <cmd> call. Cheap insurance.

util.cpp:173 (OBJ_NAMED_PARAMS in Sections::Push) — not a bug, don't change. OBJ_NAMED_PARAMS intentionally lands in the non-recursive branch. Top-level OBJ_NAMED_PARAMS args are rendered by the separate named_only_sections loop in RPCHelpMan::ToString() (around util.cpp:524-528), which spins up its own Sections for the inner keys. Merging it into the OBJ branch would double-render — once in "Arguments:", once in "Named Arguments:". This matches upstream v26.0 behavior.

util.cpp:505 (space-before-colon in "key" : ) — cosmetic and inherited. The "\" : " literal is verbatim from v26.0 util.cpp:954. The rest of the renderer uses "\": " so output is internally inconsistent, but Bitcoin has been shipping this for four releases without complaint and this PR's stated scope is "strict subset of v26.0 public surface." Leave alone.

util.cpp:634 (ToStringObj() aborts on object types) — real footgun, partially valid. RPCArg::ToString() calls ToStringObj() on every m_inner for OBJ / OBJ_NAMED_PARAMS / OBJ_USER_KEYS, and ToStringObj() aborts on those same three types via CHECK_NONFATAL(false). Any OBJ-nested-in-OBJ spec would detonate. Identical to upstream v26.0 (NONFATAL_UNREACHABLE()), and tolerated there because no v26.0 command nests objects two deep in m_inner. For the Tier 1/2/3 conversions in #2922 this is likely safe, but specs like createrawtransaction's outputs or an options inside a named-params object would hit it. Worth at minimum a comment at the ToStringObj() definition warning spec authors, with a TODO to extend the implementation when a command first needs it.

Additional findings

  1. Test-suite coverage gap. src/test/rpchelpman_tests.cpp exercises RPCResult of OBJ / ARR / OBJ_DYN (lines 182-218) but no test constructs an RPCArg of type OBJ, OBJ_NAMED_PARAMS, OBJ_USER_KEYS, or ARR. That means Sections::Push recursion, the Named Arguments: branch of RPCHelpMan::ToString(), RPCArg::ToStringObj(), and the GetArgNames OBJ_NAMED_PARAMS inner-expansion (around util.cpp:255-258) are all untouched by the new suite. First Tier 1 conversion with nested args will exercise these live — cheaper to add coverage here. Also incidentally covers the Copilot #4 assertion-trap footgun and the type_str-length concern from #1.

  2. GetName() alias constraint undocumented. CHECK_NONFATAL(std::string::npos == m_names.find('|')) at util.cpp:539 means aliases ("foo|bar" names) are only allowed on top-level args — any OBJ/OBJ_USER_KEYS inner with an aliased name will trip it via Sections::Push. Same upstream constraint but nothing in util.h flags it to spec authors. Low priority — would trip in review — but one-line comment on RPCArg::m_names would help.

  3. Drive-by #include change verified correct and necessary. Without the PR, a quoted #include "util.h" from src/rpc/blockchain.h or src/rpc/server.cpp would silently shift from src/util.h (with LogPrintf/gArgs/LogInstance) to the colocated new src/rpc/util.h. The angle-bracket form resolves unambiguously via -Isrc. Required for this PR to link.

  4. PACKAGE_BUGREPORT drive-by verified. src/util/check.h:32-41 expands PACKAGE_BUGREPORT in CHECK_NONFATAL's error message. No prior call sites exist in the tree (grep confirms). This port is the first consumer. Definition addition is correct and strictly necessary.

  5. "Strict subset of v26.0" claim verified. Diffed the ported surface against upstream; everything in this PR has an identical v26.0 signature. MatchesType, HandleRequest, Arg<T>, MaybeArg<T>, GetArgMap, m_fun, m_req, RPCMethodImpl all cleanly omitted — nothing ported here depends on anything not ported.

  6. CLAUDE.md member-init rule satisfied. All ctors use initializer lists; RPCResult ctors delegate correctly.

Overall

Mergeable after the three minor cleanups above (constructor assertion, ToStringObj nested-OBJ warning, one OBJ-typed-arg test case). The include-form and PACKAGE_BUGREPORT drive-bys are necessary and correct.

🤖 Generated with Claude Code

Resolves the actionable items from the gridcoin-community#2923 review pass:

util.h: enforce the documented RPCArgOptions::type_str length-0-or-2
invariant at construction time via CHECK_NONFATAL in both RPCArg
constructors. Misshapen specs now fail at startup rather than at
first help-rendering call. Also extend the m_names doc comment to
note that aliased names ("foo|bar") are only valid on top-level
args (GetName() asserts no '|', and Sections::Push routes inner
args through GetName() unconditionally).

util.cpp: document the v26.0-inherited footgun in
RPCArg::ToStringObj(): the function CHECK_NONFATAL(false)s for
Type::OBJ / OBJ_NAMED_PARAMS / OBJ_USER_KEYS, but RPCArg::ToString()
routes object-typed args' inner elements through ToStringObj(), so
any nested-object spec will detonate when help is rendered. Comment
warns spec authors and adds a TODO(gridcoin-community#2922) to implement object-type
rendering when the first command that needs it is converted. No
behavior change.

test/rpchelpman_tests.cpp: add rpcarg_obj_with_scalar_inner_renders
covering the previously-untested OBJ-typed RPCArg shape with scalar
inner fields. Exercises Sections::Push recursion, the
"Arguments:" rendering of OBJ wrappers, and ToStringObj() for
scalar inners. Does not test nested OBJ-in-OBJ — that path is the
documented footgun above.
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 1, 2026
Convert settxfee to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the below-minimum-amount UX bug: today the help
condition is `fHelp || params.size() < 1 || params.size() > 1 ||
AmountFromValue(params[0]) < nMinFee`, so a below-min amount throws
the help text wrapped as RPC_MISC_ERROR (-1) — useless to the caller.

Hoist the AmountFromValue and minimum-fee check out of the help
condition; below-min now throws JSONRPCError(RPC_INVALID_PARAMETER,
"txfee X below minimum Y") with both values formatted, so the caller
gets actionable feedback. The 0-arg path is unchanged in behavior:
IsValidNumArgs(0) returns false and the help is thrown, just as the
short-circuit `params.size() < 1` produced before.

Add a per-command help-render test in rpchelpman_tests.cpp asserting
that fHelp=true throws a runtime_error whose message contains the
expected RPCHelpMan markers.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 1, 2026
…id command

Convert addnode to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() != 2 || (strCommand != "onetry"
&& strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be one of: \"add\", \"remove\", \"onetry\""). The
client-visible code change (-1 -> -8) and the loss of the embedded
help dump are intentional and noted in the bundle PR description.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 1, 2026
…d command

Convert setban to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() < 2 || params.size() > 4 ||
(strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be \"add\" or \"remove\""). The pre-validation
isNull guard on params[1] is no longer needed since IsValidNumArgs
guarantees >= 2 args.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 1, 2026
…unbounded arity

Convert listsinceblock to the RPCHelpMan pattern landed in gridcoin-community#2923.
The original help text is truncated mid-sentence (ends at
`"  \"transactions\": [\n"` with no closing brace, no description,
no examples), and the function performs no arity validation,
silently accepting calls with 4+ arguments.

The RPCHelpMan literal supplies a complete help body and
IsValidNumArgs(params.size()) now rejects > 3 args. The transaction
object schema is rendered as RPCResult::Type::ELISION because
ListTransactions emits variable shapes depending on MinedType /
category and pinning a schema would misrepresent the API. Refer
callers to `listtransactions` for the per-entry layout.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 1, 2026
Resolves the actionable items from the gridcoin-community#2923 review pass:

util.h: enforce the documented RPCArgOptions::type_str length-0-or-2
invariant at construction time via CHECK_NONFATAL in both RPCArg
constructors. Misshapen specs now fail at startup rather than at
first help-rendering call. Also extend the m_names doc comment to
note that aliased names ("foo|bar") are only valid on top-level
args (GetName() asserts no '|', and Sections::Push routes inner
args through GetName() unconditionally).

util.cpp: document the v26.0-inherited footgun in
RPCArg::ToStringObj(): the function CHECK_NONFATAL(false)s for
Type::OBJ / OBJ_NAMED_PARAMS / OBJ_USER_KEYS, but RPCArg::ToString()
routes object-typed args' inner elements through ToStringObj(), so
any nested-object spec will detonate when help is rendered. Comment
warns spec authors and adds a TODO(gridcoin-community#2922) to implement object-type
rendering when the first command that needs it is converted. No
behavior change.

test/rpchelpman_tests.cpp: add rpcarg_obj_with_scalar_inner_renders
covering the previously-untested OBJ-typed RPCArg shape with scalar
inner fields. Exercises Sections::Push recursion, the
"Arguments:" rendering of OBJ wrappers, and ToStringObj() for
scalar inners. Does not test nested OBJ-in-OBJ — that path is the
documented footgun above.
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 1, 2026
Convert settxfee to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the below-minimum-amount UX bug: today the help
condition is `fHelp || params.size() < 1 || params.size() > 1 ||
AmountFromValue(params[0]) < nMinFee`, so a below-min amount throws
the help text wrapped as RPC_MISC_ERROR (-1) — useless to the caller.

Hoist the AmountFromValue and minimum-fee check out of the help
condition; below-min now throws JSONRPCError(RPC_INVALID_PARAMETER,
"txfee X below minimum Y") with both values formatted, so the caller
gets actionable feedback. The 0-arg path is unchanged in behavior:
IsValidNumArgs(0) returns false and the help is thrown, just as the
short-circuit `params.size() < 1` produced before.

Add a per-command help-render test in rpchelpman_tests.cpp asserting
that fHelp=true throws a runtime_error whose message contains the
expected RPCHelpMan markers.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 1, 2026
…id command

Convert addnode to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() != 2 || (strCommand != "onetry"
&& strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be one of: \"add\", \"remove\", \"onetry\""). The
client-visible code change (-1 -> -8) and the loss of the embedded
help dump are intentional and noted in the bundle PR description.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 1, 2026
…d command

Convert setban to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() < 2 || params.size() > 4 ||
(strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be \"add\" or \"remove\""). The pre-validation
isNull guard on params[1] is no longer needed since IsValidNumArgs
guarantees >= 2 args.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 1, 2026
…unbounded arity

Convert listsinceblock to the RPCHelpMan pattern landed in gridcoin-community#2923.
The original help text is truncated mid-sentence (ends at
`"  \"transactions\": [\n"` with no closing brace, no description,
no examples), and the function performs no arity validation,
silently accepting calls with 4+ arguments.

The RPCHelpMan literal supplies a complete help body and
IsValidNumArgs(params.size()) now rejects > 3 args. The transaction
object schema is rendered as RPCResult::Type::ELISION because
ListTransactions emits variable shapes depending on MinedType /
category and pinning a schema would misrepresent the API. Refer
callers to `listtransactions` for the per-entry layout.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 11, 2026
Resolves the actionable items from the gridcoin-community#2923 review pass:

util.h: enforce the documented RPCArgOptions::type_str length-0-or-2
invariant at construction time via CHECK_NONFATAL in both RPCArg
constructors. Misshapen specs now fail at startup rather than at
first help-rendering call. Also extend the m_names doc comment to
note that aliased names ("foo|bar") are only valid on top-level
args (GetName() asserts no '|', and Sections::Push routes inner
args through GetName() unconditionally).

util.cpp: document the v26.0-inherited footgun in
RPCArg::ToStringObj(): the function CHECK_NONFATAL(false)s for
Type::OBJ / OBJ_NAMED_PARAMS / OBJ_USER_KEYS, but RPCArg::ToString()
routes object-typed args' inner elements through ToStringObj(), so
any nested-object spec will detonate when help is rendered. Comment
warns spec authors and adds a TODO(gridcoin-community#2922) to implement object-type
rendering when the first command that needs it is converted. No
behavior change.

test/rpchelpman_tests.cpp: add rpcarg_obj_with_scalar_inner_renders
covering the previously-untested OBJ-typed RPCArg shape with scalar
inner fields. Exercises Sections::Push recursion, the
"Arguments:" rendering of OBJ wrappers, and ToStringObj() for
scalar inners. Does not test nested OBJ-in-OBJ — that path is the
documented footgun above.
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 11, 2026
Convert settxfee to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the below-minimum-amount UX bug: today the help
condition is `fHelp || params.size() < 1 || params.size() > 1 ||
AmountFromValue(params[0]) < nMinFee`, so a below-min amount throws
the help text wrapped as RPC_MISC_ERROR (-1) — useless to the caller.

Hoist the AmountFromValue and minimum-fee check out of the help
condition; below-min now throws JSONRPCError(RPC_INVALID_PARAMETER,
"txfee X below minimum Y") with both values formatted, so the caller
gets actionable feedback. The 0-arg path is unchanged in behavior:
IsValidNumArgs(0) returns false and the help is thrown, just as the
short-circuit `params.size() < 1` produced before.

Add a per-command help-render test in rpchelpman_tests.cpp asserting
that fHelp=true throws a runtime_error whose message contains the
expected RPCHelpMan markers.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 11, 2026
…id command

Convert addnode to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() != 2 || (strCommand != "onetry"
&& strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be one of: \"add\", \"remove\", \"onetry\""). The
client-visible code change (-1 -> -8) and the loss of the embedded
help dump are intentional and noted in the bundle PR description.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 11, 2026
…d command

Convert setban to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() < 2 || params.size() > 4 ||
(strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be \"add\" or \"remove\""). The pre-validation
isNull guard on params[1] is no longer needed since IsValidNumArgs
guarantees >= 2 args.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 11, 2026
…unbounded arity

Convert listsinceblock to the RPCHelpMan pattern landed in gridcoin-community#2923.
The original help text is truncated mid-sentence (ends at
`"  \"transactions\": [\n"` with no closing brace, no description,
no examples), and the function performs no arity validation,
silently accepting calls with 4+ arguments.

The RPCHelpMan literal supplies a complete help body and
IsValidNumArgs(params.size()) now rejects > 3 args. The transaction
object schema is rendered as RPCResult::Type::ELISION because
ListTransactions emits variable shapes depending on MinedType /
category and pinning a schema would misrepresent the API. Refer
callers to `listtransactions` for the per-entry layout.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922

@jamescowens jamescowens left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

All three actionable items from the review pass are resolved in c98f5d0:

  • RPCArgOptions::type_str length-0-or-2 invariant now enforced via CHECK_NONFATAL in both RPCArg constructors — misshapen specs fail at startup, not at first help render.
  • RPCArg::ToStringObj()'s v26.0-inherited nested-object footgun is now documented with a warning comment + TODO(#2922) above the function definition.
  • New rpcarg_obj_with_scalar_inner_renders test covers the previously-untested OBJ-typed RPCArg shape (Sections::Push recursion, "Arguments:" rendering, ToStringObj() for scalar inners).
  • Bonus: the m_names doc comment now spells out the alias-only-on-top-level constraint.

The four Copilot inline comments were triaged in the review body and now have inline replies. Verdicts unchanged: 173 (OBJ_NAMED_PARAMS) is not a bug — intentional non-recursive branch matching upstream; 505 (space-before-colon) is cosmetic and verbatim from v26.0, left for a dedicated upstream-sync pass; 643 and 153 are addressed by c98f5d0 as above.

CI is fully green. The "strict subset of v26.0" claim holds — nothing ported depends on anything omitted. Drive-by fixes (PACKAGE_BUGREPORT definition, src/rpc/* quoted-include → angle-bracket) are both necessary for this PR to link and verified correct.

Approving. This is the infrastructure base the #2922 Tier 1/2/3 conversion PRs build on.

@jamescowens jamescowens merged commit 490e5b4 into gridcoin-community:development May 14, 2026
21 checks passed
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 15, 2026
Convert settxfee to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the below-minimum-amount UX bug: today the help
condition is `fHelp || params.size() < 1 || params.size() > 1 ||
AmountFromValue(params[0]) < nMinFee`, so a below-min amount throws
the help text wrapped as RPC_MISC_ERROR (-1) — useless to the caller.

Hoist the AmountFromValue and minimum-fee check out of the help
condition; below-min now throws JSONRPCError(RPC_INVALID_PARAMETER,
"txfee X below minimum Y") with both values formatted, so the caller
gets actionable feedback. The 0-arg path is unchanged in behavior:
IsValidNumArgs(0) returns false and the help is thrown, just as the
short-circuit `params.size() < 1` produced before.

Add a per-command help-render test in rpchelpman_tests.cpp asserting
that fHelp=true throws a runtime_error whose message contains the
expected RPCHelpMan markers.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 15, 2026
…id command

Convert addnode to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() != 2 || (strCommand != "onetry"
&& strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be one of: \"add\", \"remove\", \"onetry\""). The
client-visible code change (-1 -> -8) and the loss of the embedded
help dump are intentional and noted in the bundle PR description.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 15, 2026
…d command

Convert setban to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() < 2 || params.size() > 4 ||
(strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be \"add\" or \"remove\""). The pre-validation
isNull guard on params[1] is no longer needed since IsValidNumArgs
guarantees >= 2 args.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 15, 2026
…unbounded arity

Convert listsinceblock to the RPCHelpMan pattern landed in gridcoin-community#2923.
The original help text is truncated mid-sentence (ends at
`"  \"transactions\": [\n"` with no closing brace, no description,
no examples), and the function performs no arity validation,
silently accepting calls with 4+ arguments.

The RPCHelpMan literal supplies a complete help body and
IsValidNumArgs(params.size()) now rejects > 3 args. The transaction
object schema is rendered as RPCResult::Type::ELISION because
ListTransactions emits variable shapes depending on MinedType /
category and pinning a schema would misrepresent the API. Refer
callers to `listtransactions` for the per-entry layout.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 23, 2026
Convert settxfee to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the below-minimum-amount UX bug: today the help
condition is `fHelp || params.size() < 1 || params.size() > 1 ||
AmountFromValue(params[0]) < nMinFee`, so a below-min amount throws
the help text wrapped as RPC_MISC_ERROR (-1) — useless to the caller.

Hoist the AmountFromValue and minimum-fee check out of the help
condition; below-min now throws JSONRPCError(RPC_INVALID_PARAMETER,
"txfee X below minimum Y") with both values formatted, so the caller
gets actionable feedback. The 0-arg path is unchanged in behavior:
IsValidNumArgs(0) returns false and the help is thrown, just as the
short-circuit `params.size() < 1` produced before.

Add a per-command help-render test in rpchelpman_tests.cpp asserting
that fHelp=true throws a runtime_error whose message contains the
expected RPCHelpMan markers.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 23, 2026
…id command

Convert addnode to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() != 2 || (strCommand != "onetry"
&& strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be one of: \"add\", \"remove\", \"onetry\""). The
client-visible code change (-1 -> -8) and the loss of the embedded
help dump are intentional and noted in the bundle PR description.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 23, 2026
…d command

Convert setban to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() < 2 || params.size() > 4 ||
(strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be \"add\" or \"remove\""). The pre-validation
isNull guard on params[1] is no longer needed since IsValidNumArgs
guarantees >= 2 args.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 23, 2026
…unbounded arity

Convert listsinceblock to the RPCHelpMan pattern landed in gridcoin-community#2923.
The original help text is truncated mid-sentence (ends at
`"  \"transactions\": [\n"` with no closing brace, no description,
no examples), and the function performs no arity validation,
silently accepting calls with 4+ arguments.

The RPCHelpMan literal supplies a complete help body and
IsValidNumArgs(params.size()) now rejects > 3 args. The transaction
object schema is rendered as RPCResult::Type::ELISION because
ListTransactions emits variable shapes depending on MinedType /
category and pinning a schema would misrepresent the API. Refer
callers to `listtransactions` for the per-entry layout.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 23, 2026
Convert settxfee to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the below-minimum-amount UX bug: today the help
condition is `fHelp || params.size() < 1 || params.size() > 1 ||
AmountFromValue(params[0]) < nMinFee`, so a below-min amount throws
the help text wrapped as RPC_MISC_ERROR (-1) — useless to the caller.

Hoist the AmountFromValue and minimum-fee check out of the help
condition; below-min now throws JSONRPCError(RPC_INVALID_PARAMETER,
"txfee X below minimum Y") with both values formatted, so the caller
gets actionable feedback. The 0-arg path is unchanged in behavior:
IsValidNumArgs(0) returns false and the help is thrown, just as the
short-circuit `params.size() < 1` produced before.

Add a per-command help-render test in rpchelpman_tests.cpp asserting
that fHelp=true throws a runtime_error whose message contains the
expected RPCHelpMan markers.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 23, 2026
…id command

Convert addnode to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() != 2 || (strCommand != "onetry"
&& strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be one of: \"add\", \"remove\", \"onetry\""). The
client-visible code change (-1 -> -8) and the loss of the embedded
help dump are intentional and noted in the bundle PR description.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 23, 2026
…d command

Convert setban to the RPCHelpMan pattern landed in gridcoin-community#2923. As a
side effect, fix the invalid-subcommand UX bug: today the help
condition is `fHelp || params.size() < 2 || params.size() > 4 ||
(strCommand != "add" && strCommand != "remove")`, so a typo'd
subcommand throws the help text wrapped as RPC_MISC_ERROR (-1).

Split the validation: arity is enforced by IsValidNumArgs(), and an
invalid subcommand now throws JSONRPCError(RPC_INVALID_PARAMETER,
"command must be \"add\" or \"remove\""). The pre-validation
isNull guard on params[1] is no longer needed since IsValidNumArgs
guarantees >= 2 args.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
PrestackI added a commit to PrestackI/Gridcoin-Research that referenced this pull request May 23, 2026
…unbounded arity

Convert listsinceblock to the RPCHelpMan pattern landed in gridcoin-community#2923.
The original help text is truncated mid-sentence (ends at
`"  \"transactions\": [\n"` with no closing brace, no description,
no examples), and the function performs no arity validation,
silently accepting calls with 4+ arguments.

The RPCHelpMan literal supplies a complete help body and
IsValidNumArgs(params.size()) now rejects > 3 args. The transaction
object schema is rendered as RPCResult::Type::ELISION because
ListTransactions emits variable shapes depending on MinedType /
category and pinning a schema would misrepresent the API. Refer
callers to `listtransactions` for the per-entry layout.

Add a per-command help-render test in rpchelpman_tests.cpp.

Refs: gridcoin-community#2922
@PrestackI PrestackI deleted the rpc/rpchelpman-infrastructure branch May 24, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants