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. nervana21 force-pushed on Sep 7, 2026
  12. nervana21 force-pushed on Sep 16, 2026
  13. nervana21 commented at 11:24 PM on September 16, 2026: contributor

    Rebased off master

  14. 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.
    02f7b33353
  15. rpc: Document `getblock` verbosity 2/3 schema overlap 7571e8dd4c
  16. 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.
    85f2993079
  17. in src/rpc/server.cpp:965 in 3b02b9b669
     960 | +        if (discriminator.has_value()) {
     961 | +            CHECK_NONFATAL(result_schema.exists("oneOf"));
     962 | +            CHECK_NONFATAL(result_schema.find_value("oneOf").size() == discriminator->m_values.size());
     963 | +            UniValue disc_meta{UniValue::VOBJ};
     964 | +            disc_meta.pushKV("parameter", discriminator->m_param_name);
     965 | +            disc_meta.pushKV("parameterIndex", static_cast<int>(discriminator->m_param_index));
    


    maflcko commented at 5:08 AM on September 17, 2026:

    What is this cast for?


    nervana21 commented at 3:14 PM on September 17, 2026:

    I've removed the cast. I had initially used it to get around an error that I can no longer reproduce

  18. nervana21 force-pushed on Sep 17, 2026
  19. nervana21 commented at 3:20 PM on September 17, 2026: contributor

    Updates:


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-20 19:52 UTC

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