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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 8 additions & 37 deletions ci/dash/lint-tidy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,45 +94,16 @@ python3 "${CLANG_TIDY_CACHE_PY}" --show-stats 2>&1 || true
cleanup_ctcache "${CTCACHE_DIR}" "${CTCACHE_MAXSIZE_MB}"
echo "=========================="

# Filter out files by regex here, because regex may not be
# accepted in src/.bear-tidy-config
# Filter out:
# * qt qrc and moc generated files
# * secp256k1
jq 'map(select(.file | test("src/qt/qrc_.*\\.cpp$|/moc_.*\\.cpp$|src/secp256k1/src/") | not))' ../compile_commands.json > tmp.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude walletutil before running IWYU

In the checked linux64_multiprocess path, .github/workflows/build-src.yml:209-216 invokes this script, but this regexp does not match src/wallet/walletutil.cpp even though lines 99-102 explicitly declare it temporarily excluded. Removing the old source whitelist therefore sends that known-unready translation unit through iwyu_tool.py and fix_includes.py; add src/wallet/walletutil.cpp to the filter so the intended exclusion actually applies.

AGENTS.md reference: AGENTS.md:L197-L202

Useful? React with 👍 / 👎.

mv tmp.json ../compile_commands.json
Comment on lines +102 to +103

@thepastaclaw thepastaclaw Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

✅ Withdrawn at 0d188488; see the replies below.

🟡 Suggestion: Retain bitcoin#27571's walletutil exclusion

Removing the IWYU whitelist now includes src/wallet/walletutil.cpp, although the new comment explicitly lists walletutil as temporarily excluded. Upstream merge 4c3d67a includes src/wallet/walletutil in this exact filter before broadening IWYU to all sources; the Dash adaptation drops that alternative. walletutil.cpp remains a compiled wallet source, so the omission changes the filtering behavior rather than merely leaving a stale comment. Restore the upstream exclusion, or establish why it is no longer needed for Dash. No resulting build failure is asserted.

Suggested change
jq 'map(select(.file | test("src/qt/qrc_.*\\.cpp$|/moc_.*\\.cpp$|src/secp256k1/src/") | not))' ../compile_commands.json > tmp.json
mv tmp.json ../compile_commands.json
jq 'map(select(.file | test("src/qt/qrc_.*\\.cpp$|/moc_.*\\.cpp$|src/wallet/walletutil|src/secp256k1/src/") | not))' ../compile_commands.json > tmp.json
mv tmp.json ../compile_commands.json

source: muse-spark-1.3-contributor (phase1-reviewer: general); gpt-6.1-sol (phase2-reviewer: general, backport-reviewer, dash-core-commit-history)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Withdrawn (re-reviewed at 0d188488): Your base already contains bitcoin#27759's wallet-header fixes, which are the same changes that let upstream remove its temporary walletutil exclusion. The current filter is therefore appropriate; the stale copied comment does not justify restoring an obsolete exclusion.


