rpc: extract ListSinceBlockTxFields() helper
What changed, and why it matters
This commit is a code cleanup that extracts a repeated list of RPC help text fields into a reusable helper function. It only affects how the documentation/help output for the listsinceblock command is generated, not how transactions or wallet data are processed. There is no security issue visible in the change.
No security action needed; this is a routine refactoring commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the listsinceblock RPC result schema by introducing ListSinceBlockTxFields() in src/wallet/rpc/transactions.cpp and reusing it for both the ‘transactions’ and ‘removed’ arrays. It also adjusts RPCResult::ToSections() in src/rpc/util.cpp so that an empty std::string print_elision renders as ‘…’ and suppresses an extra array continuation marker when the last inner element is a string elision. The change is purely presentational/help-generation logic.
Changed components
src/wallet/rpc/transactions.cppsrc/rpc/util.cpplistsinceblock RPC help outputInspect captured patch +34 / −26
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 850acee8..e96a19b4 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -1023,9 +1023,7 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
}};
if (const auto* text = std::get_if<std::string>(&m_opts.print_elision)) {
- if (!text->empty()) {
- sections.PushSection({indent + "..." + maybe_separator, *text});
- }
+ sections.PushSection({indent + "..." + maybe_separator, *text});
return;
}
if (std::holds_alternative<HelpElisionSkip>(m_opts.print_elision)) {
@@ -1077,7 +1075,7 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
}
CHECK_NONFATAL(!m_inner.empty());
CHECK_NONFATAL(elision_has_description(m_inner));
- if (m_type == Type::ARR && m_inner.back().m_type != Type::ELISION) {
+ if (m_type == Type::ARR && m_inner.back().m_type != Type::ELISION && !std::holds_alternative<std::string>(m_inner.back().m_opts.print_elision)) {
sections.PushSection({indent_next + "...", ""});
} else {
// Remove final comma, which would be invalid JSON
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index f69082e1..77af932e 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -523,6 +523,33 @@ RPCMethod listtransactions()
};
}
+static std::vector<RPCResult> ListSinceBlockTxFields()
+{
+ return Cat<std::vector<RPCResult>>(
+ {
+ {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the transaction (not returned if the output does not have an address, e.g. OP_RETURN null data)."},
+ {RPCResult::Type::STR, "category", "The transaction category.\n"
+ "\"send\" Transactions sent.\n"
+ "\"receive\" Non-coinbase transactions received.\n"
+ "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ "\"orphan\" Orphaned coinbase transactions received."},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
+ "for all other categories"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ "'send' category of transactions."},
+ },
+ Cat(
+ TransactionDescriptionString(),
+ std::vector<RPCResult>{
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable)."},
+ {RPCResult::Type::STR, "label", /*optional=*/true, "A comment for the address/transaction, if any"},
+ }
+ )
+ );
+}
+
RPCMethod listsinceblock()
{
return RPCMethod{
@@ -544,30 +571,13 @@ RPCMethod listsinceblock()
{
{RPCResult::Type::ARR, "transactions", "",
{
- {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
- {
- {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the transaction (not returned if the output does not have an address, e.g. OP_RETURN null data)."},
- {RPCResult::Type::STR, "category", "The transaction category.\n"
- "\"send\" Transactions sent.\n"
- "\"receive\" Non-coinbase transactions received.\n"
- "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
- "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
- "\"orphan\" Orphaned coinbase transactions received."},
- {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
- "for all other categories"},
- {RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
- "'send' category of transactions."},
- },
- TransactionDescriptionString()),
- {
- {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable)."},
- {RPCResult::Type::STR, "label", /*optional=*/true, "A comment for the address/transaction, if any"},
- })},
+ {RPCResult::Type::OBJ, "", "", ListSinceBlockTxFields()},
}},
{RPCResult::Type::ARR, "removed", /*optional=*/true, "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
- "Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count."
- , {{RPCResult::Type::ELISION, "", ""},}},
+ "Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count.",
+ {
+ {RPCResult::Type::OBJ, "", "", ListSinceBlockTxFields(), {.print_elision = std::string{}}},
+ }},
{RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain, or the genesis hash if the referenced block does not exist yet. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
}
},
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.