I noticed this behaviour while perf testing PR #27286 and it was not something that I expected, updating the doc to make it present in the RPCHelp command.
rpc, doc: clarify the response of listtransactions RPC #32737
pull rkrux wants to merge 1 commits into bitcoin:master from rkrux:listtx changing 1 files +5 −1-
rkrux commented at 2:53 PM on June 12, 2025: contributor
-
DrahtBot commented at 2:53 PM on June 12, 2025: contributor
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--006a51241073e994b41acfe9ec718e94-->
Code Coverage & Benchmarks
For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32737.
<!--021abf342d371248e50ceaed478a90ca-->
Reviews
See the guideline for information on the review process.
Type Reviewers ACK furszy, musaHaruna, achow101 Stale ACK w0xlt If your review is incorrectly listed, please copy-paste <code><!--meta-tag:bot-skip--></code> into the comment that the bot should ignore.
<!--5faf32d7da4f0f540f40219e4f7537a3-->
- rkrux force-pushed on Jun 14, 2025
-
w0xlt commented at 11:21 PM on June 17, 2025: contributor
-
in src/wallet/rpc/transactions.cpp:448 in 8140008759 outdated
443 | @@ -444,7 +444,10 @@ RPCHelpMan listtransactions() 444 | return RPCHelpMan{ 445 | "listtransactions", 446 | "If a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n" 447 | - "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n", 448 | + "Returns up to 'count' most recent transactions ordered from oldest to newest while skipping the first 'from' transactions.\n" 449 | + "Every item in the response array is per output level, i.e., a transaction can have multiple items in the array.\n"
luke-jr commented at 7:31 PM on June 25, 2025:"output" is kind of a low-level detail, while this is a high-level RPC. It's better to think of entries as logical/financial transactions, as opposed to blockchain transactions.
rkrux commented at 11:31 AM on June 27, 2025:Good point, I have reworded to remove the word "output".
Seeing the term "transactions" in the RPC name immediately led me to believe it's referring to the blockchain transactions. Hence, I do want to let an example be present because this behaviour was not apparent to me when I first started using this RPC
rkrux commented at 11:33 AM on June 27, 2025:I have not used the term "logical/financial" to describe these transactions yet but I can add it if people believe that would be a valuable addition.
rkrux force-pushed on Jun 27, 2025rkrux force-pushed on Jun 27, 2025DrahtBot added the label CI failed on Jun 27, 2025DrahtBot removed the label CI failed on Jul 1, 2025achow101 requested review from achow101 on Oct 22, 2025achow101 requested review from murchandamus on Oct 22, 2025achow101 commented at 9:58 PM on November 17, 2025: memberACK c27629702285029add897b89247ab9493aede22d
DrahtBot requested review from w0xlt on Nov 17, 2025musaHaruna commented at 1:03 PM on December 15, 2025: contributorACK c27629 I used the example in the docs to verify the behaviour. The doc now clearly explains the RPC! Left a nit suggestion below. I used the test script below to verify behaviour.
#!/usr/bin/env python3 """Minimal functional test for `listtransactions` behavior. This test verifies two documented behaviours: 1) A single blockchain transaction that sends wallet funds to 1 wallet address and 2 non-wallet addresses produces multiple wallet entries: 1 "receive" entry and 3 "send" entries (total 4 entries). 2) Calling `listtransactions` with a label returns only incoming transactions paying to addresses with that label. """ from test_framework.test_framework import BitcoinTestFramework from test_framework.blocktools import COINBASE_MATURITY from test_framework.util import assert_equal class ListTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True self.extra_args = [[], []] def skip_test_if_missing_module(self): self.skip_if_no_wallet() def setup_network(self): self.setup_nodes() self.connect_nodes(0, 1) self.sync_all() def run_test(self): # Mine mature coinbase outputs for node0 so it can spend self.generate(self.nodes[0], COINBASE_MATURITY + 1, sync_fun=lambda: self.sync_all(self.nodes)) self.sync_all() # Prepare addresses: one address in node0's wallet (will be labelled), and # two addresses that are not in node0's wallet (use node1 addresses). addr_wallet = self.nodes[0].getnewaddress() self.nodes[0].setlabel(addr_wallet, "receive_address") external_address1 = self.nodes[1].getnewaddress() external_address2 = self.nodes[1].getnewaddress() # Send a single transaction from node0 to three outputs: one to its own # address (labelled) and two to external addresses (node1). This should # create one blockchain transaction with 3 outputs. txid = self.nodes[0].sendmany('', {addr_wallet: 1, external_address1: 1, external_address2: 1}) # Confirm the transaction self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_all(self.nodes)) self.sync_all() # Collect listtransactions entries for this txid from node0's wallet tx_entries = [txs for txs in self.nodes[0].listtransactions('*', 100) if txs.get('txid') == txid] # According to documentation: one 'receive' entry and three 'send' entries assert_equal(len(tx_entries), 4) receive_count = sum(1 for txs in tx_entries if txs['category'] == 'receive') send_count = sum(1 for txs in tx_entries if txs['category'] == 'send') assert_equal(receive_count, 1) assert_equal(send_count, 3) # Now verify label filtering: listing with the label should return only # incoming transactions paying to addresses with that label (i.e. the # single 'receive' entry for this tx). label_entries = self.nodes[0].listtransactions(label="receive_address", count=100) # Filter to entries related to our tx label_tx_entries = [txs for txs in label_entries if txs.get('txid') == txid] assert_equal(len(label_tx_entries), 1) assert_equal(label_tx_entries[0]['category'], 'receive') assert_equal(label_tx_entries[0]['address'], addr_wallet) if __name__ == '__main__': ListTransactionsTest(__file__).main()in src/wallet/rpc/transactions.cpp:448 in c276297022
443 | @@ -444,7 +444,10 @@ RPCHelpMan listtransactions() 444 | return RPCHelpMan{ 445 | "listtransactions", 446 | "If a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n" 447 | - "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n", 448 | + "Returns up to 'count' most recent transactions ordered from oldest to newest while skipping the first 'from' transactions.\n" 449 | + "A blockchain transaction can have multiple entries in the response. Eg: A blockchain transaction sending wallet funds to\n"
musaHaruna commented at 1:05 PM on December 15, 2025:nit: If you retouch
"A blockchain transaction can have multiple entries in the response. For example a blockchain transaction sending wallet funds to\n"I think looks better than the initial abbrevation
in src/wallet/rpc/transactions.cpp:447 in c276297022
443 | @@ -444,7 +444,10 @@ RPCHelpMan listtransactions() 444 | return RPCHelpMan{ 445 | "listtransactions", 446 | "If a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n" 447 | - "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n", 448 | + "Returns up to 'count' most recent transactions ordered from oldest to newest while skipping the first 'from' transactions.\n"
furszy commented at 1:53 AM on December 16, 2025:q: how the user would understand what 'from' is on this message? As far as I can see, the message is referring to the "skip" arg instead.
rkrux commented at 2:57 PM on December 16, 2025:the message is referring to the "skip" arg instead.
I guess yeah, maybe there was a "from" argument earlier here? I somehow automatically ignored this as I was focussed on adding the example below, but I will push a commit rewording this sentence.
furszy commented at 3:27 PM on December 16, 2025:the message is referring to the "skip" arg instead.
I guess yeah, maybe there was a "from" argument earlier here? I somehow automatically ignored this as I was focussed on adding the example below, but I will push a commit rewording this sentence.
Sounds good đđź. There was probably something there before. But now it is very odd and not really something users will understand as is.
rkrux commented at 12:49 PM on December 17, 2025:Updated, good catch!
rkrux force-pushed on Dec 17, 2025in src/wallet/rpc/transactions.cpp:451 in e6cb65b3ba
443 | @@ -444,7 +444,11 @@ RPCHelpMan listtransactions() 444 | return RPCHelpMan{ 445 | "listtransactions", 446 | "If a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n" 447 | - "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n", 448 | + "Returns up to 'count' most recent transactions ordered from oldest to newest while skipping the first number of \n" 449 | + "transactions specified in the 'skip' argument. A blockchain transaction can have multiple entries in this RPC response. \n" 450 | + "Eg: A blockchain transaction sending wallet funds to 1 wallet address & 2 non-wallet addresses will have 4 entries \n" 451 | + "in the response - 1 in the 'receive' category and 3 in the 'send' category (because there are three outgoing entries \n" 452 | + "even though one of them is sent to a wallet address).",
furszy commented at 3:14 PM on December 17, 2025:Could use "transaction" instead of "blockchain transaction".
Also, I think the example could be improved, something like:
"For instance, a wallet transaction that sends funds to three addresses - one belonging to the wallet itself and two externalâ will produce four entries (one per output, including the change output). As a result, the response of 'listransactions' will contain one entry in the 'receive' category and three entries in the 'send' category. etc.."
(feel free to add stuff to it - but I think something like this reads slightly better).
2knwhzwkm6-max commented at 1:21 AM on December 18, 2025:Could use "transaction" instead of "blockchain transaction".
I agree with youďź
Also, I think the example could be improved,
I guess this sentence are great, but Iâd avoid âone per output / including the change outputâ here, since it can be misleading. listtransactions returns wallet entries, not necessarily one item per on-chain output.
In this example, the key point is that a payment to a wallet-owned address can show up as both a send entry (the wallet created that payment) and a receive entry (the wallet owns the destination).
Maybe something like: âFor instance, a wallet transaction that pays three addressesâone wallet-owned and two externalâwill produce four entries. The payment to the wallet-owned address can appear both as a send entry and as a receive entry . As a result, the response of listtransactions will contain one entry in the receive category and three entries in the send category. â
rkrux commented at 1:11 PM on December 18, 2025:Thanks for the suggestions, updated PR to accommodate both of them.
1ed8e76165rpc, doc: clarify the response of listtransactions RPC
I noticed this behaviour while perf testing PR 27286 and it was not something that I expected, updating the doc to make it present in the RPCHelp command.
rkrux force-pushed on Dec 18, 2025furszy commented at 2:37 PM on December 18, 2025: memberACK 1ed8e7616527c69dbaa9904cda59e3b73c29fa5d
musaHaruna commented at 8:37 AM on December 22, 2025: contributorACK 1ed8e76 since my last review. New changes looks good, it's much easier to understand as well, looking at it from a user's perspective.
achow101 commented at 11:08 PM on December 22, 2025: memberACK 1ed8e7616527c69dbaa9904cda59e3b73c29fa5d
achow101 merged this on Dec 22, 2025achow101 closed this on Dec 22, 2025
github-metadata-mirror
This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-17 03:12 UTC
This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me