diff --git a/CHANGES.rst b/CHANGES.rst index c84a64b2f107e1a41a438a291f441062963eac45..34eddeb09d277111b8b40fa32263eb42dc8e375c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -207,6 +207,11 @@ Node ``"per_block_votes"`` has two properties ``"liquidity_baking_vote"`` and ``"adaptive_inflation_vote"``. A vote is one of ``"on"``, ``"off"``, ``"pass"``. +- Added version ``1`` to RPC ``GET ../blocks//metadata``. It can be used + by calling the RPC with the parameter ``?version=1`` (default version is still + ``0``). Version ``1`` of this RPC and ``GET ../blocks/`` allow the RPC + to output ``attesting rewards`` and ``lost attesting rewards`` kinds in the + JSON result. (MR :gl:`!9253`) Client ------ diff --git a/src/lib_shell/block_directory.ml b/src/lib_shell/block_directory.ml index 5c87e91331f7198db538ee771af403f90cab837f..adabb8e2d063563f70ddb3b04c4d660472731332 100644 --- a/src/lib_shell/block_directory.ml +++ b/src/lib_shell/block_directory.ml @@ -218,8 +218,9 @@ let build_raw_rpc_directory (module Proto : Block_services.PROTO) Next_proto.validation_passes; } in - register0 S.metadata (fun (chain_store, block) () () -> - block_metadata chain_store block) ; + register0 S.metadata (fun (chain_store, block) q () -> + let* res = block_metadata chain_store block in + return (q#version, res)) ; let fail_opt = function None -> Lwt.fail Not_found | Some v -> return v in register0 S.metadata_hash (fun (_, block) () () -> fail_opt (Store.Block.block_metadata_hash block)) ; diff --git a/src/lib_shell_services/block_services.ml b/src/lib_shell_services/block_services.ml index 5dab39ab142c49baac049bb2159a30a21279ff22..898acde03dabfae6c3a65b2fce83dac493514df3 100644 --- a/src/lib_shell_services/block_services.ml +++ b/src/lib_shell_services/block_services.ml @@ -469,6 +469,8 @@ module type PROTO = sig val block_header_metadata_encoding_with_legacy_attestation_name : block_header_metadata Data_encoding.t + val block_header_metadata_encoding : block_header_metadata Data_encoding.t + type operation_data type operation_receipt @@ -558,8 +560,11 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct operation_list_quota : operation_list_quota list; } - let block_metadata_encoding = - def "block_header_metadata" + let block_metadata_encoding ~use_legacy_attestation_name = + def + (if use_legacy_attestation_name then + "block_header_metadata_with_legacy_attestation_name" + else "block_header_metadata") @@ conv (fun { protocol_data; @@ -604,7 +609,9 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct (req "max_operation_list_length" (dynamic_size (list operation_list_quota_encoding)))) - Proto.block_header_metadata_encoding_with_legacy_attestation_name) + (if use_legacy_attestation_name then + Proto.block_header_metadata_encoding_with_legacy_attestation_name + else Proto.block_header_metadata_encoding)) let next_operation_encoding_with_legacy_attestation_name = let open Data_encoding in @@ -737,7 +744,10 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct (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)) + (opt + "metadata" + (dynamic_size + (block_metadata_encoding ~use_legacy_attestation_name))) (req "operations" (list (dynamic_size (list operation_encoding))))) let block_info_encoding = @@ -772,10 +782,38 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct ~output:bytes Tezos_rpc.Path.(path / "header" / "raw") + let block_metadata_encoding = + encoding_versioning + ~encoding_name:"block_metadata_encoding" + ~latest_encoding: + (Version_1, block_metadata_encoding ~use_legacy_attestation_name:false) + ~old_encodings: + [ + ( Version_0, + block_metadata_encoding ~use_legacy_attestation_name:true ); + ] + + let metadata_versions = + mk_version_informations + ~supported:[version_0; version_1] + ~latest:Version_1 + ~default:Version_0 + () + + let metadata_query = + let open Tezos_rpc.Query in + query (fun version -> + object + method version = version + end) + |+ field "version" (version_arg metadata_versions) Version_0 (fun t -> + t#version) + |> seal + let metadata = Tezos_rpc.Service.get_service ~description:"All the metadata associated to the block." - ~query:Tezos_rpc.Query.empty + ~query:metadata_query ~output:block_metadata_encoding Tezos_rpc.Path.(path / "metadata") @@ -1787,9 +1825,20 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct let f = make_call0 S.raw_header ctxt in fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () () - let metadata ctxt = + let metadata ctxt ?(version = Version_0) = + let open Lwt_result_syntax in let f = make_call0 S.metadata ctxt in - fun ?(chain = `Main) ?(block = `Head 0) () -> f chain block () () + fun ?(chain = `Main) ?(block = `Head 0) () -> + let* (Version_0 | Version_1 | Version_2), res = + f + chain + block + (object + method version = version + end) + () + in + return res let metadata_hash ctxt = let f = make_call0 S.metadata_hash ctxt in @@ -2147,6 +2196,8 @@ module Fake_protocol = struct let block_header_metadata_encoding_with_legacy_attestation_name = Data_encoding.empty + let block_header_metadata_encoding = Data_encoding.empty + type operation_data = unit type operation_receipt = unit diff --git a/src/lib_shell_services/block_services.mli b/src/lib_shell_services/block_services.mli index 26867181219082f5b02aeeb3facab317c8d32260..f6764440fdefd4d9677c6d98e2cd95655d0c6c8f 100644 --- a/src/lib_shell_services/block_services.mli +++ b/src/lib_shell_services/block_services.mli @@ -104,6 +104,8 @@ module type PROTO = sig val block_header_metadata_encoding_with_legacy_attestation_name : block_header_metadata Data_encoding.t + val block_header_metadata_encoding : block_header_metadata Data_encoding.t + type operation_data type operation_receipt @@ -220,6 +222,7 @@ module Make (Proto : PROTO) (Next_proto : PROTO) : sig val metadata : #simple -> + ?version:version -> ?chain:chain -> ?block:block -> unit -> @@ -513,7 +516,13 @@ module Make (Proto : PROTO) (Next_proto : PROTO) : sig ([`GET], prefix, prefix, unit, unit, Bytes.t) Tezos_rpc.Service.t val metadata : - ([`GET], prefix, prefix, unit, unit, block_metadata) Tezos_rpc.Service.t + ( [`GET], + prefix, + prefix, + < version : version >, + unit, + version * block_metadata ) + Tezos_rpc.Service.t val metadata_hash : ( [`GET], diff --git a/tezt/lib_tezos/RPC.ml b/tezt/lib_tezos/RPC.ml index 27baa835ad764e4aabd92e232c2abba4a889b3cc..029bf7d5d493ce39509b3af7ca286b0e0a0609f1 100644 --- a/tezt/lib_tezos/RPC.ml +++ b/tezt/lib_tezos/RPC.ml @@ -295,16 +295,21 @@ let get_chain_block ?(chain = "main") ?(block = "head") ?version in make ~query_string GET ["chains"; chain; "blocks"; block] Fun.id +type balance_update = {kind : string; category : string option} + type block_metadata = { protocol : string; next_protocol : string; proposer : string; max_operations_ttl : int; dal_attestation : bool Array.t option; + balance_updates : balance_update list; } -let get_chain_block_metadata ?(chain = "main") ?(block = "head") () = - make GET ["chains"; chain; "blocks"; block; "metadata"] @@ fun json -> +let get_chain_block_metadata ?(chain = "main") ?(block = "head") ?version () = + let query_string = Query_arg.opt "version" Fun.id version in + make ~query_string GET ["chains"; chain; "blocks"; block; "metadata"] + @@ fun json -> let dal_attestation = match JSON.(json |-> "dal_attestation" |> as_string_opt) with | None -> None @@ -325,7 +330,23 @@ let get_chain_block_metadata ?(chain = "main") ?(block = "head") () = | Some proposer -> proposer in let max_operations_ttl = JSON.(json |-> "max_operations_ttl" |> as_int) in - {dal_attestation; protocol; next_protocol; proposer; max_operations_ttl} + let balance_updates = JSON.(json |-> "balance_updates" |> as_list) in + let balance_updates = + List.map + (fun json -> + let kind = JSON.(json |-> "kind" |> as_string) in + let category = JSON.(json |-> "category" |> as_string_opt) in + {kind; category}) + balance_updates + in + { + dal_attestation; + protocol; + next_protocol; + proposer; + max_operations_ttl; + balance_updates; + } let get_chain_block_protocols ?(chain = "main") ?(block = "head") () = make GET ["chains"; chain; "blocks"; block; "protocols"] Fun.id diff --git a/tezt/lib_tezos/RPC.mli b/tezt/lib_tezos/RPC.mli index 30ddf67cce940a51f2d3fe38dabbfed870036ddd..8823788fd303b8c44c8f0f1a3157e4c230206bcb 100644 --- a/tezt/lib_tezos/RPC.mli +++ b/tezt/lib_tezos/RPC.mli @@ -295,6 +295,8 @@ val get_chain_block : unit -> JSON.t t +type balance_update = {kind : string; category : string option} + type block_metadata = { protocol : string; next_protocol : string; @@ -302,6 +304,7 @@ type block_metadata = { max_operations_ttl : int; dal_attestation : bool Array.t option; (** This field is [None] if and only if the [DAL] feature flag is disabled. *) + balance_updates : balance_update list; } (** RPC: [GET /chains//blocks//metadata] @@ -309,7 +312,7 @@ type block_metadata = { [chain] defaults to ["main"]. [block] defaults to ["head"]. *) val get_chain_block_metadata : - ?chain:string -> ?block:string -> unit -> block_metadata t + ?chain:string -> ?block:string -> ?version:string -> unit -> block_metadata t (** RPC: [GET /chains//blocks//protocols] diff --git a/tezt/tests/rpc_versioning_attestation.ml b/tezt/tests/rpc_versioning_attestation.ml index 0a44befa11dd39decd2ae63a08406512ab18e718..b36f21e560d3188d64d44c18b0c55e5300cdf60f 100644 --- a/tezt/tests/rpc_versioning_attestation.ml +++ b/tezt/tests/rpc_versioning_attestation.ml @@ -1124,12 +1124,75 @@ module Block = struct Operation.Anonymous.Double_preattestation_evidence protocol + let test_block_metadata = + register_test + ~title:"Block metadata consensus rewards encoding" + ~additionnal_tags:["metadata"; "block"] + @@ fun protocol -> + let* node, client = Client.init_with_protocol ~protocol `Client () in + Log.info + "Bake 7 blocks to reach the end of a cycle with the metadata containing \ + consensus rewards" ; + let* () = repeat 7 (fun () -> Client.bake_for_and_wait ~node client) in + let get_name use_legacy_attestation_name = + if use_legacy_attestation_name then "endorsing" else "attesting" + in + + Log.info "Check block info RPC" ; + let check ~use_legacy_name:_ json name = + let balance_updates = + JSON.(json |-> "metadata" |-> "balance_updates" |> as_list) + in + let categories = + List.filter_map + (fun balance_update -> + JSON.(balance_update |-> "category" |> as_string_opt)) + balance_updates + in + let check_category name = + if not (List.mem name categories) then + Test.fail + ~__LOC__ + "At least one balance update should have %s kind" + name + in + check_category (sf "%s rewards" name) ; + check_category (sf "lost %s rewards" name) + in + let rpc ~version _ = RPC.get_chain_block ~version () in + let* () = check_rpc_versions ~check ~rpc ~get_name ~data:() client in + + Log.info "Check the block metadata RPC" ; + let check ~use_legacy_name:_ (metadata : RPC.block_metadata) name = + let check_category name = + if + not + (List.mem + name + (List.filter_map + (fun (balance_update : RPC.balance_update) -> + balance_update.category) + metadata.balance_updates)) + then + Test.fail + ~__LOC__ + "At least one balance update should have %s kind" + name + in + check_category (sf "%s rewards" name) ; + check_category (sf "lost %s rewards" name) + in + let rpc ~version data = RPC.get_chain_block_metadata ~version data in + let* () = check_rpc_versions ~check ~rpc ~get_name ~data:() client in + unit + let register ~protocols = test_block_operation_consensus protocols (* There is no test for preconsensus since crafting a block with preconsensus operations in it may be complicated to do. *) ; test_block_operation_double_consensus_evidence protocols ; - test_block_operation_double_preconsensus_evidence protocols + test_block_operation_double_preconsensus_evidence protocols ; + test_block_metadata protocols end let register ~protocols =