rpc: add “ischange: true” to decoded tx outputs in wallet gettransaction response #32517

pull pinheadmz wants to merge 3 commits into bitcoin:master from pinheadmz:wallet-gettransaction-ischange changing 7 files +78 −50
  1. pinheadmz commented at 3:40 pm on May 15, 2025: member

    This change is motivated by external RBF clients like https://github.com/CardCoins/additive-rbf-batcher/. It saves the user a redundant re-looping of tx outputs, calling getaddressinfo on each one, looking for the change output in order to adjust the fee.

    The field "ischange" only appears when gettransaction is called on a wallet, and is either true or not present at all. I chose not to include ischange: false because it is confusing to see that on received transactions.

    Example of the new field:

     0    "vout": [
     1      {
     2        "value": 1.00000000,
     3        "n": 0,
     4        "scriptPubKey": {
     5          "asm": "0 5483235e05c76273b3b50af62519738781aff021",
     6          "desc": "addr(bcrt1q2jpjxhs9ca388va4ptmz2xtns7q6lupppkw7wu)#d42g84j6",
     7          "hex": "00145483235e05c76273b3b50af62519738781aff021",
     8          "address": "bcrt1q2jpjxhs9ca388va4ptmz2xtns7q6lupppkw7wu",
     9          "type": "witness_v0_keyhash"
    10        }
    11      },
    12      {
    13        "value": 198.99859000,
    14        "n": 1,
    15        "scriptPubKey": {
    16          "asm": "0 870ab1ab58632b05a417d5295f4038500e407592",
    17          "desc": "addr(bcrt1qsu9tr26cvv4stfqh65547spc2q8yqavj7fnlju)#tgapemkv",
    18          "hex": "0014870ab1ab58632b05a417d5295f4038500e407592",
    19          "address": "bcrt1qsu9tr26cvv4stfqh65547spc2q8yqavj7fnlju",
    20          "type": "witness_v0_keyhash"
    21        },
    22        "ischange": true
    23      }
    24    ]
    
  2. in src/rpc/rawtransaction.cpp:121 in bef143cc28 outdated
    117@@ -118,6 +118,7 @@ static std::vector<RPCResult> DecodeTxDoc(const std::string& txid_field_doc)
    118                 {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},
    119                 {RPCResult::Type::NUM, "n", "index"},
    120                 {RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()},
    121+                {RPCResult::Type::BOOL, "ischange", /*optional=*/true, "Output script is change (only if wallet transaction and true for selected rpcwallet)"},
    


    maflcko commented at 4:12 pm on May 15, 2025:

    nit: Not sure about making this optional out of convenience and then write the rule in the description string.

    It would be better to just pass down a std::optionl<RPCResult> (or std::vector<RPCResult>) here, and then use Cat() to concatenate it?


    pinheadmz commented at 4:39 pm on May 15, 2025:
    Not sure what you mean here – help gettransaction doesn’t list this at all, it just tells the user to check decoderawtransaction

    maflcko commented at 5:04 pm on May 15, 2025:
    Hmm, looks like you are right. Though, that doesn’t seem very machine readable. It is a bit unrelated, but I can try to fix it, if you don’t want to include it here.

    pinheadmz commented at 5:43 pm on May 15, 2025:
    Ill give it a shot in this branch

    pinheadmz commented at 2:16 pm on May 16, 2025:
    done

    maflcko commented at 11:36 am on November 3, 2025:
    Nice. Thanks for taking the suggestion.
  3. in src/core_write.cpp:248 in bef143cc28 outdated
    242@@ -243,6 +243,11 @@ void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry
    243         UniValue o(UniValue::VOBJ);
    244         ScriptToUniv(txout.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
    245         out.pushKV("scriptPubKey", std::move(o));
    246+
    247+        if (is_change_func && is_change_func(txout)) {
    248+            out.pushKV("ischange", true);
    


    maflcko commented at 4:13 pm on May 15, 2025:
    0        if (is_change_func  ) {
    1            out.pushKV("ischange", is_change_func(txout));
    

    nit: Seems more user friendly to not make the field optional and instead return a boolean?


    pinheadmz commented at 4:34 pm on May 15, 2025:
    I started with this but then playing with the feature I made it optional because if you receive a payment you’d see a tx with a bunch of outputs that all say ischange: false. I felt like that was confusing because you didn’t create this tx and that might imply that there is some way to detect a change output in someone else’s tx.
  4. achow101 commented at 10:47 pm on May 28, 2025: member
    ACK f6517df210f5e940d87823c86358976743de2641
  5. pinheadmz requested review from maflcko on May 28, 2025
  6. in src/core_io.h:50 in bef143cc28 outdated
    46@@ -46,6 +47,6 @@ std::string FormatScript(const CScript& script);
    47 std::string EncodeHexTx(const CTransaction& tx);
    48 std::string SighashToStr(unsigned char sighash_type);
    49 void ScriptToUniv(const CScript& script, UniValue& out, bool include_hex = true, bool include_address = false, const SigningProvider* provider = nullptr);
    50-void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS);
    51+void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS,  std::function<bool(CTxOut)> is_change_func = {});
    


    furszy commented at 3:25 pm on October 10, 2025:

    Should be a const ref:

    0void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS, std::function<bool(const CTxOut&)> is_change_func = {});
    

    pinheadmz commented at 3:14 pm on October 15, 2025:
    good catch thanks I’ll update this arg with const here and in the lambda

    furszy commented at 3:18 pm on October 15, 2025:
    only const? why not passing the ref too. Otherwise you are copying the object.

    pinheadmz commented at 3:22 pm on October 15, 2025:
    oops thanks, updating…
  7. furszy commented at 3:27 pm on October 10, 2025: member

    utACK f6517df210f5e940d87823c86358976743de2641

    left a small comment that would be nice to fix.

  8. pinheadmz force-pushed on Oct 15, 2025
  9. pinheadmz force-pushed on Oct 15, 2025
  10. in src/wallet/rpc/transactions.cpp:805 in 9059a71e97 outdated
    801+                /*block_hash=*/uint256(),
    802+                /*entry=*/decoded,
    803+                /*include_hex=*/false,
    804+                /*txundo=*/nullptr,
    805+                /*verbosity=*/TxVerbosity::SHOW_DETAILS,
    806+                /*is_change_func=*/[&pwallet](const CTxOut txout) NO_THREAD_SAFETY_ANALYSIS {
    


    furszy commented at 2:26 pm on October 29, 2025:

    nit: Missing ref here const CTxOut& txout

    0                /*is_change_func=*/[&pwallet](const CTxOut& txout) NO_THREAD_SAFETY_ANALYSIS {
    

    Also, why not EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) instead of NO_THREAD_SAFETY_ANALYSIS?


    pinheadmz commented at 4:12 pm on October 29, 2025:
    Thanks, taken
  11. furszy commented at 2:27 pm on October 29, 2025: member
    ACK ab06c3f16063701cfa25504a0d3cd4d5a5eb3a97
  12. rpc: add "ischange: true" in wallet gettransaction decoded tx output d633db5416
  13. [move only] move DecodeTxDoc() to a common util file for sharing ad1c3bdba5
  14. rpc: add decoded tx details to gettransaction with extra wallet fields 060bb55508
  15. pinheadmz force-pushed on Oct 29, 2025
  16. furszy commented at 5:19 pm on October 29, 2025: member
    ACK 060bb55
  17. in src/core_io.h:50 in d633db5416 outdated
    46@@ -46,6 +47,6 @@ std::string FormatScript(const CScript& script);
    47 std::string EncodeHexTx(const CTransaction& tx);
    48 std::string SighashToStr(unsigned char sighash_type);
    49 void ScriptToUniv(const CScript& script, UniValue& out, bool include_hex = true, bool include_address = false, const SigningProvider* provider = nullptr);
    50-void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS);
    51+void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS, std::function<bool(const CTxOut&)> is_change_func = {});
    


    rkrux commented at 10:34 am on November 3, 2025:

    In d633db54166497685b80a12c51db6772982e01fe “rpc: add “ischange: true” in wallet gettransaction decoded tx output”

    Nit: #IWYU

     0diff --git a/src/core_io.h b/src/core_io.h
     1index 1874c93a05..57ae6a2af7 100644
     2--- a/src/core_io.h
     3+++ b/src/core_io.h
     4@@ -11,6 +11,7 @@
     5 #include <string>
     6 #include <vector>
     7 #include <optional>
     8+#include <functional>
     9 
    10 class CBlock;
    11 class CBlockHeader;
    

    rkrux commented at 10:35 am on November 3, 2025:

    In d633db54166497685b80a12c51db6772982e01fe “rpc: add “ischange: true” in wallet gettransaction decoded tx output”

    Nit: s/is_change_func/is_output_change_func

    0@@ -47,6 +48,6 @@ std::string FormatScript(const CScript& script);
    1 std::string EncodeHexTx(const CTransaction& tx);
    2 std::string SighashToStr(unsigned char sighash_type);
    3 void ScriptToUniv(const CScript& script, UniValue& out, bool include_hex = true, bool include_address = false, const SigningProvider* provider = nullptr);
    4-void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS, std::function<bool(const CTxOut&)> is_change_func = {});
    5+void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS, std::function<bool(const CTxOut&)> is_output_change_func = {});
    6 
    7 #endif // BITCOIN_CORE_IO_H
    
  18. in src/rpc/rawtransaction_util.cpp:376 in 060bb55508
    378+                    {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},
    379+                    {RPCResult::Type::NUM, "n", "index"},
    380+                    {RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()},
    381+                },
    382+                    wallet ?
    383+                    std::vector<RPCResult>{{RPCResult::Type::BOOL, "ischange", /*optional=*/true, "Output script is change (only present if true)"}} :
    


    rkrux commented at 10:51 am on November 3, 2025:

    In 060bb55508245776bb6a39c8b7849769ee588d69 “rpc: add decoded tx details to gettransaction with extra wallet fields”

    Nit: Can do away with the word script

    0@@ -373,7 +373,7 @@ std::vector<RPCResult> DecodeTxDoc(const std::string& txid_field_doc, bool walle
    1                     {RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()},
    2                 },
    3                     wallet ?
    4-                    std::vector<RPCResult>{{RPCResult::Type::BOOL, "ischange", /*optional=*/true, "Output script is change (only present if true)"}} :
    5+                    std::vector<RPCResult>{{RPCResult::Type::BOOL, "ischange", /*optional=*/true, "Output is change (only present if true)"}} :
    6                     std::vector<RPCResult>{}
    7                 )
    8             },
    
  19. rkrux approved
  20. rkrux commented at 10:53 am on November 3, 2025: contributor

    lgtm ACK 060bb55508245776bb6a39c8b7849769ee588d69

    Only minor nits if retouched.

  21. maflcko commented at 11:35 am on November 3, 2025: member

    Sorry for missing the review request earlier.

    review ACK 060bb55508245776bb6a39c8b7849769ee588d69 🌛

    Signature:

    0untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    1RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    2trusted comment: review ACK 060bb55508245776bb6a39c8b7849769ee588d69 🌛
    3oFaD8aXwgNJESDCDkPUK36fans9bFYO48Z+kJQ23jaQZBYjSlHReFMS0DTWGUZ53/9KXGQYvRo3vNbWJscPFAA==
    
  22. achow101 commented at 4:50 pm on November 10, 2025: member
    ACK 060bb55508245776bb6a39c8b7849769ee588d69
  23. achow101 merged this on Nov 10, 2025
  24. achow101 closed this on Nov 10, 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: 2025-11-12 18:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me