cd "${BASE_ROOT_DIR}/build-ci/dashcore-${BUILD_TARGET}"
iwyu_tool.py \
"src/common/init.cpp" \
"src/common/url.cpp" \
"src/compat" \
"src/dbwrapper.cpp" \
"src/init" \
"src/kernel" \
"src/node/chainstate.cpp" \
"src/node/chainstatemanager_args.cpp" \
"src/node/mempool_args.cpp" \
"src/node/utxo_snapshot.cpp" \
"src/node/validation_cache_args.cpp" \
"src/node/minisketchwrapper.cpp" \
"src/policy/feerate.cpp" \
"src/policy/packages.cpp" \
"src/policy/settings.cpp" \
"src/primitives/transaction.cpp" \
"src/random.cpp" \
"src/rpc/fees.cpp" \
"src/rpc/signmessage.cpp" \
"src/test/fuzz/string.cpp" \
"src/test/fuzz/txorphan.cpp" \
"src/test/fuzz/util/" \
"src/util/bip32.cpp" \
"src/util/bytevectorhash.cpp" \
"src/util/check.cpp" \
"src/util/error.cpp" \
"src/util/exception.cpp" \
"src/util/hasher.cpp" \
"src/util/message.cpp" \
"src/util/moneystr.cpp" \
"src/util/serfloat.cpp" \
"src/util/spanparsing.cpp" \
"src/util/string.cpp" \
"src/util/strencodings.cpp" \
"src/util/syserror.cpp" \
"src/util/threadinterrupt.cpp" \
"src/zmq" \
-p . "${MAKEJOBS}" \
-- -Xiwyu --cxx17ns -Xiwyu --mapping_file="${BASE_ROOT_DIR}/contrib/devtools/iwyu/bitcoin.core.imp" \
-Xiwyu --max_line_length=160 \
Expand Down
2 changes: 1 addition & 1 deletion ci/lint/04_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ python3 --version
# NOTE: BUMP ALSO contrib/containers/ci/ci-slim.Dockerfile
${CI_RETRY_EXE} pip3 install \
codespell==2.2.1 \
flake8==5.0.4 \
flake8==6.1.0 \
lief==0.13.2 \
mypy==0.981 \
pyzmq==24.0.1 \
Expand Down
3 changes: 2 additions & 1 deletion ci/test/00_setup_env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

export LC_ALL=C.UTF-8

set -ex

# The root dir.
# The ci system copies this folder.
BASE_ROOT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../ >/dev/null 2>&1 && pwd )
Expand Down Expand Up @@ -52,7 +54,6 @@ export RUN_STDLIB_PATH_CHECK=${RUN_STDLIB_PATH_CHECK:-false}
export TEST_RUNNER_TIMEOUT_FACTOR=${TEST_RUNNER_TIMEOUT_FACTOR:-4}
export RUN_FUZZ_TESTS=${RUN_FUZZ_TESTS:-false}

export CONTAINER_NAME=${CONTAINER_NAME:-ci_unnamed}
# Randomize test order.
# See https://www.boost.org/doc/libs/1_71_0/libs/test/doc/html/boost_test/utf_reference/rt_param_reference/random.html
export BOOST_TEST_RANDOM=${BOOST_TEST_RANDOM:-1}
Expand Down
2 changes: 2 additions & 0 deletions ci/test/00_setup_env_mac_native.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ export CI_OS_NAME="macos"
export NO_DEPENDS=1
export OSX_SDK=""
export CCACHE_MAXSIZE=400M
export RUN_FUZZ_TESTS=true
export FUZZ_TESTS_CONFIG="--exclude banman" # https://github.com/bitcoin/bitcoin/issues/27924
Comment on lines +18 to +19

@thepastaclaw thepastaclaw Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

✅ Withdrawn at bc1963cb; see the replies below.

🟡 Suggestion: Preserve bitcoin#27932's macOS fuzz CI coverage

These exports reproduce upstream's configuration additions but do not enable corpus replay in Dash CI. Upstream's native macOS job selects the modified environment and ci/test/06_script_b.sh consumes RUN_FUZZ_TESTS and FUZZ_TESTS_CONFIG to launch the runner. Dash's mac-build job instead selects 00_setup_env_mac_cross.sh through ci/dash/matrix.sh, and no checked CI execution script consumes these settings. All four upstream changed areas have counterparts in this backport; the remaining defect is the ineffective CI adaptation, not an omitted production API or a demonstrated runtime failure. Adapt an existing appropriate job to execute macOS replay without adding a matrix job, or explicitly document the partial coverage and link a tracking issue before deferring it.

source: gpt-6.1-sol (phase2-reviewer: general)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

