rpc: replace ELISION references with explicit result fields #34764

pull satsfy wants to merge 7 commits into bitcoin:master from satsfy:fix-rpc-inheritance changing 9 files +396 −218
  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.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/34764.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK nervana21, Bortlesboat
    Stale ACK w0xlt, rustaceanrob, willcl-ark

    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:

    • #34872 (wallet: fix mixed-input transaction accounting in history RPCs by w0xlt)
    • #34683 (rpc: support a formal description of our JSON-RPC interface by willcl-ark)
    • #33741 (rpc: Optionally print feerates in sat/vb by polespinasa)
    • #32800 (rpc: Distinguish between vsize and sigop adjusted mempool vsize by musaHaruna)
    • #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. satsfy force-pushed on Apr 10, 2026
  48. DrahtBot removed the label Needs rebase on Apr 10, 2026
  49. in src/rpc/rawtransaction_util.cpp:422 in f751ebdfcb outdated
     418 | @@ -388,8 +419,29 @@ std::vector<RPCResult> TxDoc(const TxDocOptions& opts)
     419 |          }},
     420 |      };
     421 |  
     422 | -    if (opts.elision_description) {
     423 | -        fields = ElideGroup(std::move(fields), *opts.elision_description);
     424 | +    if (opts.elision_description || opts.elision_description_silent) {
    


    willcl-ark commented at 10:54 AM on April 29, 2026:

    I wonder if here we should have something like

    CHECK_NONFATAL(!(opts.elision_description && opts.elision_description_silent))
    

    As currently if both description and description_silet are set, then silent "wins" out. This means that silent=true causes the string in elision_description to be (silently) ignored.

    A caller could write:

      TxDoc({
          .elision_description = "Same output as verbosity = 1",
          .elision_description_silent = true,
      })
    

    and reasonably expect the summary to appear. Instead, it disappears with no warning, and no obvious failure unless someone manually inspects bitcoin-cli help.


    satsfy commented at 12:34 AM on May 1, 2026:

    Thanks for the review, great point! The latest rebase will include that check. I have found 4 other cases where such a check may be useful, to avoid caller confusion.

    Btw, I agree that removing ELISION is a good follow-up. It forces future bitcoin-cli changes to be explicit about the interface invariants. I left this out of the current PR to keep the change focused and easier to review.

  50. willcl-ark commented at 10:55 AM on April 29, 2026: member

    Looks decent to me @ ed0e9a6496da8a12ceb6533c241e91e039b65906 now.

    Left a comment about adding an assert to try and acoid descriptions silently going missing.

    If I'm not mistaken, after this PR RPCResult::Type::ELISION might be dead code? Which a followup could remove.

  51. satsfy force-pushed on May 1, 2026
  52. in src/rpc/util.cpp:1018 in eef9ef697d outdated
    1014 | @@ -1015,22 +1015,22 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
    1015 |                 (this->m_description.empty() ? "" : " " + this->m_description);
    1016 |      };
    1017 |  
    1018 | -    // Ensure at least one elision description exists, if there is any elision
    1019 | +    // Ensure at least one visible field exists when elision is used
    


    maflcko commented at 9:39 AM on May 13, 2026:

    eef9ef697d79a5ecdcee08b7c680137de659667f: Why are you changing the behavior? Please follow the dev guideline to split commits into behavior-changing ones and refactor-only commits.


    satsfy commented at 3:04 AM on May 24, 2026:

    Have moved the behaviour change into the following commit, which triggers it: 8ad2d2d.

  53. in src/rpc/util.h:305 in eef9ef697d
     309 | -    /// - If nullopt: normal display.
     310 | -    /// - If empty string: suppress from help.
     311 | -    /// - If non-empty: show "..." with this description.
     312 | -    std::optional<std::string> print_elision{std::nullopt};
     313 | +    HelpElision help_elision{HelpElision::NONE};
     314 | +    std::string help_elision_text{};  //!< only meaningful when help_elision == START
    


    maflcko commented at 9:48 AM on May 13, 2026:

    eef9ef697d79a5ecdcee08b7c680137de659667f: Is this change meaningful? Instead of a tri-state, this is now a tri-enum and a second string field, which can be empty or not.

    I'd say this should either stay as-is, or maybe a variant can be used:

    struct HelpElisionNone{};
    struct HelpElisionSkip{};
    ...
    std::variant<HelpElisionNone, HelpElisionSkip, std::string> print_elision;
    

    However, I am haven't tried the full diff.


    satsfy commented at 3:22 AM on May 24, 2026:

    I had done the enums for readability over nullopt/"". The solution suggested has been applied.

  54. in src/rpc/util.h:389 in eef9ef697d
     385 | @@ -385,6 +386,18 @@ struct RPCResult {
     386 |          RPCResultOptions opts = {})
     387 |          : RPCResult{type, std::move(m_key_name), /*optional=*/false, std::move(description), std::move(inner), std::move(opts)} {}
     388 |  
     389 | +    /// Copy with replacement options because const members prevent assignment
    


    maflcko commented at 9:50 AM on May 13, 2026:

    eef9ef697d79a5ecdcee08b7c680137de659667f: I don't recall why I made the fields const, so maybe it can be made mutable as an easier diff


    satsfy commented at 3:06 AM on May 24, 2026:

    Thanks, it makes things easier for sure. Done.

  55. maflcko commented at 9:51 AM on May 13, 2026: member

    left some comments in the first commit

  56. satsfy force-pushed on May 24, 2026
  57. satsfy commented at 3:28 AM on May 24, 2026: contributor

    Force-pushed:

    • HelpElision is now a std::variant<HelpElisionNone, HelpElisionSkip, std::string>.
    • const dropped from RPCResult members.
    • Refactor commit (c5722ea) is now behavior-preserving. The elision_has_description() relaxation moved to 8ad2d2d, which is the first commit that needs it.
  58. DrahtBot added the label CI failed on May 24, 2026
  59. satsfy force-pushed on May 25, 2026
  60. DrahtBot removed the label CI failed on May 25, 2026
  61. in src/wallet/rpc/addresses.cpp:394 in 58d9a919b6 outdated
     389 | +            "The number of signatures required to spend multisig output (only if script is multisig)."},
     390 | +        {RPCResult::Type::STR_HEX, "pubkey", /*optional=*/true,
     391 | +            "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
     392 | +    };
     393 | +
     394 | +    if (include_nested) {
    


    w0xlt commented at 10:37 PM on May 27, 2026:

    The getaddressinfo() implementation can recurse through embedded scripts via DescribeWalletAddressVisitor::ProcessSubScript(). The new RPCResult docs expand two embedded levels. If a provider ever returns a deeper shape such as embedded.embedded.embedded, -rpcdoccheck would reject it as an undocumented key.

    That said, current descriptor parsing appears to prevent constructing more than two embedded levels through normal importdescriptors paths, so the two-level documentation is probably fine for currently wallets.

    To preserve the previous ELISION behavior, one option would be to add an optional embedded field with RPCResultOptions{.skip_type_check = true} at the recursive boundary, though this does not seem to be strictly necessary.

    <details> <summary>diff</summary>

    diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp
    index 8feae47863..372cf0f0cd 100644
    --- a/src/wallet/rpc/addresses.cpp
    +++ b/src/wallet/rpc/addresses.cpp
    @@ -391,15 +391,15 @@ static std::vector<RPCResult> GetAddressInfoEmbeddedFields(bool include_nested)
                 "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
         };
     
    -    if (include_nested) {
    -        fields.emplace_back(
    -            RPCResult::Type::OBJ,
    -            "embedded",
    -            /*optional=*/true,
    -            "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
    -            GetAddressInfoEmbeddedFields(/*include_nested=*/false)
    -        );
    -    }
    +    // Known nested scripts can recurse beyond the expanded docs below.
    +    fields.emplace_back(
    +        RPCResult::Type::OBJ,
    +        "embedded",
    +        /*optional=*/true,
    +        "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
    +        include_nested ? GetAddressInfoEmbeddedFields(/*include_nested=*/false) : std::vector<RPCResult>{},
    +        include_nested ? RPCResultOptions{} : RPCResultOptions{.skip_type_check = true}
    +    );
    

    </details>

    <details> <summary>unit test</summary>

    diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
    index 1b6dbda0d9..9fed12dd5a 100644
    --- a/src/test/rpc_tests.cpp
    +++ b/src/test/rpc_tests.cpp
    @@ -600,6 +600,29 @@ static void CheckRpc(const std::vector<RPCArg>& params, const UniValue& args, RP
         rpc.HandleRequest(req);
     }
     
    +BOOST_AUTO_TEST_CASE(rpc_result_recursive_boundary_skip_type_check)
    +{
    +    const RPCResult result_doc{
    +        RPCResult::Type::OBJ,
    +        "",
    +        "",
    +        {
    +            {RPCResult::Type::OBJ, "embedded", /*optional=*/true, "",
    +            {
    +                {RPCResult::Type::STR, "address", /*optional=*/true, ""},
    +                {RPCResult::Type::OBJ, "embedded", /*optional=*/true, "", {}, {.skip_type_check = true}},
    +            }},
    +        },
    +    };
    +
    +    BOOST_CHECK(result_doc.MatchesType(JSON(
    +        R"({"embedded":{"embedded":{"embedded":{"address":"bcrt1qexample"}}}})"
    +    )).isTrue());
    +    BOOST_CHECK(!result_doc.MatchesType(JSON(
    +        R"({"embedded":{"unexpected":1}})"
    +    )).isTrue());
    +}
    +
     BOOST_AUTO_TEST_CASE(rpc_arg_helper)
     {
         constexpr bool DEFAULT_BOOL = true;
    

    </details>


    satsfy commented at 12:04 AM on May 28, 2026:

    Thanks for the review!

    IMO, skip_type_check is an ambiguous escape hatch this PR is trying to remove. It produces no machine-readable shape for a comprehensive OpenRPC description. If 3+ levels are needed, I think it should be documented explicitly rather than skipping the check.

  62. w0xlt commented at 10:37 PM on May 27, 2026: contributor

    ACK 58d9a919b63975df175b6622349d8789831e5fc4

  63. DrahtBot requested review from Bortlesboat on May 27, 2026
  64. sedited removed review request from Bortlesboat on Jun 2, 2026
  65. sedited requested review from stickies-v on Jun 2, 2026
  66. in src/rpc/util.h:330 in 58d9a919b6
     332 | +    std::string m_key_name;         //!< Only used for dicts
     333 | +    std::vector<RPCResult> m_inner; //!< Only used for arrays or dicts
     334 | +    bool m_optional;
     335 | +    RPCResultOptions m_opts;
     336 | +    std::string m_description;
     337 | +    std::string m_cond;
    


    sedited commented at 10:46 AM on June 3, 2026:

    This seems to compile with const for me. What is this change for?


    satsfy commented at 4:40 PM on June 4, 2026:

    Good catch. This guarded against a CI failure at some point, but it's unnecessary now. Removed.

  67. in src/rpc/rawtransaction_util.h:80 in 58d9a919b6
      75 | +    std::optional<std::string> prevout_doc{};
      76 | +    /// Customize the fee field's description (only meaningful when fee is true)
      77 | +    std::optional<std::string> fee_doc{};
      78 | +    /// When true, silently elide all top-level fields with no summary text.
      79 | +    /// Mutually exclusive with elision_description.
      80 | +    bool elision_description_silent{false};
    


    rustaceanrob commented at 1:27 PM on June 3, 2026:

    As follow up, it feels like we could somehow merge elision_description and elision_description_silent, perhaps using variant to clean up the fields here.


    janb84 commented at 7:56 PM on June 3, 2026:

    or introduce an extra enum and remove the boolean.

    eg:

    enum class ElisionMode {
        None,        ///< no elision, all top-level fields rendered normally
        WithSummary, ///< first field carries elision_summary as "...", rest skipped
        Silent,      ///< all top-level fields skipped silently (no "..." line)
    };
    
    struct TxDocOptions {
        /// The description of the txid field
        std::string txid_field_doc{"The transaction id"};
        /// Include wallet-related fields (e.g. ischange on outputs)
        bool wallet{false};
        /// Controls top-level field elision in the help
        ElisionMode elision_mode{ElisionMode::None};
        /// Summary text shown as "..." required for elision_mode == WithSummary
        std::optional<std::string> elision_summary{};
        /// Include prevout field
        bool prevout{false};
        /// Include fee field
        bool fee{false};
        /// Include hex field
        bool hex{false};
        /// Customize the vin item object's description (only meaningful when vin_inner_elision is set)
        std::optional<std::string> vin_item_doc{};
        /// Customize the prevout field's description (only meaningful when prevout is true)
        std::optional<std::string> prevout_doc{};
        /// Customize the fee field's description (only meaningful when fee is true)
        std::optional<std::string> fee_doc{};
        /// Elide vin inner fields but keep vin array with prevout expanded.
        std::optional<std::string> vin_inner_elision{};
    };
    

    satsfy commented at 4:41 PM on June 4, 2026:

    Thanks for the suggestion, reads great. Took it, implemented.

  68. DrahtBot requested review from Bortlesboat on Jun 3, 2026
  69. rustaceanrob approved
  70. rustaceanrob commented at 1:29 PM on June 3, 2026: member

    Using variant in print_elision is much easier to reason about than the optional semantics. Seems like a clear win to me.

    ACK 58d9a919b63975df175b6622349d8789831e5fc4

  71. willcl-ark approved
  72. willcl-ark commented at 2:01 PM on June 3, 2026: member

    ACK 58d9a919b63975df175b6622349d8789831e5fc4

    Changing elisions into hidden-but-structured result fields makes sense on its own, and as a building block for #34683 IMO.

  73. in src/rpc/rawtransaction.cpp:263 in 8ad2d2dd3d
     278 | +                                {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "the block hash"},
     279 | +                                {RPCResult::Type::NUM, "confirmations", /*optional=*/true, "The confirmations"},
     280 | +                                {RPCResult::Type::NUM_TIME, "blocktime", /*optional=*/true, "The block time expressed in " + UNIX_EPOCH_TIME},
     281 | +                                {RPCResult::Type::NUM, "time", /*optional=*/true, "Same as \"blocktime\""},
     282 | +                                {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for 'txid'"},
     283 | +                            }, "Same output as verbosity = 1"),
    


    achow101 commented at 6:19 PM on June 3, 2026:

    In 8ad2d2dd3d538122e95ed3a43e74c9dd51180b2e "rpc: extend TxDoc() for getrawtransaction verbosity 2"

    This is a copy of verbosity = 1 help, can this be deduplicated?


    satsfy commented at 4:39 PM on June 4, 2026:

    Deduplicated with a verbosity_1_block variable.

  74. satsfy force-pushed on Jun 4, 2026
  75. satsfy commented at 4:47 PM on June 4, 2026: contributor

    Force-pushed to address review feedback.

    • Replaced the elision_description/elision_description_silent with an ElisionMode enum plus a separate elision_summary.
    • Dropped the change that removed const from the RPCResult members
    • Added a prevout_optional option so verbosity 3 can mark prevout optional.
  76. rpc: introduce HelpElision variant and ElideGroup helper
    Switch RPCResultOptions::print_elision to a variant
    <HelpElisionNone, HelpElisionSkip, std::string> and add a constructor
    that copies a result with replacement options.
    
    Use the new ElideGroup() in TxDoc() to apply elision to field groups
    instead of setting print_elision per field. Type::ELISION stays as a
    deprecated alias.
    44fc3a290d
  77. rpc: extend TxDoc() for getrawtransaction verbosity 2
    Add prevout, elision_mode, and vin_inner_elision options
    to TxDocOptions, and use them to describe getrawtransaction verbosity=2
    with structured metadata instead of raw ELISION entries. Extract
    verbosity=1 block fields into a local verbosity_1_block variable so
    verbosity=2 can reuse them via ElideGroup.
    
    When vin_inner_elision is set, vin fields are elided while keeping
    prevout visible. With elision_mode == ElisionMode::Silent, every other
    top level field is hidden, so the only rendered output comes from the
    vin not elided.
    
    Loosen elision_has_description() to accept any group with at least one
    field that is not SKIP.
    0528e26a18
  78. 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.
    5b5b128780
  79. rpc: extract ListSinceBlockTxFields() helper
    Extract the inline listsinceblock transaction field list into a reusable
    helper. The removed entry replaces its Type::ELISION placeholder with an empty
    string elision.
    
    ToSections() render an empty std::string elision as "...", and suppress
    the array continuation marker when the last inner element renders as a
    std::string elision.
    383085a3ad
  80. 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.
    7731cb418e
  81. 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.
    d3d73260d3
  82. satsfy force-pushed on Jun 4, 2026
  83. satsfy commented at 4:53 PM on June 4, 2026: contributor

    Rebased master to solve a conflict. The conflict is that the decodepsbt field tx is now optional to mirror master.

  84. rpc: expand decodepsbt output script with explicit fields
    Reuse ScriptPubKeyDoc(), which matches what ScriptToUniv() emits here
    wrapped in ElideGroup().
    b0b2df99bc
  85. satsfy commented at 7:41 PM on June 4, 2026: contributor

    The latest push fixes an additional ELISION in a recently merged PR.


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-06-07 13:51 UTC

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