rpc: replace ELISION references with explicit result fields #34764

pull satsfy wants to merge 6 commits into bitcoin:master from satsfy:fix-rpc-inheritance changing 9 files +377 −206
  1. satsfy commented at 8:18 PM on March 6, 2026: contributor

    Partially addresses #29912. Motivated by #34683, which exports OpenRPC from existing RPCHelpMan metadata. Sample OpenRPC.

    Some RPC help definitions rely on RPCResult::Type::ELISION entries whose structure is only described in prose. This keeps human-readable help concise, but leaves parts of the result layout implicit and prevents tools from deriving complete machine-readable schemas from RPCHelpMan metadata.

    This PR replaces ELISION-based reuse with shared structured definitions, so result layouts are represented directly in metadata rather than only in text. At the same time, human-readable help remains compact via explicit help-rendering elision using HelpElision, so previously elided sections stay abbreviated without losing schema completeness.

    Affected RPCs: getrawtransaction, getblock, listsinceblock, estimaterawfee, getaddressinfo.

    RPC return values are unchanged. Human-readable help remains compact, while structured result metadata becomes explicit enough to derive complete machine-readable schemas.

    A related RPCResult::Type::ELISION use in importdescriptors was split out into the follow-up PR #34867 because it changes the generated help output, per this review comment.

    Changes:

    • Introduce HelpElision (NONE, START, SKIP) and ElideGroup(), replacing the tri-state print_elision
    • Add an RPCResult copy-with-replacement-options constructor to support applying elision while keeping m_opts const
    • Extend TxDoc() / TxDocOptions to support reusable transaction layouts with optional prevout, fee, hex, and elision behavior
    • Replace ELISION-based reuse in getrawtransaction and getblock with explicit structured definitions
    • Factor shared result layouts into GetBlockFields(), ListSinceBlockTxFields(), FeeRateBucketDoc(), GetAddressInfoEmbeddedFields() and FeeEstimateHorizonDoc()
    • Expand listsinceblock.removed, estimaterawfee horizons/buckets and getaddressinfo.embedded into explicit metadata while preserving concise help output
  2. DrahtBot added the label RPC/REST/ZMQ on Mar 6, 2026
  3. DrahtBot commented at 8:19 PM on March 6, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK nervana21, Bortlesboat, w0xlt

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #31252 (rpc: print P2WSH and P2SH redem Script in (get/decode)rawtransaction by polespinasa)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. nervana21 commented at 9:19 PM on March 6, 2026: contributor

    Concept ACK

  5. satsfy force-pushed on Mar 7, 2026
  6. Bortlesboat commented at 10:50 PM on March 7, 2026: none

    Code Review ACK

    Thorough review of the diff. This is a well-structured elimination of RPCResult::Type::ELISION in favor of explicit documentation:

    What it does:

    • Renames DecodeTxDocTxDoc with a new TxDocOptions struct using designated initializers instead of positional booleans — much cleaner API
    • Creates GetBlockFields() to deduplicate the getblock verbosity=1/2/3 help text, parameterized by the tx array result
    • Creates ListSinceBlockTxFields() to share field definitions between transactions and removed arrays in listsinceblock
    • Expands all ELISION placeholders into explicit field documentation

    Why this matters: ELISION forced users reading bitcoin-cli help getblock to mentally cross-reference different verbosity levels. Now each level is self-contained, which is especially helpful for verbosity=3 where the prevout fields were previously buried behind two levels of "same as above".

    Code observations:

    • TxDocOptions with default-false members + designated initializers is clean C++20 — callers only name what they enable
    • fee_optional controlling the /*optional=*/ param on RPCResult is a nice touch
    • GetBlockFields taking RPCResult tx_result by value + std::move is correct
    • The #include <rpc/rawtransaction_util.h> addition in blockchain.cpp is the right include for the new TxDoc/TxDocOptions dependency

    One minor note: the old verbosity=2 getblock ELISION had descriptive text "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 'tx' result" — this contextual note is lost, but the explicit fields are self-documenting so arguably better.

  7. DrahtBot added the label Needs rebase on Mar 8, 2026
  8. satsfy force-pushed on Mar 9, 2026
  9. satsfy marked this as ready for review on Mar 9, 2026
  10. DrahtBot removed the label Needs rebase on Mar 9, 2026
  11. willcl-ark commented at 1:53 PM on March 10, 2026: member

    Currently this doesn't compile as a few brackets are mis-matched:

    diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
    index 920080d3b9e..44baf03aad6 100644
    --- a/src/rpc/blockchain.cpp
    +++ b/src/rpc/blockchain.cpp
    @@ -793,9 +793,7 @@ static RPCHelpMan getblock()
                             {
                                 {RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"},
                                 {RPCResult::Type::NUM, "fee", /*optional=*/true, "The transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"},
    -                        }},
    -                    }},
    -                }},
    +                        }})},
                         RPCResult{"for verbosity = 3",
                     RPCResult::Type::OBJ, "", "",
                     {
    @@ -806,7 +804,9 @@ static RPCHelpMan getblock()
                             {
                                 {RPCResult::Type::OBJ, "", "",
                                     TxDoc("The transaction id", TxDocOptions{.prevout = true, .fee = true, .fee_optional = true, .hex = true})},
    -                        }})},
    +                        }},
    +                    }},
    +                }},
                     },
                     RPCExamples{
                         HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
    
    

    I'm not super thrilled about expanding (an already large) help output e.g.

    <details> <summary>Comparison</summary>

    ❯ bitcoin-cli -regtest help getblock
    getblock "blockhash" ( verbosity )
    
    If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.
    If verbosity is 1, returns an Object with information about block <hash>.
    If verbosity is 2, returns an Object with information about block <hash> and information about each transaction.
    If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs (only for unpruned blocks in the current best chain).
    
    Arguments:
    1. blockhash    (string, required) The block hash
    2. verbosity    (numeric, optional, default=1) 0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs
    
    Result (for verbosity = 0):
    "hex"    (string) A string that is serialized, hex-encoded data for block 'hash'
    
    Result (for verbosity = 1):
    {                                 (json object)
      "hash" : "hex",                 (string) the block hash (same as provided)
      "confirmations" : n,            (numeric) The number of confirmations, or -1 if the block is not on the main chain
      "size" : n,                     (numeric) The block size
      "strippedsize" : n,             (numeric) The block size excluding witness data
      "weight" : n,                   (numeric) The block weight as defined in BIP 141
      "coinbase_tx" : {               (json object) Coinbase transaction metadata
        "version" : n,                (numeric) The coinbase transaction version
        "locktime" : n,               (numeric) The coinbase transaction's locktime (nLockTime)
        "sequence" : n,               (numeric) The coinbase input's sequence number (nSequence)
        "coinbase" : "hex",           (string) The coinbase input's script
        "witness" : "hex"             (string, optional) The coinbase input's first (and only) witness stack element, if present
      },
      "height" : n,                   (numeric) The block height or index
      "version" : n,                  (numeric) The block version
      "versionHex" : "hex",           (string) The block version formatted in hexadecimal
      "merkleroot" : "hex",           (string) The merkle root
      "tx" : [                        (json array) The transaction ids
        "hex",                        (string) The transaction id
        ...
      ],
      "time" : xxx,                   (numeric) The block time expressed in UNIX epoch time
      "mediantime" : xxx,             (numeric) The median block time expressed in UNIX epoch time
      "nonce" : n,                    (numeric) The nonce
      "bits" : "hex",                 (string) nBits: compact representation of the block difficulty target
      "target" : "hex",               (string) The difficulty target
      "difficulty" : n,               (numeric) The difficulty
      "chainwork" : "hex",            (string) Expected number of hashes required to produce the chain up to this block (in hex)
      "nTx" : n,                      (numeric) The number of transactions in the block
      "previousblockhash" : "hex",    (string, optional) The hash of the previous block (if available)
      "nextblockhash" : "hex"         (string, optional) The hash of the next block (if available)
    }
    
    Result (for verbosity = 2):
    {                   (json object)
      ...,              Same output as verbosity = 1
      "tx" : [          (json array)
        {               (json object)
          ...,          The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 "tx" result
          "fee" : n     (numeric) The transaction fee in BTC, omitted if block undo data is not available
        },
        ...
      ]
    }
    
    Result (for verbosity = 3):
    {                                        (json object)
      ...,                                   Same output as verbosity = 2
      "tx" : [                               (json array)
        {                                    (json object)
          "vin" : [                          (json array)
            {                                (json object)
              ...,                           The same output as verbosity = 2
              "prevout" : {                  (json object) (Only if undo information is available)
                "generated" : true|false,    (boolean) Coinbase or not
                "height" : n,                (numeric) The height of the prevout
                "value" : n,                 (numeric) The value in BTC
                "scriptPubKey" : {           (json object)
                  "asm" : "str",             (string) Disassembly of the output script
                  "desc" : "str",            (string) Inferred descriptor for the output
                  "hex" : "hex",             (string) The raw output script bytes, hex-encoded
                  "address" : "str",         (string, optional) The Bitcoin address (only if a well-defined address exists)
                  "type" : "str"             (string) The type (one of: nonstandard, anchor, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_scripthash, witness_v0_keyhash, witness_v1_taproot, witness_unknown)
                }
              }
            },
            ...
          ]
        },
        ...
      ]
    }
    
    Examples:
    > bitcoin-cli getblock "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"
    > curl --user myusername --data-binary '{"jsonrpc": "2.0", "id": "curltest", "method": "getblock", "params": ["00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"]}' -H 'content-type: application/json' http://127.0.0.1:8332/
    

    goes to

    ❯ ./build/bin/bitcoin-cli -regtest help getblock
    getblock "blockhash" ( verbosity )
    
    If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.
    If verbosity is 1, returns an Object with information about block <hash>.
    If verbosity is 2, returns an Object with information about block <hash> and information about each transaction.
    If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs (only for unpruned blocks in the current best chain).
    
    Arguments:
    1. blockhash    (string, required) The block hash
    2. verbosity    (numeric, optional, default=1) 0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs
    
    Result (for verbosity = 0):
    "hex"    (string) A string that is serialized, hex-encoded data for block 'hash'
    
    Result (for verbosity = 1):
    {                                 (json object)
      "hash" : "hex",                 (string) the block hash (same as provided)
      "confirmations" : n,            (numeric) The number of confirmations, or -1 if the block is not on the main chain
      "size" : n,                     (numeric) The block size
      "strippedsize" : n,             (numeric) The block size excluding witness data
      "weight" : n,                   (numeric) The block weight as defined in BIP 141
      "coinbase_tx" : {               (json object) Coinbase transaction metadata
        "version" : n,                (numeric) The coinbase transaction version
        "locktime" : n,               (numeric) The coinbase transaction's locktime (nLockTime)
        "sequence" : n,               (numeric) The coinbase input's sequence number (nSequence)
        "coinbase" : "hex",           (string) The coinbase input's script
        "witness" : "hex"             (string, optional) The coinbase input's first (and only) witness stack element, if present
      },
      "height" : n,                   (numeric) The block height or index
      "version" : n,                  (numeric) The block version
      "versionHex" : "hex",           (string) The block version formatted in hexadecimal
      "merkleroot" : "hex",           (string) The merkle root
      "tx" : [                        (json array) The transaction ids
        "hex",                        (string) The transaction id
        ...
      ],
      "time" : xxx,                   (numeric) The block time expressed in UNIX epoch time
      "mediantime" : xxx,             (numeric) The median block time expressed in UNIX epoch time
      "nonce" : n,                    (numeric) The nonce
      "bits" : "hex",                 (string) nBits: compact representation of the block difficulty target
      "target" : "hex",               (string) The difficulty target
      "difficulty" : n,               (numeric) The difficulty
      "chainwork" : "hex",            (string) Expected number of hashes required to produce the chain up to this block (in hex)
      "nTx" : n,                      (numeric) The number of transactions in the block
      "previousblockhash" : "hex",    (string, optional) The hash of the previous block (if available)
      "nextblockhash" : "hex"         (string, optional) The hash of the next block (if available)
    }
    
    Result (for verbosity = 2):
    {                                 (json object)
      "hash" : "hex",                 (string) the block hash (same as provided)
      "confirmations" : n,            (numeric) The number of confirmations, or -1 if the block is not on the main chain
      "size" : n,                     (numeric) The block size
      "strippedsize" : n,             (numeric) The block size excluding witness data
      "weight" : n,                   (numeric) The block weight as defined in BIP 141
      "coinbase_tx" : {               (json object) Coinbase transaction metadata
        "version" : n,                (numeric) The coinbase transaction version
        "locktime" : n,               (numeric) The coinbase transaction's locktime (nLockTime)
        "sequence" : n,               (numeric) The coinbase input's sequence number (nSequence)
        "coinbase" : "hex",           (string) The coinbase input's script
        "witness" : "hex"             (string, optional) The coinbase input's first (and only) witness stack element, if present
      },
      "height" : n,                   (numeric) The block height or index
      "version" : n,                  (numeric) The block version
      "versionHex" : "hex",           (string) The block version formatted in hexadecimal
      "merkleroot" : "hex",           (string) The merkle root
      "tx" : [                        (json array)
        ...,                          The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 "tx" result
        n,                            (numeric, optional) The transaction fee in BTC, omitted if block undo data is not available
        ...
      ],
      "time" : xxx,                   (numeric) The block time expressed in UNIX epoch time
      "mediantime" : xxx,             (numeric) The median block time expressed in UNIX epoch time
      "nonce" : n,                    (numeric) The nonce
      "bits" : "hex",                 (string) nBits: compact representation of the block difficulty target
      "target" : "hex",               (string) The difficulty target
      "difficulty" : n,               (numeric) The difficulty
      "chainwork" : "hex",            (string) Expected number of hashes required to produce the chain up to this block (in hex)
      "nTx" : n,                      (numeric) The number of transactions in the block
      "previousblockhash" : "hex",    (string, optional) The hash of the previous block (if available)
      "nextblockhash" : "hex"         (string, optional) The hash of the next block (if available)
    }
    
    Result (for verbosity = 3):
    {                                          (json object)
      ...,                                     Same output as verbosity = 2
      "tx" : [                                 (json array)
        {                                      (json object)
          "" : {                               (json object)
            "txid" : "hex",                    (string) The transaction id
            "hash" : "hex",                    (string) The transaction hash (differs from txid for witness transactions)
            "size" : n,                        (numeric) The serialized transaction size
            "vsize" : n,                       (numeric) The virtual transaction size (differs from size for witness transactions)
            "weight" : n,                      (numeric) The transaction's weight (between vsize*4-3 and vsize*4)
            "version" : n,                     (numeric) The version
            "locktime" : xxx,                  (numeric) The lock time
            "vin" : [                          (json array)
              {                                (json object)
                "coinbase" : "hex",            (string, optional) The coinbase value (only if coinbase transaction)
                "txid" : "hex",                (string, optional) The transaction id (if not coinbase transaction)
                "vout" : n,                    (numeric, optional) The output number (if not coinbase transaction)
                "scriptSig" : {                (json object, optional) The script (if not coinbase transaction)
                  "asm" : "str",               (string) Disassembly of the signature script
                  "hex" : "hex"                (string) The raw signature script bytes, hex-encoded
                },
                "txinwitness" : [              (json array, optional)
                  "hex",                       (string) hex-encoded witness data (if any)
                  ...
                ],
                "prevout" : {                  (json object, optional) The previous output, omitted if block undo data is not available
                  "generated" : true|false,    (boolean) Coinbase or not
                  "height" : n,                (numeric) The height of the prevout
                  "value" : n,                 (numeric) The value in BTC
                  "scriptPubKey" : {           (json object)
                    "asm" : "str",             (string) Disassembly of the output script
                    "desc" : "str",            (string) Inferred descriptor for the output
                    "hex" : "hex",             (string) The raw output script bytes, hex-encoded
                    "address" : "str",         (string, optional) The Bitcoin address (only if a well-defined address exists)
                    "type" : "str"             (string) The type (one of: nonstandard, anchor, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_scripthash, witness_v0_keyhash, witness_v1_taproot, witness_unknown)
                  }
                },
                "sequence" : n                 (numeric) The script sequence number
              },
              ...
            ],
            "vout" : [                         (json array)
              {                                (json object)
                "value" : n,                   (numeric) The value in BTC
                "n" : n,                       (numeric) index
                "scriptPubKey" : {             (json object)
                  "asm" : "str",               (string) Disassembly of the output script
                  "desc" : "str",              (string) Inferred descriptor for the output
                  "hex" : "hex",               (string) The raw output script bytes, hex-encoded
                  "address" : "str",           (string, optional) The Bitcoin address (only if a well-defined address exists)
                  "type" : "str"               (string) The type (one of: nonstandard, anchor, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_scripthash, witness_v0_keyhash, witness_v1_taproot, witness_unknown)
                }
              },
              ...
            ],
            "fee" : n,                         (numeric, optional) transaction fee in BTC, omitted if block undo data is not available
            "hex" : "hex"                      (string) The hex-encoded transaction data
          }
        },
        ...
      ]
    }
    
    Examples:
    > bitcoin-cli getblock "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"
    > curl --user myusername --data-binary '{"jsonrpc": "2.0", "id": "curltest", "method": "getblock", "params": ["00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"]}' -H 'content-type: application/json' http://127.0.0.1:8332/
    

    </details>

    but, this is certainly one "solution" to ellisions being difficult for a spec parser to read, so it definitely improves on this front. But doing it at the expense of a worse experience for humans reading bitcoin-cli help <cmd>.

    I've picked this PR (with the fixup) into #34683 so we can take a look at it all in one place there.

  12. satsfy force-pushed on Mar 10, 2026
  13. satsfy commented at 3:47 PM on March 10, 2026: contributor

    Thanks for the review, will keep supporting the OpenRPC implementation.

    In the latest push, I fixed bracket balancing and dropped the fee optionality config since it's always optional (see #34702).

    On the help output getting larger for user, I envision a follow-up with the OpenRPC work that:

    • Fully defines all RPCHelpMan results, eliminating ELISIONs
    • Allows configuring what bitcoin-cli help <cmd> displays to avoid excessive verbosity
    • Improves the type system for codegen use (see this comment).
  14. satsfy commented at 4:15 PM on March 10, 2026: contributor

    Note for reviewers: the changes in this PR have been incorporated into #34683. I'll keep this open until it's clear which one will move forward.

  15. w0xlt commented at 11:38 PM on March 10, 2026: contributor

    Concept ACK

  16. maflcko commented at 11:29 AM on March 11, 2026: member

    I'd presume it would be possible to return a human readable help text, but use the full structure internally for MatchesType checks. This is required for #34702 (comment) etc ...

    I've implemented this in #34799.

    If you agree, you may review it and rebase on top of it. I've also re-named the function to TxDoc, so that it is easier for you to rebase.

  17. willcl-ark commented at 11:39 AM on March 11, 2026: member

    Perhaps we could approve CI to run in here please?

  18. satsfy force-pushed on Mar 11, 2026
  19. DrahtBot added the label CI failed on Mar 12, 2026
  20. in src/rpc/rawtransaction_util.h:86 in e9072d1e55 outdated
      82 | + * Optional sections are controlled by @p opts.
      83 | + *
      84 | + * @param[in] opts            Selects which optional fields to include
      85 | + *
      86 | + * @return A vector of RPCResult describing the decoded transaction object
      87 | + */
    


    maflcko commented at 2:46 PM on March 12, 2026:

    nit in e9072d1e55a96912c1a90ad8b385be4f9de79ef8: I think the comment is a bit long. The function should be pretty self-explanatory and a very brief comment should be sufficient:

    /// Explain the UniValue "decoded" transaction object, some fields are adjusted according to the passed [@p](/bitcoin-bitcoin/contributor/p/) opts.
    

    satsfy commented at 3:12 AM on March 18, 2026:

    Sure, done!

  21. in src/rpc/rawtransaction_util.h:63 in e9072d1e55 outdated
      55 | @@ -56,15 +56,34 @@ void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in);
      56 |  /** Create a transaction from univalue parameters */
      57 |  CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf, uint32_t version);
      58 |  
      59 | +/** Options controlling which optional fields TxDoc() includes. All fields
      60 | + * default to false so callers only need to name the ones they enable:
      61 | + *
      62 | + *   TxDoc({.prevout = true, .hex = true})
      63 | + */
    


    maflcko commented at 2:51 PM on March 12, 2026:

    nit in e9072d1e55a96912c1a90ad8b385be4f9de79ef8: Same here. I think this can be removed. Also, "all default to false" may not be accurate, if the type is std::string or std::optional.

    Maybe remove or:

    /// Options controlling some fields in TxDoc(). Callers only need to name the ones they enable:
    ///
    /// TxDoc({.prevout = true, .hex = true})
    

    (Also changed to the doxygen /// comment format, because the /** is a bit ugly, when the text starts inconsistently and is inconsistently indented)


    maflcko commented at 2:53 PM on March 12, 2026:

    Also, in the same commit: It could be easier to review if the commit adding the option also made use of it at the same time. Otherwise, this looks like dead code.

    That is, adding prevout option to TxDoc could be done in the commit f225a25a9eff167cce0c5012bd7b7475f486cd3e, which makes use of it.


    satsfy commented at 3:15 AM on March 18, 2026:

    Comment change done. My current commit order still have some dead code, will address.


    satsfy commented at 3:29 AM on March 19, 2026:

    All bisect issues and review coherence issues solved. Lmk if anything else should change.

  22. maflcko commented at 2:54 PM on March 12, 2026: member

    left some nits

  23. satsfy marked this as a draft on Mar 12, 2026
  24. satsfy commented at 8:28 PM on March 12, 2026: contributor

    In light of @maflcko’s work in #34799, I’m adjusting this PR so the result structure remains explicit in metadata without worsening the human-readable help output.

    I’ve already updated estimaterawfee. After re-checking getaddressinfo.embedded, I think its currently documented shape can also be made explicit here, so I plan to include that as well. I’ll also cover the remaining related case in importdescriptors and clean up the documentation diffs affecting getblock and listsinceblock.

    The goal remains the same: make these result layouts fully specified in code while avoiding unnecessary user-visible help churn.

  25. luke-jr commented at 6:55 AM on March 16, 2026: member

    Seems like it should be practical to continue to elide for human-facing docs, while still keeping track of the structure internally for checking and OpenRPC...

  26. DrahtBot removed the label CI failed on Mar 16, 2026
  27. satsfy force-pushed on Mar 18, 2026
  28. satsfy marked this as ready for review on Mar 18, 2026
  29. in src/wallet/rpc/backup.cpp:348 in 514f8dbf69
     346 | -                                {RPCResult::Type::ELISION, "", "JSONRPC error"},
     347 | -                            }},
     348 | +                                ElideGroup({
     349 | +                                    {RPCResult::Type::NUM, "code", "JSONRPC error code"},
     350 | +                                    {RPCResult::Type::STR, "message", "JSONRPC error message"},
     351 | +                                }, "JSONRPC error")},
    


    maflcko commented at 6:37 AM on March 18, 2026:

    nit in 514f8dbf691b115f0223dfbb53489a08510b2b96: Looks like there are two fields and elision brings them down to one. Not sure why this is done. It could make sense to confirm that exactly both fields are always returned (even with a different jsonrpc version) and then print both in the help text, as well as the machine readable json.

    Maybe as a separate pull request, since it would be unrelated to the new elision feature?


    satsfy commented at 3:53 AM on March 19, 2026:

    Good catch. I verified this on regtest. The "error" field in importdescriptors is returned as a structured JSON-RPC object with code and message:

    {
      "success": false,
      "error": {
        "code": -5,
        "message": "Missing checksum"
      }
    }
    
    

    I agree that making the help text reflect both fields would be a separate follow-up, since that would be unrelated to the elision change here.


    maflcko commented at 5:14 AM on March 19, 2026:

    What I wanted to say is that there is no need to first change the code, then only to change it again. It would be better to only change the code once.


    maflcko commented at 1:56 PM on March 19, 2026:

    I think it could make sense to split up fc98a984bb60b6382f708b892362b0c952485a76 into its own pull, as it is conceptually different from the other changes here.


    satsfy commented at 4:22 PM on March 19, 2026:

    Thanks for the suggestion, have separated the change in a new PR: #34867.

  30. satsfy force-pushed on Mar 19, 2026
  31. satsfy force-pushed on Mar 19, 2026
  32. willcl-ark commented at 1:51 PM on March 19, 2026: member

    While the CI is not re-run here (sorry, it takes a manual approval for new contributors which is tedious) you could run the clang-tidy ci locally and fixup

    env -i HOME="$HOME" PATH="$PATH" USER="$USER" FILE_ENV="./ci/test/00_setup_env_native_tidy.sh" ./ci/test_run_all.sh
    
  33. DrahtBot added the label CI failed on Mar 19, 2026
  34. satsfy force-pushed on Mar 19, 2026
  35. in src/rpc/util.cpp:1196 in 2da7be19aa
    1192 | @@ -1193,6 +1193,7 @@ UniValue RPCResult::MatchesType(const UniValue& result) const
    1193 |          }
    1194 |  
    1195 |          for (const auto& doc_entry : m_inner) {
    1196 | +            if (doc_entry.m_opts.skip_type_check) continue;
    


    w0xlt commented at 6:20 PM on March 19, 2026:

    Placing this here causes skip_type_check fields to bypass checks for required fields. Moving it below the key lookup ensures that only type validation is skipped.

    --- a/src/rpc/util.cpp
    +++ b/src/rpc/util.cpp
    @@ -1193,7 +1193,6 @@ UniValue RPCResult::MatchesType(const UniValue& result) const
             }
     
             for (const auto& doc_entry : m_inner) {
    -            if (doc_entry.m_opts.skip_type_check) continue;
                 const auto result_it{result_obj.find(doc_entry.m_key_name)};
                 if (result_it == result_obj.end()) {
                     if (!doc_entry.m_optional) {
    @@ -1201,6 +1200,7 @@ UniValue RPCResult::MatchesType(const UniValue& result) const
                     }
                     continue;
                 }
    +            if (doc_entry.m_opts.skip_type_check) continue;
                 UniValue match{doc_entry.MatchesType(result_it->second)};
                 if (!match.isTrue()) errors.pushKV(doc_entry.m_key_name, std::move(match));
             }
    

    The following test mirrors the getwalletinfo RPC's scanning field behavior and can be used to validate this.

    --- a/src/test/rpc_tests.cpp
    +++ b/src/test/rpc_tests.cpp
    @@ -600,6 +600,24 @@ static void CheckRpc(const std::vector<RPCArg>& params, const UniValue& args, RP
         rpc.HandleRequest(req);
     }
     
    +BOOST_AUTO_TEST_CASE(rpc_result_skip_type_check_still_checks_presence)
    +{
    +    const RPCResult result_doc{
    +        RPCResult::Type::OBJ,
    +        "",
    +        "",
    +        {
    +            {RPCResult::Type::BOOL, "scanning", "Either false or a scanning progress object", {}, {.skip_type_check = true}},
    +        },
    +    };
    +
    +    const UniValue missing_result{result_doc.MatchesType(JSON(R"({})"))};
    +    BOOST_CHECK_EQUAL(missing_result.write(), R"({"scanning":"key missing, despite not being optional in doc"})");
    +
    +    BOOST_CHECK(result_doc.MatchesType(JSON(R"({"scanning":false})")).isTrue());
    +    BOOST_CHECK(result_doc.MatchesType(JSON(R"({"scanning":{"duration":1,"progress":0.5}})")).isTrue());
    +}
    +
     BOOST_AUTO_TEST_CASE(rpc_arg_helper)
     {
         constexpr bool DEFAULT_BOOL = true;
    

    satsfy commented at 8:30 PM on March 20, 2026:

    Thanks for pointing this out. While addressing it, I ran into CI failures and re-checked the prevout handling covered by this PR. That made it clear that prevout should remain documented as optional here, so the skip_type_check change is not needed after all. I’ve dropped that modification in the updated version.

  36. DrahtBot removed the label CI failed on Mar 20, 2026
  37. satsfy force-pushed on Mar 20, 2026
  38. satsfy force-pushed on Mar 20, 2026
  39. satsfy commented at 10:20 PM on March 20, 2026: contributor

    For reviewers following earlier revisions: after several force-pushes, the current PR is limited to the help/metadata elision refactor. bitcoin-cli help output is unchanged.

    Current scope:

    • introduce HelpElision / ElideGroup()
    • extend TxDoc() for reusable transaction layouts
    • replace ELISION-based reuse with explicit structured metadata in getrawtransaction, getblock, listsinceblock.removed, estimaterawfee, and getaddressinfo.embedded
    • factor repeated layouts into small helpers

    No longer part of this PR:

    • importdescriptors error-object change, split out into #34867
    • earlier skip_type_check / MatchesType() change, now dropped
  40. DrahtBot added the label CI failed on Mar 24, 2026
  41. DrahtBot removed the label CI failed on Mar 24, 2026
  42. fanquake referenced this in commit f6e6fad0d9 on Mar 30, 2026
  43. DrahtBot added the label Needs rebase on Mar 31, 2026
  44. satsfy force-pushed on Mar 31, 2026
  45. DrahtBot removed the label Needs rebase on Mar 31, 2026
  46. DrahtBot added the label Needs rebase on Apr 7, 2026
  47. rpc: introduce HelpElision enum and ElideGroup helper
    Replace the tri-state std::optional<std::string> print_elision
    option with an explicit HelpElision enum (NONE, START, SKIP),
    and add an ElideGroup() helper for stamping elision onto a vector
    of RPCResult fields.
    
    Keep Type::ELISION as a deprecated alias.
    eef9ef697d
  48. rpc: extend TxDoc() for getrawtransaction verbosity 2
    Add prevout, elision_description_silent, and
    vin_inner_elision options to TxDocOptions, and use them to
    describe getrawtransaction verbosity=2 with structured metadata
    instead of raw ELISION entries.
    
    When vin_inner_elision is set, vin fields are elided while
    keeping prevout visible.
    f751ebdfcb
  49. rpc: extend TxDoc() for getblock verbosity 2/3
    Add fee, hex, fee_doc, prevout_doc, and vin_item_doc
    options to TxDocOptions, and use them to describe
    getblock verbosity=2/3 with structured metadata instead
    of inline ELISION-based reuse.
    
    Add GetBlockFields() to share the block-level result layout
    across verbosity levels.
    d19f8855cf
  50. rpc: extract ListSinceBlockTxFields() helper
    Extract the inline listsinceblock transaction field list into
    a reusable helper.
    
    Use the shared definition for both the transactions and removed
    arrays, replacing the ELISION-based removed entry with explicit
    metadata.
    d2b7be7101
  51. rpc: extract fee estimate result helpers
    Replace inline fee estimation field lists in estimaterawfee with
    reusable FeeRateBucketDoc() and FeeEstimateHorizonDoc() helpers.
    
    Use ElideGroup() to keep repeated horizon and bucket sections
    compact in human-readable help while preserving explicit result
    metadata.
    2fbee7b5ea
  52. rpc: expand getaddressinfo embedded with explicit fields
    Replace the ELISION entry inside getaddressinfo's embedded object
    with explicit fields using ElideGroup().
    
    Factor the embedded field list into a helper so the nested layout
    can be reused without duplicating the full structure inline.
    ed0e9a6496
  53. satsfy force-pushed on Apr 10, 2026
  54. DrahtBot removed the label Needs rebase on Apr 10, 2026

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-21 18:12 UTC

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