These exports are kept so the env file stays identical to upstream and later backports apply cleanly. Dash CI never replays the fuzz corpus on any platform: no script in ci/dash or .github/workflows calls test/fuzz/test_runner.py or reads RUN_FUZZ_TESTS, not even for linux64_fuzz, and no workflow selects 00_setup_env_mac_native.sh. Adding macOS corpus replay would be a new CI feature, not part of this backport. The bitcoin#27932 code changes (test_runner.py, rpc.cpp, config.ini.in) are fully included.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You're right: linux64_fuzz only builds the fuzz executable, and Dash's CI has no corpus-replay consumer for these settings; that gap predates this backport. The runner/config/RPC changes are present, and enabling macOS replay would require new CI execution wiring rather than preserving coverage Dash already had. I overstated this as a backport defect and withdraw the finding; no tracking issue is required to lift it.

Withdrawing this finding.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Withdrawn (re-reviewed at bc1963cb): Your explanation is confirmed by the execution scripts: Dash CI does not consume RUN_FUZZ_TESTS or replay the corpus on Linux either, and this backport includes all four upstream changed areas. Adding macOS corpus execution would introduce new Dash CI behavior, so this finding remains withdrawn.

3 changes: 2 additions & 1 deletion contrib/containers/ci/ci-slim.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ RUN set -ex; \
curl \
g++ \
git \
jq \
libbz2-dev \
liblzma-dev \
libreadline-dev \
Expand Down Expand Up @@ -79,7 +80,7 @@ ENV UV_SYSTEM_PYTHON=1
# NOTE: if versions are changed, update ci/lint/04_install.sh
RUN uv pip install --system --break-system-packages \
codespell==2.2.1 \
flake8==5.0.4 \
flake8==6.1.0 \

@thepastaclaw thepastaclaw Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

✅ Withdrawn at 2552f141; see the replies below.

🔴 Blocking: Adapt Dash type-comment imports for the bitcoin#28194 lint upgrade

The upgrade from flake8 5.0.4 to 6.1.0 leaves Dash-specific imports incompatible with the new checker. CI run 41651190122, job 125993768620, fails lint-python.py with F401 for MasternodeInfo in feature_governance.py, feature_llmq_connections.py, feature_llmq_singlenode.py, feature_mnehf.py and interface_zmq_dash.py. In those files the name appears only in loop type comments, which the newer checker does not count as import uses. Remove the unused imports or replace the comments with supported typing constructs, retaining the synchronized flake8 pins. This is a demonstrated validation regression from the tool upgrade, not merely a request for stylistic cleanup.

source: gpt-6.1-sol (phase2-reviewer: general, backport-reviewer, dash-core-commit-history)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Resolved (re-reviewed at 0d188488): You removed the comment-only MasternodeInfo imports and associated type comments from all five affected files since the supplied prior head. Both flake8 pins remain at 6.1.0, and the current-head lint check passes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Withdrawn (re-reviewed at 2552f141): Both pins are 6.1.0 and the five Dash-specific tests remove the unused imports with their loop type comments. I verified these adaptations were already present at bc1963c as well, so the original blocker was unsupported at the prior reviewed head rather than a newly fixed defect.

jinja2 \
lief==0.13.2 \
mypy==0.981 \
Expand Down
16 changes: 16 additions & 0 deletions doc/release-notes-26485.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
JSON-RPC
---

