Raw File
block_services.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com>     *)
(* Copyright (c) 2018-2021 Nomadic Labs. <contact@nomadic-labs.com>          *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

open Data_encoding
module Proof = Tezos_context_sigs.Context.Proof_types

(* TODO: V2.Tree32 has been chosen arbitrarily ; maybe it's not the best option *)
module Merkle_proof_encoding =
  Tezos_context_merkle_proof_encoding.Merkle_proof_encoding.V2.Tree32

type chain = [`Main | `Test | `Hash of Chain_id.t]

let metadata_rpc_arg =
  let construct = function `Always -> "always" | `Never -> "never" in
  let destruct arg =
    Result.catch_f
      (fun () ->
        match arg with
        | "always" -> `Always
        | "never" -> `Never
        | s -> invalid_arg (Format.sprintf "unrecognize parameter %s" s))
      (fun exn ->
        Format.sprintf "Invalid argument: %s" (Printexc.to_string exn))
  in
  let description = "defines the way metadata are queried" in
  let name = "metadata_rpc_arg" in
  Tezos_rpc.Arg.make ~descr:description ~name ~construct ~destruct ()

let parse_chain s =
  try
    match s with
    | "main" -> Ok `Main
    | "test" -> Ok `Test
    | h -> Ok (`Hash (Chain_id.of_b58check_exn h))
  with _ -> Error "Cannot parse chain identifier."

let chain_to_string = function
  | `Main -> "main"
  | `Test -> "test"
  | `Hash h -> Chain_id.to_b58check h

let chain_arg =
  let name = "chain_id" in
  let descr =
    "A chain identifier. This is either a chain hash in Base58Check notation \
     or a one the predefined aliases: 'main', 'test'."
  in
  let construct = chain_to_string in
  let destruct = parse_chain in
  Tezos_rpc.Arg.make ~name ~descr ~construct ~destruct ()

type block =
  [ `Genesis
  | `Head of int
  | `Alias of [`Caboose | `Checkpoint | `Savepoint] * int
  | `Hash of Block_hash.t * int
  | `Level of Int32.t ]

let parse_block s =
  let delims = ['~'; '-'; '+'] in
  let count_delims s =
    List.map
      (fun d ->
        (String.fold_left (fun i c -> if c = d then i + 1 else i) 0 s, d))
      delims
  in
  let split_on_delim counts =
    match List.fold_left (fun i (v, _) -> i + v) 0 counts with
    | 0 -> ([s], ' ')
    | 1 ->
        let delim =
          WithExceptions.Option.get ~loc:__LOC__
          @@ List.assoc ~equal:Int.equal 1 counts
        in
        (String.split_no_empty delim s, delim)
    | _ -> raise Exit
  in
  (* Converts a string representing a block level into a Int32. Fails
     if the resulting integer is negative. *)
  let to_valid_level_id s =
    let l = Int32.of_string s in
    if Compare.Int32.(l < 0l) then raise Exit else l
  in
  (* Converts an Int32 into a level identifier. If [?offset] is given,
     returns the level identifier minus that offset. *)
  let to_level ?offset l =
    if Compare.Int32.(l = 0l) then Ok `Genesis
    else
      match offset with
      | Some ofs -> Ok (`Level Int32.(sub l ofs))
      | None -> Ok (`Level l)
  in
  try
    match split_on_delim (count_delims s) with
    | ["genesis"], _ -> Ok `Genesis
    | ["genesis"; n], '+' -> Ok (`Level (Int32.of_string n))
    | ["head"], _ -> Ok (`Head 0)
    | ["head"; n], '~' | ["head"; n], '-' -> Ok (`Head (int_of_string n))
    | ["checkpoint"], _ -> Ok (`Alias (`Checkpoint, 0))
    | ["checkpoint"; n], '~' | ["checkpoint"; n], '-' ->
        Ok (`Alias (`Checkpoint, int_of_string n))
    | ["checkpoint"; n], '+' -> Ok (`Alias (`Checkpoint, -int_of_string n))
    | ["savepoint"], _ -> Ok (`Alias (`Savepoint, 0))
    | ["savepoint"; n], '~' | ["savepoint"; n], '-' ->
        Ok (`Alias (`Savepoint, int_of_string n))
    | ["savepoint"; n], '+' -> Ok (`Alias (`Savepoint, -int_of_string n))
    | ["caboose"], _ -> Ok (`Alias (`Caboose, 0))
    | ["caboose"; n], '~' | ["caboose"; n], '-' ->
        Ok (`Alias (`Caboose, int_of_string n))
    | ["caboose"; n], '+' -> Ok (`Alias (`Caboose, -int_of_string n))
    | [hol], _ -> (
        match Block_hash.of_b58check_opt hol with
        | Some h -> Ok (`Hash (h, 0))
        | None -> to_level (to_valid_level_id s))
    | [hol; n], '~' | [hol; n], '-' -> (
        match Block_hash.of_b58check_opt hol with
        | Some h -> Ok (`Hash (h, int_of_string n))
        | None ->
            let offset = to_valid_level_id n in
            to_level ~offset (to_valid_level_id hol))
    | [hol; n], '+' -> (
        match Block_hash.of_b58check_opt hol with
        | Some h -> Ok (`Hash (h, -int_of_string n))
        | None ->
            let offset = Int32.neg (to_valid_level_id n) in
            to_level ~offset (to_valid_level_id hol))
    | _ -> raise Exit
  with _ -> Error ("Cannot parse block identifier: " ^ s)

type range = [`Level of Int32.t] * [`Level of Int32.t]

let parse_block_range r =
  try
    match String.split '.' r with
    | [starts; ""; ends] ->
        let to_level s =
          let l = Int32.of_string s in
          if Compare.Int32.(l < 0l) then raise Exit ;
          `Level l
        in
        Ok (to_level starts, to_level ends)
    | _ -> raise Exit
  with _ ->
    Error
      (Format.asprintf
         "Cannot parse block range: %S (expected <level>..<level>)"
         r)

type block_or_range = Block of block | Range of range

let parse_block_or_range r =
  if String.contains r '.' then
    Result.map (fun r -> Range r) (parse_block_range r)
  else Result.map (fun b -> Block b) (parse_block r)

let alias_to_string = function
  | `Checkpoint -> "checkpoint"
  | `Savepoint -> "savepoint"
  | `Caboose -> "caboose"

let to_string = function
  | `Genesis -> "genesis"
  | `Alias (a, 0) -> alias_to_string a
  | `Alias (a, n) when n < 0 -> Printf.sprintf "%s+%d" (alias_to_string a) (-n)
  | `Alias (a, n) -> Printf.sprintf "%s~%d" (alias_to_string a) n
  | `Head 0 -> "head"
  | `Head n when n < 0 -> Printf.sprintf "head+%d" (-n)
  | `Head n -> Printf.sprintf "head~%d" n
  | `Hash (h, 0) -> Block_hash.to_b58check h
  | `Hash (h, n) when n < 0 ->
      Printf.sprintf "%s+%d" (Block_hash.to_b58check h) (-n)
  | `Hash (h, n) -> Printf.sprintf "%s~%d" (Block_hash.to_b58check h) n
  | `Level i -> Printf.sprintf "%d" (Int32.to_int i)

let blocks_arg =
  let name = "block_id" in
  let descr =
    "A block identifier. This is either a block hash in Base58Check notation, \
     one the predefined aliases: 'genesis', 'head' or a block level (index in \
     the chain). One might also use 'head~N' or '<hash>~N' where N is an \
     integer to denote the Nth predecessor of the designated block.Also, \
     '<hash>+N' denotes the Nth successor of a block."
  in
  let construct = to_string in
  let destruct = parse_block in
  Tezos_rpc.Arg.make ~name ~descr ~construct ~destruct ()

type chain_prefix = unit * chain

type prefix = chain_prefix * block

let chain_path = Tezos_rpc.Path.(root / "chains" /: chain_arg)

let mempool_path p = Tezos_rpc.Path.(p / "mempool")

let live_blocks_path p = Tezos_rpc.Path.(p / "live_blocks")

let dir_path : (chain_prefix, chain_prefix) Tezos_rpc.Path.t =
  Tezos_rpc.Path.(open_root / "blocks")

let path = Tezos_rpc.Path.(dir_path /: blocks_arg)

type operation_list_quota = {max_size : int; max_op : int option}

let operation_list_quota_encoding =
  conv
    (fun {max_size; max_op} -> (max_size, max_op))
    (fun (max_size, max_op) -> {max_size; max_op})
    (obj2 (req "max_size" int31) (opt "max_op" int31))

let raw_context_encoding =
  let open Proof in
  mu "raw_context" (fun encoding ->
      union
        [
          case
            (Tag 0)
            bytes
            ~title:"Key"
            (function Key k -> Some k | _ -> None)
            (fun k -> Key k);
          case
            (Tag 1)
            (assoc encoding)
            ~title:"Dir"
            (function Dir map -> Some (String.Map.bindings map) | _ -> None)
            (fun bindings ->
              Dir
                (List.fold_left
                   (fun wip_map (k, v) -> String.Map.add k v wip_map)
                   String.Map.empty
                   bindings));
          case
            (Tag 2)
            null
            ~title:"Cut"
            (function Cut -> Some () | _ -> None)
            (fun () -> Cut);
        ])

let raw_context_insert =
  let open Proof in
  let default = Dir String.Map.empty in
  (* not tail recursive but over the length of [k], which is small *)
  let rec aux (k, v) ctx =
    let d = match ctx with Dir d -> d | Key _ | Cut -> String.Map.empty in
    match k with
    | [] -> v
    | [kh] -> Dir (String.Map.add kh v d)
    | kh :: ktl ->
        Dir
          (String.Map.update
             kh
             (fun ctxtopt ->
               let ctx' = Option.value ctxtopt ~default in
               Some (aux (ktl, v) ctx'))
             d)
  in
  aux

let stringmap_encoding value_encoding =
  let open Data_encoding in
  conv
    String.Map.bindings
    (fun l ->
      List.fold_left
        (fun acc (k, v) -> String.Map.add k v acc)
        String.Map.empty
        l)
    (list (tup2 string value_encoding))

let merkle_tree_encoding : Proof.merkle_tree Data_encoding.t =
  let open Proof in
  let open Data_encoding in
  let hash_tag = 0 and hash_encoding = tup2 bool string in
  let data_tag = 1 and data_encoding = raw_context_encoding in
  let continue_tag = 2 in
  mu "merkle_tree" (fun encoding ->
      let continue_encoding = encoding in
      stringmap_encoding
        (matching
           (function
             | Hash (kind, content) ->
                 matched
                   hash_tag
                   hash_encoding
                   ( (match kind with Contents -> true | Node -> false),
                     content )
             | Data raw_context -> matched data_tag data_encoding raw_context
             | Continue dir -> matched continue_tag encoding dir)
           [
             case
               (Tag hash_tag)
               ~title:"Hash"
               hash_encoding
               (function Hash (k, h) -> Some (k = Contents, h) | _ -> None)
               (fun (k, h) ->
                 let kind = if k then Contents else Node in
                 Hash (kind, h));
             case
               (Tag data_tag)
               data_encoding
               ~title:"Data"
               (function Data raw_context -> Some raw_context | _ -> None)
               (fun raw_context -> Data raw_context);
             case
               (Tag continue_tag)
               continue_encoding
               ~title:"Continue"
               (function Continue dir -> Some dir | _ -> None)
               (fun dir -> Continue dir);
           ]))

module type PROTO = sig
  val hash : Protocol_hash.t

  type block_header_data

  val block_header_data_encoding : block_header_data Data_encoding.t

  type block_header_metadata

  val block_header_metadata_encoding : block_header_metadata Data_encoding.t

  type operation_data

  type operation_receipt

  type operation = {
    shell : Operation.shell_header;
    protocol_data : operation_data;
  }

  val operation_data_encoding : operation_data Data_encoding.t

  val operation_receipt_encoding : operation_receipt Data_encoding.t

  val operation_data_and_receipt_encoding :
    (operation_data * operation_receipt) Data_encoding.t
end

type protocols = {
  current_protocol : Protocol_hash.t;
  next_protocol : Protocol_hash.t;
}

let raw_protocol_encoding =
  conv
    (fun {current_protocol; next_protocol} -> (current_protocol, next_protocol))
    (fun (current_protocol, next_protocol) -> {current_protocol; next_protocol})
    (obj2
       (req "protocol" Protocol_hash.encoding)
       (req "next_protocol" Protocol_hash.encoding))

module Make (Proto : PROTO) (Next_proto : PROTO) = struct
  let protocol_hash = Protocol_hash.to_b58check Proto.hash

  let next_protocol_hash = Protocol_hash.to_b58check Next_proto.hash

  type raw_block_header = {
    shell : Block_header.shell_header;
    protocol_data : Proto.block_header_data;
  }

  let raw_block_header_encoding =
    def "raw_block_header"
    @@ conv
         (fun {shell; protocol_data} -> (shell, protocol_data))
         (fun (shell, protocol_data) -> {shell; protocol_data})
         (merge_objs
            Block_header.shell_header_encoding
            Proto.block_header_data_encoding)

  type block_header = {
    chain_id : Chain_id.t;
    hash : Block_hash.t;
    shell : Block_header.shell_header;
    protocol_data : Proto.block_header_data;
  }

  let block_header_encoding =
    def "block_header"
    @@ conv
         (fun {chain_id; hash; shell; protocol_data} ->
           (((), chain_id, hash), {shell; protocol_data}))
         (fun (((), chain_id, hash), {shell; protocol_data}) ->
           {chain_id; hash; shell; protocol_data})
         (merge_objs
            (obj3
               (req "protocol" (constant protocol_hash))
               (req "chain_id" Chain_id.encoding)
               (req "hash" Block_hash.encoding))
            raw_block_header_encoding)

  type block_metadata = {
    protocol_data : Proto.block_header_metadata;
    test_chain_status : Test_chain_status.t;
    (* for the next block: *)
    max_operations_ttl : int;
    max_operation_data_length : int;
    max_block_header_length : int;
    operation_list_quota : operation_list_quota list;
  }

  let block_metadata_encoding =
    def "block_header_metadata"
    @@ conv
         (fun {
                protocol_data;
                test_chain_status;
                max_operations_ttl;
                max_operation_data_length;
                max_block_header_length;
                operation_list_quota;
              } ->
           ( ( (),
               (),
               test_chain_status,
               max_operations_ttl,
               max_operation_data_length,
               max_block_header_length,
               operation_list_quota ),
             protocol_data ))
         (fun ( ( (),
                  (),
                  test_chain_status,
                  max_operations_ttl,
                  max_operation_data_length,
                  max_block_header_length,
                  operation_list_quota ),
                protocol_data ) ->
           {
             protocol_data;
             test_chain_status;
             max_operations_ttl;
             max_operation_data_length;
             max_block_header_length;
             operation_list_quota;
           })
         (merge_objs
            (obj7
               (req "protocol" (constant protocol_hash))
               (req "next_protocol" (constant next_protocol_hash))
               (req "test_chain_status" Test_chain_status.encoding)
               (req "max_operations_ttl" int31)
               (req "max_operation_data_length" int31)
               (req "max_block_header_length" int31)
               (req
                  "max_operation_list_length"
                  (dynamic_size (list operation_list_quota_encoding))))
            Proto.block_header_metadata_encoding)

  let next_operation_encoding =
    let open Data_encoding in
    def "next_operation"
    @@ conv
         (fun Next_proto.{shell; protocol_data} -> ((), (shell, protocol_data)))
         (fun ((), (shell, protocol_data)) -> {shell; protocol_data})
         (merge_objs
            (obj1 (req "protocol" (constant next_protocol_hash)))
            (merge_objs
               (dynamic_size Operation.shell_header_encoding)
               (dynamic_size Next_proto.operation_data_encoding)))

  type operation_receipt =
    | Empty
    | Too_large
    | Receipt of Proto.operation_receipt

  type operation = {
    chain_id : Chain_id.t;
    hash : Operation_hash.t;
    shell : Operation.shell_header;
    protocol_data : Proto.operation_data;
    receipt : operation_receipt;
  }

  let operation_data_encoding =
    let open Data_encoding in
    union
      ~tag_size:`Uint8
      [
        case
          ~title:"Operation with too large metadata"
          (Tag 0)
          (merge_objs
             Proto.operation_data_encoding
             (obj1 (req "metadata" (constant "too large"))))
          (function
            | operation_data, Too_large -> Some (operation_data, ()) | _ -> None)
          (fun (operation_data, ()) -> (operation_data, Too_large));
        case
          ~title:"Operation without metadata"
          (Tag 1)
          Proto.operation_data_encoding
          (function operation_data, Empty -> Some operation_data | _ -> None)
          (fun operation_data -> (operation_data, Empty));
        case
          ~title:"Operation with metadata"
          (Tag 2)
          Proto.operation_data_and_receipt_encoding
          (function
            | operation_data, Receipt receipt -> Some (operation_data, receipt)
            | _ -> None)
          (function
            | operation_data, receipt -> (operation_data, Receipt receipt));
      ]

  let operation_encoding =
    def "operation"
    @@
    let open Data_encoding in
    conv
      (fun {chain_id; hash; shell; protocol_data; receipt} ->
        (((), chain_id, hash), (shell, (protocol_data, receipt))))
      (fun (((), chain_id, hash), (shell, (protocol_data, receipt))) ->
        {chain_id; hash; shell; protocol_data; receipt})
      (merge_objs
         (obj3
            (req "protocol" (constant protocol_hash))
            (req "chain_id" Chain_id.encoding)
            (req "hash" Operation_hash.encoding))
         (merge_objs
            (dynamic_size Operation.shell_header_encoding)
            (dynamic_size operation_data_encoding)))

  type block_info = {
    chain_id : Chain_id.t;
    hash : Block_hash.t;
    header : raw_block_header;
    metadata : block_metadata option;
    operations : operation list list;
  }

  let block_info_encoding =
    conv
      (fun {chain_id; hash; header; metadata; operations} ->
        ((), chain_id, hash, header, metadata, operations))
      (fun ((), chain_id, hash, header, metadata, operations) ->
        {chain_id; hash; header; metadata; operations})
      (obj6
         (req "protocol" (constant protocol_hash))
         (req "chain_id" Chain_id.encoding)
         (req "hash" Block_hash.encoding)
         (req "header" (dynamic_size raw_block_header_encoding))
         (opt "metadata" (dynamic_size block_metadata_encoding))
         (req "operations" (list (dynamic_size (list operation_encoding)))))

  module S = struct
    let path : prefix Tezos_rpc.Path.context = Tezos_rpc.Path.open_root

    let hash =
      Tezos_rpc.Service.get_service
        ~description:"The block's hash, its unique identifier."
        ~query:Tezos_rpc.Query.empty
        ~output:Block_hash.encoding
        Tezos_rpc.Path.(path / "hash")

    let header =
      Tezos_rpc.Service.get_service
        ~description:"The whole block header."
        ~query:Tezos_rpc.Query.empty
        ~output:block_header_encoding
        Tezos_rpc.Path.(path / "header")

    let raw_header =
      Tezos_rpc.Service.get_service
        ~description:"The whole block header (unparsed)."
        ~query:Tezos_rpc.Query.empty
        ~output:bytes
        Tezos_rpc.Path.(path / "header" / "raw")

    let metadata =
      Tezos_rpc.Service.get_service
        ~description:"All the metadata associated to the block."
        ~query:Tezos_rpc.Query.empty
        ~output:block_metadata_encoding
        Tezos_rpc.Path.(path / "metadata")

    let metadata_hash =
      Tezos_rpc.Service.get_service
        ~description:
          "Hash of the metadata associated to the block. This is only set on \
           blocks starting from environment V1."
        ~query:Tezos_rpc.Query.empty
        ~output:Block_metadata_hash.encoding
        Tezos_rpc.Path.(path / "metadata_hash")

    let protocols =
      Tezos_rpc.Service.get_service
        ~description:"Current and next protocol."
        ~query:Tezos_rpc.Query.empty
        ~output:raw_protocol_encoding
        Tezos_rpc.Path.(path / "protocols")

    let resulting_context_hash =
      Tezos_rpc.Service.get_service
        ~description:"Context hash resulting of the block application."
        ~query:Tezos_rpc.Query.empty
        ~output:Context_hash.encoding
        Tezos_rpc.Path.(path / "resulting_context_hash")

    module Header = struct
      let path = Tezos_rpc.Path.(path / "header")

      let shell_header =
        Tezos_rpc.Service.get_service
          ~description:"The shell-specific fragment of the block header."
          ~query:Tezos_rpc.Query.empty
          ~output:Block_header.shell_header_encoding
          Tezos_rpc.Path.(path / "shell")

      let protocol_data =
        Tezos_rpc.Service.get_service
          ~description:"The version-specific fragment of the block header."
          ~query:Tezos_rpc.Query.empty
          ~output:
            (conv
               (fun h -> ((), h))
               (fun ((), h) -> h)
               (merge_objs
                  (obj1 (req "protocol" (constant protocol_hash)))
                  Proto.block_header_data_encoding))
          Tezos_rpc.Path.(path / "protocol_data")

      let raw_protocol_data =
        Tezos_rpc.Service.get_service
          ~description:
            "The version-specific fragment of the block header (unparsed)."
          ~query:Tezos_rpc.Query.empty
          ~output:bytes
          Tezos_rpc.Path.(path / "protocol_data" / "raw")
    end

    let force_operation_metadata_query =
      let open Tezos_rpc.Query in
      query (fun force_metadata metadata ->
          object
            method force_metadata = force_metadata

            method metadata = metadata
          end)
      |+ flag
           "force_metadata"
           ~descr:
             "DEPRECATED: Forces to recompute the operations metadata if it \
              was considered as too large."
           (fun x -> x#force_metadata)
      |+ opt_field
           "metadata"
           ~descr:
             "Specifies whether or not if the operations metadata should be \
              returned. To get the metadata, even if it is needed to recompute \
              them, use \"always\". To avoid getting the metadata, use \
              \"never\". By default, the metadata will be returned depending \
              on the node's metadata size limit policy."
           metadata_rpc_arg
           (fun x -> x#metadata)
      |> seal

    module Operations = struct
      let path = Tezos_rpc.Path.(path / "operations")

      let operations =
        Tezos_rpc.Service.get_service
          ~description:"All the operations included in the block."
          ~query:force_operation_metadata_query
          ~output:(list (dynamic_size (list operation_encoding)))
          path

      let list_arg =
        let name = "list_offset" in
        let descr = "Index `n` of the requested validation pass." in
        let construct = string_of_int in
        let destruct s =
          try Ok (int_of_string s)
          with _ -> Error (Format.sprintf "Invalid list offset (%s)" s)
        in
        Tezos_rpc.Arg.make ~name ~descr ~construct ~destruct ()

      let offset_arg =
        let name = "operation_offset" in
        let descr =
          "Index `m` of the requested operation in its validation pass."
        in
        let construct = string_of_int in
        let destruct s =
          try Ok (int_of_string s)
          with _ -> Error (Format.sprintf "Invalid operation offset (%s)" s)
        in
        Tezos_rpc.Arg.make ~name ~descr ~construct ~destruct ()

      let operations_in_pass =
        Tezos_rpc.Service.get_service
          ~description:
            "All the operations included in `n-th` validation pass of the \
             block."
          ~query:force_operation_metadata_query
          ~output:(list operation_encoding)
          Tezos_rpc.Path.(path /: list_arg)

      let operation =
        Tezos_rpc.Service.get_service
          ~description:
            "The `m-th` operation in the `n-th` validation pass of the block."
          ~query:force_operation_metadata_query
          ~output:operation_encoding
          Tezos_rpc.Path.(path /: list_arg /: offset_arg)
    end

    module Operation_hashes = struct
      let path = Tezos_rpc.Path.(path / "operation_hashes")

      let operation_hashes =
        Tezos_rpc.Service.get_service
          ~description:"The hashes of all the operations included in the block."
          ~query:Tezos_rpc.Query.empty
          ~output:(list (list Operation_hash.encoding))
          path

      let operation_hashes_in_pass =
        Tezos_rpc.Service.get_service
          ~description:
            "All the operations included in `n-th` validation pass of the \
             block."
          ~query:Tezos_rpc.Query.empty
          ~output:(list Operation_hash.encoding)
          Tezos_rpc.Path.(path /: Operations.list_arg)

      let operation_hash =
        Tezos_rpc.Service.get_service
          ~description:
            "The hash of then `m-th` operation in the `n-th` validation pass \
             of the block."
          ~query:Tezos_rpc.Query.empty
          ~output:Operation_hash.encoding
          Tezos_rpc.Path.(path /: Operations.list_arg /: Operations.offset_arg)
    end

    module Operation_metadata_hashes = struct
      let root =
        Tezos_rpc.Service.get_service
          ~description:
            "The root hash of the operations metadata from the block. This is \
             only set on blocks starting from environment V1."
          ~query:Tezos_rpc.Query.empty
          ~output:Operation_metadata_list_list_hash.encoding
          Tezos_rpc.Path.(path / "operations_metadata_hash")

      let path = Tezos_rpc.Path.(path / "operation_metadata_hashes")

      let operation_metadata_hashes =
        Tezos_rpc.Service.get_service
          ~description:
            "The hashes of all the operation metadata included in the block. \
             This is only set on blocks starting from environment V1."
          ~query:Tezos_rpc.Query.empty
          ~output:(list (list Operation_metadata_hash.encoding))
          path

      let operation_metadata_hashes_in_pass =
        Tezos_rpc.Service.get_service
          ~description:
            "All the operation metadata included in `n-th` validation pass of \
             the block. This is only set on blocks starting from environment \
             V1."
          ~query:Tezos_rpc.Query.empty
          ~output:(list Operation_metadata_hash.encoding)
          Tezos_rpc.Path.(path /: Operations.list_arg)

      let operation_metadata_hash =
        Tezos_rpc.Service.get_service
          ~description:
            "The hash of then `m-th` operation metadata in the `n-th` \
             validation pass of the block. This is only set on blocks starting \
             from environment V1."
          ~query:Tezos_rpc.Query.empty
          ~output:Operation_metadata_hash.encoding
          Tezos_rpc.Path.(path /: Operations.list_arg /: Operations.offset_arg)
    end

    module Helpers = struct
      let path = Tezos_rpc.Path.(path / "helpers")

      module Forge = struct
        let block_header =
          Tezos_rpc.Service.post_service
            ~description:"Forge a block header"
            ~query:Tezos_rpc.Query.empty
            ~input:Block_header.encoding
            ~output:(obj1 (req "block" bytes))
            Tezos_rpc.Path.(path / "forge_block_header")
      end

      module Preapply = struct
        let path = Tezos_rpc.Path.(path / "preapply")

        let block_result_encoding =
          obj2
            (req "shell_header" Block_header.shell_header_encoding)
            (req
               "operations"
               (list (Preapply_result.encoding Tezos_rpc.Error.encoding)))

        type block_param = {
          protocol_data : Next_proto.block_header_data;
          operations : Next_proto.operation list list;
        }

        let block_param_encoding =
          conv
            (fun {protocol_data; operations} -> (protocol_data, operations))
            (fun (protocol_data, operations) -> {protocol_data; operations})
            (obj2
               (req
                  "protocol_data"
                  (conv
                     (fun h -> ((), h))
                     (fun ((), h) -> h)
                     (merge_objs
                        (obj1 (req "protocol" (constant next_protocol_hash)))
                        (dynamic_size Next_proto.block_header_data_encoding))))
               (req
                  "operations"
                  (list (dynamic_size (list next_operation_encoding)))))

        let block_query =
          let open Tezos_rpc.Query in
          query (fun sort timestamp ->
              object
                method sort_operations = sort

                method timestamp = timestamp
              end)
          |+ flag "sort" (fun t -> t#sort_operations)
          |+ opt_field "timestamp" Time.Protocol.rpc_arg (fun t -> t#timestamp)
          |> seal

        let block =
          Tezos_rpc.Service.post_service
            ~description:
              "Simulate the validation of a block that would contain the given \
               operations and return the resulting fitness and context hash."
            ~query:block_query
            ~input:block_param_encoding
            ~output:block_result_encoding
            Tezos_rpc.Path.(path / "block")

        let operations =
          Tezos_rpc.Service.post_service
            ~description:
              "Simulate the application of the operations with the context of \
               the given block and return the result of each operation \
               application."
            ~query:Tezos_rpc.Query.empty
            ~input:(list next_operation_encoding)
            ~output:
              (list
                 (dynamic_size Next_proto.operation_data_and_receipt_encoding))
            Tezos_rpc.Path.(path / "operations")
      end

      let complete =
        let prefix_arg =
          let destruct s = Ok s and construct s = s in
          Tezos_rpc.Arg.make ~name:"prefix" ~destruct ~construct ()
        in
        Tezos_rpc.Service.get_service
          ~description:
            "Try to complete a prefix of a Base58Check-encoded data. This RPC \
             is actually able to complete hashes of block, operations, \
             public_keys and contracts."
          ~query:Tezos_rpc.Query.empty
          ~output:(list string)
          Tezos_rpc.Path.(path / "complete" /: prefix_arg)
    end

    module Context = struct
      let path = Tezos_rpc.Path.(path / "context")

      let raw_bytes_path = Tezos_rpc.Path.(path / "raw" / "bytes")

      let merkle_tree_v1_path = Tezos_rpc.Path.(path / "merkle_tree")

      (* The duplication of the ["/merkle_tree"] RPC path is due to MR !5535.
         This MR introduces a breaking change in the former [merkle_tree] RPC,
         because it changes the data type it returns.
         In order to avoid breaking clients that still use the old code,
         we keep the old RPC at path ["/merkle_tree"], and introduce the new one
         at path ["/merkle_tree_v2"].
         Once we are sure that all clients have migrated to the new code, we will
         1) duplicate the behaviour of ["/merkle_tree_v2"] onto ["/merkle_tree"],
            and make the clients call ["/merkle_tree"],
         2) once all clients have applied patch 1), remove ["/merkle_tree_v2"]
            altogether. *)
      let merkle_tree_v2_path = Tezos_rpc.Path.(path / "merkle_tree_v2")

      let context_path_arg : string Tezos_rpc.Arg.t =
        let name = "context_path" in
        let descr = "A path inside the context" in
        let construct s = s in
        let destruct s = Ok s in
        Tezos_rpc.Arg.make ~name ~descr ~construct ~destruct ()

      let raw_context_query : < depth : int option > Tezos_rpc.Query.t =
        let open Tezos_rpc.Query in
        query (fun depth ->
            object
              method depth = depth
            end)
        |+ opt_field "depth" Tezos_rpc.Arg.uint (fun t -> t#depth)
        |> seal

      let read =
        Tezos_rpc.Service.get_service
          ~description:"Returns the raw context."
          ~query:raw_context_query
          ~output:raw_context_encoding
          Tezos_rpc.Path.(raw_bytes_path /:* context_path_arg)

      let merkle_tree_query : < holey : bool option > Tezos_rpc.Query.t =
        let open Tezos_rpc.Query in
        query (fun holey ->
            object
              method holey = holey
            end)
        |+ opt_field
             ~descr:"Send only hashes, omit data of key"
             "holey"
             Tezos_rpc.Arg.bool
             (fun t -> t#holey)
        |> seal

      let merkle_tree =
        Tezos_rpc.Service.get_service
          ~description:"Returns the merkle tree of a piece of context."
          ~query:merkle_tree_query
          ~output:(option merkle_tree_encoding)
          Tezos_rpc.Path.(merkle_tree_v1_path /:* context_path_arg)

      let merkle_tree_v2 =
        Tezos_rpc.Service.get_service
          ~description:"Returns the Irmin merkle tree of a piece of context."
          ~query:merkle_tree_query
          ~output:(option Merkle_proof_encoding.tree_proof_encoding)
          Tezos_rpc.Path.(merkle_tree_v2_path /:* context_path_arg)
    end

    let info =
      Tezos_rpc.Service.get_service
        ~description:
          "All the information about a block. The associated metadata may not \
           be present depending on the history mode and block's distance from \
           the head."
        ~query:force_operation_metadata_query
        ~output:block_info_encoding
        path

    module Mempool = struct
      type t = {
        applied : (Operation_hash.t * Next_proto.operation) list;
        refused : (Next_proto.operation * error list) Operation_hash.Map.t;
        outdated : (Next_proto.operation * error list) Operation_hash.Map.t;
        branch_refused :
          (Next_proto.operation * error list) Operation_hash.Map.t;
        branch_delayed :
          (Next_proto.operation * error list) Operation_hash.Map.t;
        unprocessed : Next_proto.operation Operation_hash.Map.t;
      }

      let version_0_encoding =
        conv
          (fun {
                 applied;
                 refused;
                 outdated;
                 branch_refused;
                 branch_delayed;
                 unprocessed;
               } ->
            ( applied,
              refused,
              outdated,
              branch_refused,
              branch_delayed,
              unprocessed ))
          (fun ( applied,
                 refused,
                 outdated,
                 branch_refused,
                 branch_delayed,
                 unprocessed ) ->
            {
              applied;
              refused;
              outdated;
              branch_refused;
              branch_delayed;
              unprocessed;
            })
          (obj6
             (req
                "applied"
                (list
                   (conv
                      (fun (hash, (op : Next_proto.operation)) ->
                        ((hash, op.shell), op.protocol_data))
                      (fun ((hash, shell), protocol_data) ->
                        (hash, {shell; protocol_data}))
                      (merge_objs
                         (merge_objs
                            (obj1 (req "hash" Operation_hash.encoding))
                            (dynamic_size Operation.shell_header_encoding))
                         (dynamic_size Next_proto.operation_data_encoding)))))
             (req
                "refused"
                (Operation_hash.Map.encoding
                   (merge_objs
                      (dynamic_size next_operation_encoding)
                      (obj1 (req "error" Tezos_rpc.Error.encoding)))))
             (req
                "outdated"
                (Operation_hash.Map.encoding
                   (merge_objs
                      (dynamic_size next_operation_encoding)
                      (obj1 (req "error" Tezos_rpc.Error.encoding)))))
             (req
                "branch_refused"
                (Operation_hash.Map.encoding
                   (merge_objs
                      (dynamic_size next_operation_encoding)
                      (obj1 (req "error" Tezos_rpc.Error.encoding)))))
             (req
                "branch_delayed"
                (Operation_hash.Map.encoding
                   (merge_objs
                      (dynamic_size next_operation_encoding)
                      (obj1 (req "error" Tezos_rpc.Error.encoding)))))
             (req
                "unprocessed"
                (Operation_hash.Map.encoding
                   (dynamic_size next_operation_encoding))))

      let version_1_encoding =
        let operations_with_error_encoding kind =
          req
            kind
            (conv
               (fun map -> Operation_hash.Map.bindings map)
               (fun list -> list |> List.to_seq |> Operation_hash.Map.of_seq)
               (list
                  (merge_objs
                     (obj1 (req "hash" Operation_hash.encoding))
                     (merge_objs
                        next_operation_encoding
                        (obj1 (req "error" Tezos_rpc.Error.encoding))))))
        in
        conv
          (fun {
                 applied;
                 refused;
                 outdated;
                 branch_refused;
                 branch_delayed;
                 unprocessed;
               } ->
            ( applied,
              refused,
              outdated,
              branch_refused,
              branch_delayed,
              unprocessed ))
          (fun ( applied,
                 refused,
                 outdated,
                 branch_refused,
                 branch_delayed,
                 unprocessed ) ->
            {
              applied;
              refused;
              outdated;
              branch_refused;
              branch_delayed;
              unprocessed;
            })
          (obj6
             (req
                "applied"
                (list
                   (conv
                      (fun (hash, (op : Next_proto.operation)) ->
                        ((hash, op.shell), op.protocol_data))
                      (fun ((hash, shell), protocol_data) ->
                        (hash, {shell; protocol_data}))
                      (merge_objs
                         (merge_objs
                            (obj1 (req "hash" Operation_hash.encoding))
                            Operation.shell_header_encoding)
                         (dynamic_size Next_proto.operation_data_encoding)))))
             (operations_with_error_encoding "refused")
             (operations_with_error_encoding "outdated")
             (operations_with_error_encoding "branch_refused")
             (operations_with_error_encoding "branch_delayed")
             (req
                "unprocessed"
                (conv
                   (fun map -> Operation_hash.Map.bindings map)
                   (fun list ->
                     list |> List.to_seq |> Operation_hash.Map.of_seq)
                   (list
                      (merge_objs
                         (obj1 (req "hash" Operation_hash.encoding))
                         next_operation_encoding)))))

      (* This encoding should be always the one by default. *)
      let encoding = version_1_encoding

      (* If you change this value, also change [encoding]. *)
      let default_pending_operations_version = 1

      let pending_query =
        let open Tezos_rpc.Query in
        query
          (fun
            version
            applied
            refused
            outdated
            branch_refused
            branch_delayed
            validation_passes
          ->
            object
              method version = version

              method applied = applied

              method refused = refused

              method outdated = outdated

              method branch_refused = branch_refused

              method branch_delayed = branch_delayed

              method validation_passes = validation_passes
            end)
        |+ field
             "version"
             Tezos_rpc.Arg.int
             default_pending_operations_version
             (fun t -> t#version)
        |+ field
             ~descr:"Include applied operations (true by default)"
             "applied"
             Tezos_rpc.Arg.bool
             true
             (fun t -> t#applied)
        |+ field
             ~descr:"Include refused operations (true by default)"
             "refused"
             Tezos_rpc.Arg.bool
             true
             (fun t -> t#refused)
        |+ field
             ~descr:"Include outdated operations (true by default)"
             "outdated"
             Tezos_rpc.Arg.bool
             true
             (fun t -> t#outdated)
        |+ field
             ~descr:"Include branch refused operations (true by default)"
             "branch_refused"
             Tezos_rpc.Arg.bool
             true
             (fun t -> t#branch_refused)
        |+ field
             ~descr:"Include branch delayed operations (true by default)"
             "branch_delayed"
             Tezos_rpc.Arg.bool
             true
             (fun t -> t#branch_delayed)
        |+ multi_field
             ~descr:
               "Include operations filtered by validation pass (all by default)"
             "validation_pass"
             Tezos_rpc.Arg.int
             (fun t -> t#validation_passes)
        |> seal

      (* If you update this datatype, please update also [supported_version]. *)
      type t_with_version = Version_0 of t | Version_1 of t

      (* This value should be consistent with [t_with_version]. *)
      let supported_version = [0; 1]

      let pending_operations_encoding =
        union
          [
            case
              ~title:"new_encoding_pending_operations"
              (Tag 1)
              version_1_encoding
              (function
                | Version_1 pending_operations -> Some pending_operations
                | Version_0 _ -> None)
              (fun pending_operations -> Version_1 pending_operations);
            case
              ~title:"old_encoding_pending_operations"
              Json_only
              version_0_encoding
              (function
                | Version_0 pending_operations -> Some pending_operations
                | Version_1 _ -> None)
              (fun pending_operations -> Version_0 pending_operations);
          ]

      let pending_operations_version_dispatcher ~version pending_operations =
        if version = 0 then
          Tezos_rpc.Answer.return (Version_0 pending_operations)
        else if version = 1 then
          Tezos_rpc.Answer.return (Version_1 pending_operations)
        else
          Tezos_rpc.Answer.fail
            (Tezos_rpc.Error.bad_version
               ~given:version
               ~supported:supported_version)

      let pending_operations path =
        Tezos_rpc.Service.get_service
          ~description:"List the prevalidated operations."
          ~query:pending_query
          ~output:pending_operations_encoding
          Tezos_rpc.Path.(path / "pending_operations")

      let ban_operation path =
        Tezos_rpc.Service.post_service
          ~description:
            "Remove an operation from the mempool if present, reverting its \
             effect if it was applied. Add it to the set of banned operations \
             to prevent it from being fetched/processed/injected in the \
             future. Note: If the baker has already received the operation, \
             then it's necessary to restart it to flush the operation from it."
          ~query:Tezos_rpc.Query.empty
          ~input:Operation_hash.encoding
          ~output:unit
          Tezos_rpc.Path.(path / "ban_operation")

      let unban_operation path =
        Tezos_rpc.Service.post_service
          ~description:
            "Remove an operation from the set of banned operations (nothing \
             happens if it was not banned)."
          ~query:Tezos_rpc.Query.empty
          ~input:Operation_hash.encoding
          ~output:unit
          Tezos_rpc.Path.(path / "unban_operation")

      let unban_all_operations path =
        Tezos_rpc.Service.post_service
          ~description:"Clear the set of banned operations."
          ~query:Tezos_rpc.Query.empty
          ~input:Data_encoding.empty
          ~output:unit
          Tezos_rpc.Path.(path / "unban_all_operations")

      let mempool_query =
        let open Tezos_rpc.Query in
        query
          (fun
            applied
            refused
            outdated
            branch_refused
            branch_delayed
            validation_passes
          ->
            object
              method applied = applied

              method refused = refused

              method outdated = outdated

              method branch_refused = branch_refused

              method branch_delayed = branch_delayed

              method validation_passes = validation_passes
            end)
        |+ field
             ~descr:"Include applied operations (set by default)"
             "applied"
             Tezos_rpc.Arg.bool
             true
             (fun t -> t#applied)
        |+ field
             ~descr:"Include refused operations"
             "refused"
             Tezos_rpc.Arg.bool
             false
             (fun t -> t#refused)
        |+ field
             ~descr:"Include outdated operations"
             "outdated"
             Tezos_rpc.Arg.bool
             false
             (fun t -> t#outdated)
        |+ field
             ~descr:"Include branch refused operations"
             "branch_refused"
             Tezos_rpc.Arg.bool
             false
             (fun t -> t#branch_refused)
        |+ field
             ~descr:"Include branch delayed operations (set by default)"
             "branch_delayed"
             Tezos_rpc.Arg.bool
             true
             (fun t -> t#branch_delayed)
        |+ multi_field
             ~descr:
               "Include operations filtered by validation pass (all by default)"
             "validation_pass"
             Tezos_rpc.Arg.int
             (fun t -> t#validation_passes)
        |> seal

      (* We extend the object so that the fields of 'next_operation'
         stay toplevel, for backward compatibility. *)
      let processed_operation_encoding =
        merge_objs
          (merge_objs
             (obj1 (req "hash" Operation_hash.encoding))
             next_operation_encoding)
          (obj1 (dft "error" Tezos_rpc.Error.opt_encoding None))

      let monitor_operations path =
        Tezos_rpc.Service.get_service
          ~description:"Monitor the mempool operations."
          ~query:mempool_query
          ~output:(list processed_operation_encoding)
          Tezos_rpc.Path.(path / "monitor_operations")

      let get_filter_query =
        let open Tezos_rpc.Query in
        query (fun include_default ->
            object
              method include_default = include_default
            end)
        |+ field
             ~descr:"Show fields equal to their default value (set by default)"
             "include_default"
             Tezos_rpc.Arg.bool
             true
             (fun t -> t#include_default)
        |> seal

      let get_filter path =
        Tezos_rpc.Service.get_service
          ~description:
            {|Get the configuration of the mempool filter. The minimal_fees are in mutez. Each field minimal_nanotez_per_xxx is a rational number given as a numerator and a denominator, e.g. "minimal_nanotez_per_gas_unit": [ "100", "1" ].|}
          ~query:get_filter_query
          ~output:json
          Tezos_rpc.Path.(path / "filter")

      let set_filter path =
        Tezos_rpc.Service.post_service
          ~description:
            {|Set the configuration of the mempool filter. **If any of the fields is absent from the input JSON, then it is set to the default value for this field (i.e. its value in the default configuration), even if it previously had a different value.** If the input JSON does not describe a valid configuration, then the configuration is left unchanged. Also return the new configuration (which may differ from the input if it had omitted fields or was invalid). You may call [./octez-client rpc get '/chains/main/mempool/filter?include_default=true'] to see an example of JSON describing a valid configuration.|}
          ~query:Tezos_rpc.Query.empty
          ~input:json
          ~output:json
          Tezos_rpc.Path.(path / "filter")

      let request_operations_query =
        let open Tezos_rpc.Query in
        query (fun peer_id ->
            object
              method peer_id = peer_id
            end)
        |+ opt_field "peer_id" P2p_peer_id.rpc_arg (fun t -> t#peer_id)
        |> seal

      let request_operations path =
        Tezos_rpc.Service.post_service
          ~description:
            "Request the operations of our peers or a specific peer if \
             specified via a query parameter."
          ~input:Data_encoding.empty
          ~query:request_operations_query
          ~output:Data_encoding.empty
          Tezos_rpc.Path.(path / "request_operations")
    end

    let live_blocks =
      Tezos_rpc.Service.get_service
        ~description:
          "List the ancestors of the given block which, if referred to as the \
           branch in an operation header, are recent enough for that operation \
           to be included in the current block."
        ~query:Tezos_rpc.Query.empty
        ~output:Block_hash.Set.encoding
        Tezos_rpc.Path.(live_blocks_path open_root)
  end

  let path = Tezos_rpc.Path.prefix chain_path path

  let make_call0 s ctxt a b q p =
    let s = Tezos_rpc.Service.prefix path s in
    Tezos_rpc.Context.make_call2 s ctxt a b q p

  let make_call1 s ctxt a b c q p =
    let s = Tezos_rpc.Service.prefix path s in
    Tezos_rpc.Context.make_call3 s ctxt a b c q p

  let make_call2 s ctxt a b c d q p =
    let s = Tezos_rpc.Service.prefix path s in
    Tezos_rpc.Context.make_call s ctxt (((((), a), b), c), d) q p

  let hash ctxt =
    let f = make_call0 S.hash ctxt in
    fun ?(chain = `Main) ?(block = `Head 0) () ->
      match block with
      | `Hash (h, 0) -> Lwt.return_ok h
      | _ -> f chain block () ()

  let header ctxt =
    let f = make_call0 S.header ctxt in
    fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

  let raw_header ctxt =
    let f = make_call0 S.raw_header ctxt in
    fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

  let metadata ctxt =
    let f = make_call0 S.metadata ctxt in
    fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

  let metadata_hash ctxt =
    let f = make_call0 S.metadata_hash ctxt in
    fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

  let protocols ctxt =
    let f = make_call0 S.protocols ctxt in
    fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

  let resulting_context_hash ctxt =
    let f = make_call0 S.resulting_context_hash ctxt in
    fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

  module Header = struct
    module S = S.Header

    let shell_header ctxt =
      let f = make_call0 S.shell_header ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

    let protocol_data ctxt =
      let f = make_call0 S.protocol_data ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

    let raw_protocol_data ctxt =
      let f = make_call0 S.raw_protocol_data ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()
  end

  module Operations = struct
    module S = S.Operations

    let operations ctxt ?(force_metadata = false) ?metadata =
      let f = make_call0 S.operations ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) () ->
        f
          chain
          block
          (object
             method force_metadata = force_metadata

             method metadata = metadata
          end)
          ()

    let operations_in_pass ctxt ?(force_metadata = false) ?metadata =
      let f = make_call1 S.operations_in_pass ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) n ->
        f
          chain
          block
          n
          (object
             method force_metadata = force_metadata

             method metadata = metadata
          end)
          ()

    let operation ctxt ?(force_metadata = false) ?metadata =
      let f = make_call2 S.operation ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) n m ->
        f
          chain
          block
          n
          m
          (object
             method force_metadata = force_metadata

             method metadata = metadata
          end)
          ()
  end

  module Operation_hashes = struct
    module S = S.Operation_hashes

    let operation_hashes ctxt =
      let f = make_call0 S.operation_hashes ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

    let operation_hashes_in_pass ctxt =
      let f = make_call1 S.operation_hashes_in_pass ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) n -> f chain block n () ()

    let operation_hash ctxt =
      let f = make_call2 S.operation_hash ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) n m -> f chain block n m () ()
  end

  module Operation_metadata_hashes = struct
    module S = S.Operation_metadata_hashes

    let root ctxt =
      let f = make_call0 S.root ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

    let operation_metadata_hashes ctxt =
      let f = make_call0 S.operation_metadata_hashes ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()

    let operation_metadata_hashes_in_pass ctxt =
      let f = make_call1 S.operation_metadata_hashes_in_pass ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) n -> f chain block n () ()

    let operation_metadata_hash ctxt =
      let f = make_call2 S.operation_metadata_hash ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) n m -> f chain block n m () ()
  end

  module Context = struct
    module S = S.Context

    let read ctxt =
      let f = make_call1 S.read ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) ?depth path ->
        f
          chain
          block
          path
          (object
             method depth = depth
          end)
          ()

    let merkle_tree ctxt =
      let f = make_call1 S.merkle_tree_v2 ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) ?holey path ->
        f
          chain
          block
          path
          (object
             method holey = holey
          end)
          ()
  end

  module Helpers = struct
    module S = S.Helpers

    module Forge = struct
      module S = S.Forge

      let block_header ctxt =
        let f = make_call0 S.block_header ctxt in
        fun ?(chain = `Main) ?(block = `Head 0) header ->
          f chain block () header
    end

    module Preapply = struct
      module S = S.Preapply

      let block ctxt =
        let f = make_call0 S.block ctxt in
        fun ?(chain = `Main)
            ?(block = `Head 0)
            ?(sort = false)
            ?timestamp
            ~protocol_data
            operations ->
          f
            chain
            block
            (object
               method sort_operations = sort

               method timestamp = timestamp
            end)
            {protocol_data; operations}

      let operations ctxt =
        let f = make_call0 S.operations ctxt in
        fun ?(chain = `Main) ?(block = `Head 0) operations ->
          f chain block () operations
    end

    let complete ctxt =
      let f = make_call1 S.complete ctxt in
      fun ?(chain = `Main) ?(block = `Head 0) s -> f chain block s () ()
  end

  let info ctxt ?(force_metadata = false) ?metadata =
    let f = make_call0 S.info ctxt in
    fun ?(chain = `Main) ?(block = `Head 0) () ->
      f
        chain
        block
        (object
           method force_metadata = force_metadata

           method metadata = metadata
        end)
        ()

  module Mempool = struct
    type t = S.Mempool.t = {
      applied : (Operation_hash.t * Next_proto.operation) list;
      refused : (Next_proto.operation * error list) Operation_hash.Map.t;
      outdated : (Next_proto.operation * error list) Operation_hash.Map.t;
      branch_refused : (Next_proto.operation * error list) Operation_hash.Map.t;
      branch_delayed : (Next_proto.operation * error list) Operation_hash.Map.t;
      unprocessed : Next_proto.operation Operation_hash.Map.t;
    }

    type t_with_version = S.Mempool.t_with_version =
      | Version_0 of t
      | Version_1 of t

    let pending_operations_version_dispatcher =
      S.Mempool.pending_operations_version_dispatcher

    let pending_operations ctxt ?(chain = `Main)
        ?(version = S.Mempool.default_pending_operations_version)
        ?(applied = true) ?(branch_delayed = true) ?(branch_refused = true)
        ?(refused = true) ?(outdated = true) ?(validation_passes = []) () =
      let open Lwt_result_syntax in
      let* v =
        Tezos_rpc.Context.make_call1
          (S.Mempool.pending_operations (mempool_path chain_path))
          ctxt
          chain
          (object
             method version = version

             method applied = applied

             method refused = refused

             method outdated = outdated

             method branch_refused = branch_refused

             method branch_delayed = branch_delayed

             method validation_passes = validation_passes
          end)
          ()
      in
      match v with
      | Version_1 pending_operations | Version_0 pending_operations ->
          return pending_operations

    let ban_operation ctxt ?(chain = `Main) op_hash =
      let s = S.Mempool.ban_operation (mempool_path chain_path) in
      Tezos_rpc.Context.make_call1 s ctxt chain () op_hash

    let unban_operation ctxt ?(chain = `Main) op_hash =
      let s = S.Mempool.unban_operation (mempool_path chain_path) in
      Tezos_rpc.Context.make_call1 s ctxt chain () op_hash

    let unban_all_operations ctxt ?(chain = `Main) () =
      let s = S.Mempool.unban_all_operations (mempool_path chain_path) in
      Tezos_rpc.Context.make_call1 s ctxt chain () ()

    let monitor_operations ctxt ?(chain = `Main) ?(applied = true)
        ?(branch_delayed = true) ?(branch_refused = false) ?(refused = false)
        ?(outdated = false) ?(validation_passes = []) () =
      let s = S.Mempool.monitor_operations (mempool_path chain_path) in
      Tezos_rpc.Context.make_streamed_call
        s
        ctxt
        ((), chain)
        (object
           method applied = applied

           method refused = refused

           method outdated = outdated

           method branch_refused = branch_refused

           method branch_delayed = branch_delayed

           method validation_passes = validation_passes
        end)
        ()

    let request_operations ctxt ?(chain = `Main) ?peer_id () =
      let s = S.Mempool.request_operations (mempool_path chain_path) in
      Tezos_rpc.Context.make_call1
        s
        ctxt
        chain
        (object
           method peer_id = peer_id
        end)
        ()
  end

  let live_blocks ctxt =
    let f = make_call0 S.live_blocks ctxt in
    fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () ()
end

module Fake_protocol = struct
  let hash = Protocol_hash.zero

  type block_header_data = unit

  let block_header_data_encoding = Data_encoding.empty

  type block_header_metadata = unit

  let block_header_metadata_encoding = Data_encoding.empty

  type operation_data = unit

  type operation_receipt = unit

  type operation = {
    shell : Operation.shell_header;
    protocol_data : operation_data;
  }

  let operation_data_encoding = Data_encoding.empty

  let operation_receipt_encoding = Data_encoding.empty

  let operation_data_and_receipt_encoding =
    Data_encoding.conv
      (fun ((), ()) -> ())
      (fun () -> ((), ()))
      Data_encoding.empty
end

module Empty = Make (Fake_protocol) (Fake_protocol)

let () =
  Printexc.register_printer (function
      | ( Json_schema.Cannot_parse _ | Json_schema.Dangling_reference _
        | Json_schema.Bad_reference _ | Json_schema.Unexpected _
        | Json_schema.Duplicate_definition _ ) as exn ->
          Some
            (Format.asprintf "%a" (fun ppf -> Json_schema.print_error ppf) exn)
      | _ -> None)

let protocols = Empty.protocols
back to top