Problem: Transaction-creation RPCs currently take quadratic time to parse outputs.
An authenticated RPC client can therefore tie up a worker with a large request.
sendmany also holds the wallet lock while parsing, delaying other operations on the same wallet.
Fix: Parse transaction outputs in linear time by reading corresponding keys and values by index instead of looking up each value by key.
Reproducer: Run time build/bin/test_bitcoin --run_test=rpc_tests/parse_outputs before and after the fix:
<details> <summary>parse_outputs test in `rpc_tests.cpp`</summary>
BOOST_AUTO_TEST_CASE(parse_outputs)
{
constexpr size_t OUTPUT_COUNT{10'000};
UniValue outputs{UniValue::VOBJ};
for (size_t i{0}; i < OUTPUT_COUNT; ++i) {
auto destination{EncodeDestination(WitnessV0ScriptHash{CScript{} << i})};
outputs.pushKVEnd(destination, ValueFromAmount(i + 1));
}
const auto parsed_outputs{ParseOutputs(outputs)};
BOOST_REQUIRE_EQUAL(parsed_outputs.size(), OUTPUT_COUNT);
for (size_t i{OUTPUT_COUNT}; i > 0; --i) {
std::pair expected{CTxDestination{WitnessV0ScriptHash{CScript{} << (i - 1)}}, static_cast<CAmount>(i)};
BOOST_CHECK(parsed_outputs[i - 1] == expected);
}
}
</details> E.g. on my M4 Max with `debug` build:
Before ████████████████████ 1.80 s
After █████▒░░░░░░░░░░░░░░ 0.50 s -72%
Related to #35889