For RPC methods which accept `options` parameters ((`importmulti`, `listunspent`, `fundrawtransaction`, `send`, `sendall`, `walletcreatefundedpsbt`, `simulaterawtransaction`), it is now possible to pass the options as named parameters without the need for a nested object. (#8010)

This means it is possible make calls like:

```sh
src/dash-cli -named listunspent coinType=1
```

instead of

```sh
src/dash-cli -named listunspent query_options='{"coinType": 1}'
```
4 changes: 4 additions & 0 deletions src/bench/wallet_create_tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();

// Set clock to genesis block, so the descriptors/keys creation time don't interfere with the blocks scanning process.
SetMockTime(test_setup->m_node.chainman->GetParams().GenesisBlock().nTime);
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
{
LOCK(wallet.cs_wallet);
Expand Down Expand Up @@ -137,6 +139,8 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType>& output_type)
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
// Set clock to genesis block, so the descriptors/keys creation time don't interfere with the blocks scanning process.
SetMockTime(test_setup->m_node.chainman->GetParams().GenesisBlock().nTime);
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
{
LOCK(wallet.cs_wallet);
Expand Down
5 changes: 2 additions & 3 deletions src/qt/addresstablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,14 @@ struct AddressTableEntryLessThan
};

/* Determine address type from address purpose */
static AddressTableEntry::Type translateTransactionType(wallet::AddressPurpose purpose, bool isMine)
constexpr AddressTableEntry::Type translateTransactionType(wallet::AddressPurpose purpose, bool isMine)
{
// "refund" addresses aren't shown, and change addresses aren't returned by getAddresses at all.
switch (purpose) {
case wallet::AddressPurpose::SEND: return AddressTableEntry::Sending;
case wallet::AddressPurpose::RECEIVE: return AddressTableEntry::Receiving;
case wallet::AddressPurpose::REFUND: return AddressTableEntry::Hidden;
// No default case to allow for compiler to warn
}
} // no default case, so the compiler can warn about missing cases
assert(false);
}

Expand Down
64 changes: 64 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listunspent", 2, "addresses" },
{ "listunspent", 3, "include_unsafe" },
{ "listunspent", 4, "query_options" },
{ "listunspent", 4, "minimumAmount" },
{ "listunspent", 4, "maximumAmount" },
{ "listunspent", 4, "maximumCount" },
{ "listunspent", 4, "minimumSumAmount" },
{ "listunspent", 4, "include_immature_coinbase" },
Comment on lines +124 to +128

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🔴 Blocking: Adapt the portable bitcoin#26485 tests from excluded suites

The advertised full bitcoin#26485 backport still omits two portable upstream test transformations. Upstream merge 34ac3f4 changes wallet_bumpfee.py:308 from listunspent(query_options={'minimumAmount': fee}) to listunspent(minimumAmount=fee), and wallet_taproot.py:381 from sendall(..., options={'psbt': True}) to sendall(..., psbt=True). Both operations are supported by Dash independently of RBF or Taproot. At this head, functional listunspent query-filter calls still use query_options, and no functional sendall call passes the named psbt option, so neither transformation has equivalent coverage. The PR description contains no explicit exclusion for these hunks. Preserve their coverage in existing suites—for example, adapt a minimumAmount call in wallet_basic.py and add an explicit named psbt case in wallet_sendall.py—without importing the unsupported subsystems. This is a full-backport completeness blocker, not evidence of a production runtime failure.

source: gpt-6.1-sol (phase2-reviewer: general, backport-reviewer, dash-core-commit-history)

{ "listunspent", 4, "coinType" },
{ "getblock", 1, "verbosity" },
{ "getblock", 1, "verbose" },
{ "getblockheader", 1, "verbose" },
Expand Down Expand Up @@ -152,10 +158,37 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "submitpackage", 0, "package" },
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 1, "add_inputs"},
{ "fundrawtransaction", 1, "include_unsafe"},
{ "fundrawtransaction", 1, "minconf"},
{ "fundrawtransaction", 1, "maxconf"},
{ "fundrawtransaction", 1, "changePosition"},
{ "fundrawtransaction", 1, "includeWatching"},
{ "fundrawtransaction", 1, "lockUnspents"},
{ "fundrawtransaction", 1, "fee_rate"},
{ "fundrawtransaction", 1, "feeRate"},
{ "fundrawtransaction", 1, "subtractFeeFromOutputs"},
{ "fundrawtransaction", 1, "input_sizes"},
{ "fundrawtransaction", 1, "conf_target"},
{ "fundrawtransaction", 1, "solving_data"},
{ "fundrawtransaction", 1, "use_cj"},
{ "walletcreatefundedpsbt", 0, "inputs" },
{ "walletcreatefundedpsbt", 1, "outputs" },
{ "walletcreatefundedpsbt", 2, "locktime" },
{ "walletcreatefundedpsbt", 3, "options" },
{ "walletcreatefundedpsbt", 3, "add_inputs"},
{ "walletcreatefundedpsbt", 3, "include_unsafe"},
{ "walletcreatefundedpsbt", 3, "minconf"},
{ "walletcreatefundedpsbt", 3, "maxconf"},
{ "walletcreatefundedpsbt", 3, "changePosition"},
{ "walletcreatefundedpsbt", 3, "includeWatching"},
{ "walletcreatefundedpsbt", 3, "lockUnspents"},
{ "walletcreatefundedpsbt", 3, "fee_rate"},
{ "walletcreatefundedpsbt", 3, "feeRate"},
{ "walletcreatefundedpsbt", 3, "subtractFeeFromOutputs"},
{ "walletcreatefundedpsbt", 3, "conf_target"},
{ "walletcreatefundedpsbt", 3, "solving_data"},
{ "walletcreatefundedpsbt", 3, "use_cj"},
{ "walletcreatefundedpsbt", 4, "bip32derivs" },
{ "walletprocesspsbt", 1, "sign" },
{ "walletprocesspsbt", 3, "bip32derivs" },
Expand All @@ -182,19 +215,50 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "send", 1, "conf_target" },
{ "send", 3, "fee_rate"},
{ "send", 4, "options" },
{ "send", 4, "add_inputs"},
{ "send", 4, "include_unsafe"},
{ "send", 4, "minconf"},
{ "send", 4, "maxconf"},
{ "send", 4, "add_to_wallet"},
{ "send", 4, "change_position"},
{ "send", 4, "fee_rate"},
{ "send", 4, "include_watching"},
{ "send", 4, "inputs"},
{ "send", 4, "locktime"},
{ "send", 4, "lock_unspents"},
{ "send", 4, "psbt"},
{ "send", 4, "subtract_fee_from_outputs"},
{ "send", 4, "conf_target"},
{ "send", 4, "solving_data"},
{ "send", 4, "use_cj"},
{ "sendall", 0, "recipients" },
{ "sendall", 1, "conf_target" },
{ "sendall", 3, "fee_rate"},
{ "sendall", 4, "options" },
{ "sendall", 4, "add_to_wallet"},
{ "sendall", 4, "fee_rate"},
{ "sendall", 4, "include_watching"},
{ "sendall", 4, "inputs"},
{ "sendall", 4, "locktime"},
{ "sendall", 4, "lock_unspents"},
{ "sendall", 4, "psbt"},
{ "sendall", 4, "send_max"},
{ "sendall", 4, "minconf"},
{ "sendall", 4, "maxconf"},
{ "sendall", 4, "conf_target"},
{ "sendall", 4, "solving_data"},
{ "sendall", 4, "use_cj"},
{ "simulaterawtransaction", 0, "rawtxs" },
{ "simulaterawtransaction", 1, "options" },
{ "simulaterawtransaction", 1, "include_watchonly"},
{ "importprivkey", 2, "rescan" },
{ "importelectrumwallet", 1, "index" },
{ "importaddress", 2, "rescan" },
{ "importaddress", 3, "p2sh" },
{ "importpubkey", 2, "rescan" },
{ "importmulti", 0, "requests" },
{ "importmulti", 1, "options" },
{ "importmulti", 1, "rescan" },
{ "importdescriptors", 0, "requests" },
{ "listdescriptors", 0, "private" },
{ "verifychain", 0, "checklevel" },
Expand Down
46 changes: 38 additions & 8 deletions src/rpc/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq)
* Process named arguments into a vector of positional arguments, based on the
* passed-in specification for the RPC call's arguments.
*/
static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::string>& argNames)
static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::pair<std::string, bool>>& argNames)
{
JSONRPCRequest out = in;
out.params = UniValue(UniValue::VARR);
Expand All @@ -434,7 +434,9 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
// "args" parameter, if present.
int hole = 0;
int initial_hole_size = 0;
for (const std::string &argNamePattern: argNames) {
const std::string* initial_param = nullptr;
UniValue options{UniValue::VOBJ};
for (const auto& [argNamePattern, named_only]: argNames) {
std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
auto fr = argsIn.end();
for (const std::string & argName : vargNames) {
Expand All @@ -443,30 +445,58 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
break;
}
}
if (fr != argsIn.end()) {

// Handle named-only parameters by pushing them into a temporary options
// object, and then pushing the accumulated options as the next
// positional argument.
if (named_only) {
if (fr != argsIn.end()) {
if (options.exists(fr->first)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " specified multiple times");
}
options.pushKVEnd(fr->first, *fr->second);
argsIn.erase(fr);
}
continue;
}

if (!options.empty() || fr != argsIn.end()) {
for (int i = 0; i < hole; ++i) {
// Fill hole between specified parameters with JSON nulls,
// but not at the end (for backwards compatibility with calls
// that act based on number of specified parameters).
out.params.push_back(UniValue());
}
hole = 0;
out.params.push_back(*fr->second);
argsIn.erase(fr);
if (!initial_param) initial_param = &argNamePattern;
} else {
hole += 1;
if (out.params.empty()) initial_hole_size = hole;
}

// If named input parameter "fr" is present, push it onto out.params. If
// options are present, push them onto out.params. If both are present,
// throw an error.
if (fr != argsIn.end()) {
if (!options.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " conflicts with parameter " + options.getKeys().front());
}
out.params.push_back(*fr->second);
argsIn.erase(fr);
}
if (!options.empty()) {
out.params.push_back(std::move(options));
options = UniValue{UniValue::VOBJ};
}
}
// If leftover "args" param was found, use it as a source of positional
// arguments and add named arguments after. This is a convenience for
// clients that want to pass a combination of named and positional
// arguments as described in doc/JSON-RPC-interface.md#parameter-passing
auto positional_args{argsIn.extract("args")};
if (positional_args && positional_args.mapped()->isArray()) {
const bool has_named_arguments{initial_hole_size < (int)argNames.size()};
if (initial_hole_size < (int)positional_args.mapped()->size() && has_named_arguments) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + argNames[initial_hole_size] + " specified twice both as positional and named argument");
if (initial_hole_size < (int)positional_args.mapped()->size() && initial_param) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + *initial_param + " specified twice both as positional and named argument");
}
// Assign positional_args to out.params and append named_args after.
UniValue named_args{std::move(out.params)};
Expand Down
13 changes: 11 additions & 2 deletions src/rpc/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class CRPCCommand
using Actor = std::function<bool(const JSONRPCRequest& request, UniValue& result, bool last_handler)>;

//! Constructor taking Actor callback supporting multiple handlers.
CRPCCommand(std::string category, std::string name, Actor actor, std::vector<std::string> args, intptr_t unique_id)
CRPCCommand(std::string category, std::string name, Actor actor, std::vector<std::pair<std::string, bool>> args, intptr_t unique_id)
: category(std::move(category)), name(std::move(name)), actor(std::move(actor)), argNames(std::move(args)),
unique_id(unique_id)
{
Expand All @@ -113,7 +113,16 @@ class CRPCCommand
std::string category;
std::string name;
Actor actor;
std::vector<std::string> argNames;
//! List of method arguments and whether they are named-only. Incoming RPC
//! requests contain a "params" field that can either be an array containing
//! unnamed arguments or an object containing named arguments. The
//! "argNames" vector is used in the latter case to transform the params
//! object into an array. Each argument in "argNames" gets mapped to a
//! unique position in the array, based on the order it is listed, unless
//! the argument is a named-only argument with argNames[x].second set to
//! true. Named-only arguments are combined into a JSON object that is
//! appended after other arguments, see transformNamedArguments for details.
std::vector<std::pair<std::string, bool>> argNames;
intptr_t unique_id;
};

Expand Down
Loading
Loading