rpc: Stamp `getblock` OpenRPC result discriminator #36175

pull nervana21 wants to merge 3 commits into bitcoin:master from nervana21:openrpc-discriminated-result changing 4 files +54 −5
  1. nervana21 commented at 2:20 PM on September 5, 2026: contributor

    OpenRPC documents getblock as a four-way oneOf on verbosity, but the dump does not say which integer selects which arm. Therefore, clients that expose a type or method per verbosity (e.g. corepc, btcd) have to hardcode that map.

    This patch adds optional RPCResultDiscriminator metadata, emits it as x-bitcoin-discriminated-result, and stamps getblock. getblock request handling and returned block data are unchanged. Now, help getblock notes that verbosity 2 and 3 cannot be told apart by validating a response alone.

    Also updates getopenrpcinfo schema text to document x-bitcoin-discriminated-result and x-bitcoin-default-hint.

    The change to the schema's format is:

    <details> <summary><code>x-bitcoin-discriminated-result</code> on <code>getblock</code></summary>

    diff dump.json dump_new.json
    5590c5590,5600
    <           ]
    ---
    >           ],
    >           "x-bitcoin-discriminated-result": {
    >             "parameter": "verbosity",
    >             "parameterIndex": 1,
    >             "values": [
    >               0,
    >               1,
    >               2,
    >               3
    >             ]
    >           }
    

    </details>

    <details> <summary><code>getblock</code> description notes verbosity 2 and 3 overlap</summary>

    diff openrpc.json openrpc_new.json
    4696c4696
    <       "description": "If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\nIf verbosity is 1, returns an Object with information about block <hash>.\nIf verbosity is 2, returns an Object with information about block <hash> and information about each transaction.\nIf 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).",
    ---
    >       "description": "If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\nIf verbosity is 1, returns an Object with information about block <hash>.\nIf verbosity is 2, returns an Object with information about block <hash> and information about each transaction.\nIf 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).\nThe verbosity = 2 and verbosity = 3 result schemas cannot be distinguished by validating a response alone because a block with no spent inputs returns byte-identical JSON at both levels (a coinbase input has no prevout to add), and the verbosity = 3 schema lists prevout as optional, so both object arms accept both responses.",
    

    </details>

    <details> <summary>schema text for <code>x-bitcoin-default-hint</code> and <code>x-bitcoin-discriminated-result</code></summary>

    diff openrpc.json openrpc_new.json
    8610c8610
    <                           "description": "JSON Schema for the parameter."
    ---
    >                           "description": "JSON Schema for the parameter. May include \"x-bitcoin-default-hint\" when the default cannot be expressed as a JSON value."
    8649c8649
    <                         "description": "JSON Schema for the result. Numeric schemas may include \"x-bitcoin-unit\" property: \"amount\" which denotes a Bitcoin amount in BTC."
    ---
    >                         "description": "JSON Schema for the result. Numeric schemas may include \"x-bitcoin-unit\" property: \"amount\" which denotes a Bitcoin amount in BTC. Multi-result schemas may include \"x-bitcoin-discriminated-result\" naming the request parameter or parameters that select among the top-level oneOf branches."
    13319c13319
    <                           "description": "JSON Schema for the parameter."
    ---
    >                           "description": "JSON Schema for the parameter. May include \"x-bitcoin-default-hint\" when the default cannot be expressed as a JSON value."
    13358c13358
    <                         "description": "JSON Schema for the result. Numeric schemas may include \"x-bitcoin-unit\" property: \"amount\" which denotes a Bitcoin amount in BTC."
    ---
    >                         "description": "JSON Schema for the result. Numeric schemas may include \"x-bitcoin-unit\" property: \"amount\" which denotes a Bitcoin amount in BTC. Multi-result schemas may include \"x-bitcoin-discriminated-result\" naming the request parameter or parameters that select among the top-level oneOf branches."
    

    </details>

    Other results can also use the same discriminator pattern.

  2. DrahtBot added the label RPC/REST/ZMQ on Sep 5, 2026
  3. DrahtBot commented at 2:20 PM on September 5, 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/36175.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept ACK jeanpablojp

    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.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. sedited requested review from willcl-ark on Sep 5, 2026
  5. nervana21 force-pushed on Sep 6, 2026
  6. jeanpablojp commented at 10:40 PM on September 6, 2026: contributor

    Concept ACK

    One thing I could not settle by reading. The test asserts the discriminator's literals rather than the two relations behind them.

    Inserting an argument before verbosity in getblock leaves parameterIndex 1 addressing the new argument, and swapping the verbosity 2 and 3 arms points value 2 at the arm with prevout. rpc_openrpc.py passes on both builds, and none of the three new CHECK_NONFATALs looks at either relation. Worth pinning them?

    Comparing getblock["params"][discriminator["parameterIndex"]]["name"] to discriminator["parameter"], and asserting the two object arms differ in prevout in the order values claims, fails on both and passes on this head.

  7. in src/rpc/util.h:425 in 270ecacf90 outdated
     420 | +struct RPCResultDiscriminator {
     421 | +    std::string param_name;
     422 | +    size_t param_index;
     423 | +    std::vector<UniValue> values;
     424 | +
     425 | +    RPCResultDiscriminator(std::string name, size_t index, std::vector<int> vals)
    


    jeanpablojp commented at 10:40 PM on September 6, 2026:

    The only constructor takes std::vector<int>, though values is already a std::vector<UniValue>. Of the fifteen methods with a oneOf result today, only getrawtransaction is another integer case and six more turn on a boolean or a string, so UniValue would cover eight instead of two. The other seven need a different shape rather than a wider type, so eight is the ceiling for this shape. Is the integer form deliberate for a first pass? I tried it, and a std::vector<UniValue> overload sits beside the int one, with getblockheader emitting "values": [true, false] and getblock unchanged.

    nit: param_name, param_index and values could carry the m_ prefix and the const that RPCResults and RPCResult next door use.


    nervana21 commented at 8:18 PM on September 7, 2026:

    Yes, the integer form was done deliberately on this first pass. Would prefer to add overloads in specific commits that use them.

    Nits done.

  8. in src/rpc/server.cpp:964 in 270ecacf90
     959 | +            disc_meta.pushKV("parameter", discriminator->param_name);
     960 | +            disc_meta.pushKV("parameterIndex", static_cast<int>(discriminator->param_index));
     961 | +            UniValue vals{UniValue::VARR};
     962 | +            vals.push_backV(discriminator->values);
     963 | +            disc_meta.pushKV("values", std::move(vals));
     964 | +            result_schema.pushKV("x-bitcoin-discriminatedResult", std::move(disc_meta));
    


    jeanpablojp commented at 10:40 PM on September 6, 2026:

    The schema field in OpenRPCDocResult already explains x-bitcoin-unit to whoever reads the help. Worth a line for x-bitcoin-discriminatedResult too? The parameter-side schema field next to it has the same gap for x-bitcoin-default-hint, if you would rather cover both at once.

    nit: the other x-bitcoin- keys in the document are all lowercase and hyphenated, so x-bitcoin-discriminated-result would match.


    nervana21 commented at 8:14 PM on September 7, 2026:

    Done.

  9. in src/rpc/blockchain.cpp:845 in 270ecacf90 outdated
     841 | @@ -842,7 +842,7 @@ static RPCMethod getblock()
     842 |                                         .prevout_doc = "(Only if undo information is available)",
     843 |                                         .vin_inner_elision = "The same output as verbosity = 2"})},
     844 |                          }}, /*elision_msg=*/"Same output as verbosity = 2")},
     845 | -                },
     846 | +                }, RPCResultDiscriminator{"verbosity", 1, std::vector<int>{0, 1, 2, 3}}},
    


    jeanpablojp commented at 10:40 PM on September 6, 2026:

    Worth putting in the description? Validating cannot pick the arm either, because a block with no spends comes back byte identical at verbosity 2 and 3, since a coinbase input has no prevout to add, and arm 3 lists prevout without requiring it, so both arms accept both responses and the oneOf does not resolve. That answers the reviewer who asks why a consumer cannot just validate, which the current wording leaves open. Already true on the base.


    nervana21 commented at 8:14 PM on September 7, 2026:

    Done.

  10. nervana21 commented at 8:37 PM on September 7, 2026: contributor

    Comparing getblock["params"][discriminator["parameterIndex"]]["name"] to discriminator["parameter"], and asserting the two object arms differ in prevout in the order values claims, fails on both and passes on this head.

    Good catch, thanks! I've updated the tests according to your suggestion

  11. rpc: Stamp `getblock` OpenRPC result discriminator
    OpenRPC documents `getblock` as a four-way `oneOf` on `verbosity`, but
    nothing in the schema maps `verbosity` values onto those branches.
    
    Add optional `RPCResultDiscriminator` on `RPCResults`, emit it as
    `x-bitcoin-discriminated-result`, and stamp `getblock` so `verbosity`
    selects the existing oneOf arms via {0, 1, 2, 3} in that order.
    
    No runtime behavior change.
    75385950ad
  12. rpc: Document `getblock` verbosity 2/3 schema overlap 78480bc08e
  13. rpc: Document OpenRPC schema extensions
    Parameter schemas may include `x-bitcoin-default-hint` when the default
    cannot be expressed as a JSON value.
    
    Multi-result schemas may include `x-bitcoin-discriminated-result`
    naming the request parameter or parameters that select among the
    top-level oneOf branches.
    213b2ed9f8
  14. nervana21 force-pushed on Sep 7, 2026


willcl-ark


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-09-09 07:56 UTC