From 10602daa67341e6e8afb6a9f7b02032dc8cc4fee Mon Sep 17 00:00:00 2001 From: Gauthier SEBILLE Date: Fri, 26 May 2023 12:26:15 +0200 Subject: [PATCH 1/3] DAC: new endpoint to get binary certificate in DAC CLI --- src/bin_dac_client/command_handlers.ml | 6 ++-- src/lib_dac/RPC_services.ml | 10 ++++++ src/lib_dac/RPC_services.mli | 13 +++++++- src/lib_dac/certificate_repr.ml | 45 ++++++++++++++++++++++++++ src/lib_dac/certificate_repr.mli | 16 +++++++++ src/lib_dac_client/dac_node_client.ml | 7 ++++ src/lib_dac_client/dac_node_client.mli | 7 ++++ src/lib_dac_node/RPC_handlers.ml | 15 +++++++++ src/lib_dac_node/RPC_handlers.mli | 7 ++++ src/lib_dac_node/RPC_server.ml | 12 +++++++ 10 files changed, 135 insertions(+), 3 deletions(-) diff --git a/src/bin_dac_client/command_handlers.ml b/src/bin_dac_client/command_handlers.ml index 09a4f3eeb11f..1bb3c92b44e5 100644 --- a/src/bin_dac_client/command_handlers.ml +++ b/src/bin_dac_client/command_handlers.ml @@ -117,6 +117,8 @@ let get_certificate cctxt root_page_hash = Currently we have only one major DAC API version ([V0]). For this reason, client binary can always default to it. This should be revisited once we add another major version. *) - Dac_node_client.V0.get_certificate cctxt ~root_page_hash + Dac_node_client.V0.get_serialized_certificate cctxt ~root_page_hash in - Option.map_es serialize_certificate certificate_opt + match certificate_opt with + | Some certificate -> return @@ Some (Hex.of_bytes certificate) + | None -> return None diff --git a/src/lib_dac/RPC_services.ml b/src/lib_dac/RPC_services.ml index 7fe6eb452b30..13e12a531b1e 100644 --- a/src/lib_dac/RPC_services.ml +++ b/src/lib_dac/RPC_services.ml @@ -89,6 +89,16 @@ module V0 = struct ~output:(Data_encoding.option Certificate_repr.encoding) Tezos_rpc.Path.(v0_prefix / "certificates" /: Dac_plugin.raw_hash_rpc_arg) + let get_serialized_certificate = + Tezos_rpc.Service.get_service + ~description: + "Retrieve the Dac certificate encoded in binary associated with the \ + given root page hash" + ~query:Tezos_rpc.Query.empty + ~output:(Data_encoding.option Data_encoding.bytes) + Tezos_rpc.Path.( + v0_prefix / "serialized_certificates" /: Dac_plugin.raw_hash_rpc_arg) + let get_missing_page = Tezos_rpc.Service.get_service ~description: diff --git a/src/lib_dac/RPC_services.mli b/src/lib_dac/RPC_services.mli index e10d84d47f16..f1f2851db09e 100644 --- a/src/lib_dac/RPC_services.mli +++ b/src/lib_dac/RPC_services.mli @@ -1,7 +1,7 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2023 Marigold *) +(* Copyright (c) 2023 Marigold *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -75,6 +75,17 @@ module V0 : sig Certificate_repr.t option ) Tezos_rpc.Service.service + (** "GET v0/serialized_certificate" endpoint returns the DAC certificate for the + provided [root_page_hash] encoded in Binary. *) + val get_serialized_certificate : + ( [`GET], + unit, + unit * Dac_plugin.raw_hash, + unit, + unit, + Bytes.t option ) + Tezos_rpc.Service.service + (** "GET v0/missing_page/[page_hash]" Observer fetches the missing page from a Coordinator node. The missing page is then saved to a page store before returning the page as a response. *) diff --git a/src/lib_dac/certificate_repr.ml b/src/lib_dac/certificate_repr.ml index 22367cf1ea2b..bb1c7ca5ba83 100644 --- a/src/lib_dac/certificate_repr.ml +++ b/src/lib_dac/certificate_repr.ml @@ -74,6 +74,51 @@ module V0 = struct (* Equivalent to Bitset.diff expected_witnesses witnesses. *) let missing_witnesses = Z.logand expected_witnesses (Z.lognot witnesses) in Z.(equal missing_witnesses zero) + + module Protocol_dependant = struct + type serialized_certificate = { + root_hash : Dac_plugin.hash; + aggregate_signature : Tezos_crypto.Aggregate_signature.signature; + witnesses : Z.t; + } + + let certificate_client_encoding ((module Plugin) : Dac_plugin.t) = + let untagged = + Data_encoding.( + conv + (fun {root_hash; aggregate_signature; witnesses} -> + (root_hash, aggregate_signature, witnesses)) + (fun (root_hash, aggregate_signature, witnesses) -> + {root_hash; aggregate_signature; witnesses}) + (obj3 + (req "root_hash" Plugin.encoding) + (req + "aggregate_signature" + Tezos_crypto.Aggregate_signature.encoding) + (req "witnesses" z))) + in + Data_encoding.( + union + ~tag_size:`Uint8 + [ + case + ~title:"certificate_V0" + (Tag 0) + untagged + (fun certificate -> Some certificate) + (fun certificate -> certificate); + ]) + + let serialize_certificate dac_plugin serialized_certificate = + let bytes_as_result = + Data_encoding.Binary.to_bytes + (certificate_client_encoding dac_plugin) + serialized_certificate + in + match bytes_as_result with + | Ok serialized_certificate -> Some (Hex.of_bytes serialized_certificate) + | Error _ -> None + end end type t = V0 of V0.t diff --git a/src/lib_dac/certificate_repr.mli b/src/lib_dac/certificate_repr.mli index 7793560981ec..d8a35aa031d4 100644 --- a/src/lib_dac/certificate_repr.mli +++ b/src/lib_dac/certificate_repr.mli @@ -42,6 +42,22 @@ module V0 : sig Tezos_crypto.Aggregate_signature.signature -> Z.t -> t + + module Protocol_dependant : sig + type serialized_certificate = { + root_hash : Dac_plugin.hash; + aggregate_signature : Tezos_crypto.Aggregate_signature.signature; + witnesses : Z.t; + } + + val certificate_client_encoding : + Dac_plugin.t -> serialized_certificate Data_encoding.t + + val serialize_certificate : + Dac_plugin.t -> + serialized_certificate -> + Tezos_base.TzPervasives.Hex.t option + end end type t = V0 of V0.t diff --git a/src/lib_dac_client/dac_node_client.ml b/src/lib_dac_client/dac_node_client.ml index d1dfdd65d756..b678bb3256cf 100644 --- a/src/lib_dac_client/dac_node_client.ml +++ b/src/lib_dac_client/dac_node_client.ml @@ -78,6 +78,13 @@ module V0 = struct () () + let get_serialized_certificate (cctxt : #cctxt) ~root_page_hash = + cctxt#call_service + RPC_services.V0.get_serialized_certificate + ((), root_page_hash) + () + () + let monitor_certificate (cctxt : #cctxt) ~root_hash = Monitor_services.V0.certificate cctxt root_hash diff --git a/src/lib_dac_client/dac_node_client.mli b/src/lib_dac_client/dac_node_client.mli index 9cf7a90c58f9..147fa9d6524d 100644 --- a/src/lib_dac_client/dac_node_client.mli +++ b/src/lib_dac_client/dac_node_client.mli @@ -78,6 +78,13 @@ module V0 : sig root_page_hash:Dac_plugin.raw_hash -> Certificate_repr.t option tzresult Lwt.t + (** [get_serialized_certificate cctxt ~root_page_hash] fetches the DAC certificate for + the provided [root_page_hash]. *) + val get_serialized_certificate : + #cctxt -> + root_page_hash:Dac_plugin.raw_hash -> + Bytes.t option tzresult Lwt.t + (** [monitor_certificate cctxt ~root_hash] returns a stream and a stopper for monitoring certificate updates for [root_hash]. *) val monitor_certificate : diff --git a/src/lib_dac_node/RPC_handlers.ml b/src/lib_dac_node/RPC_handlers.ml index db36003baa9f..97be02c60958 100644 --- a/src/lib_dac_node/RPC_handlers.ml +++ b/src/lib_dac_node/RPC_handlers.ml @@ -170,6 +170,21 @@ module V0 = struct V0 (V0.make raw_root_hash aggregate_signature witnesses))) value_opt + let handle_get_serialized_certificate dac_plugin node_store raw_root_hash = + let open Lwt_result_syntax in + let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in + let+ value_opt = Store.Certificate_store.find node_store root_hash in + match value_opt with + | Some Store.{aggregate_signature; witnesses} -> + let serialized_certificate = + Certificate_repr.V0.Protocol_dependant.serialize_certificate + dac_plugin + Certificate_repr.V0.Protocol_dependant. + {root_hash; aggregate_signature; witnesses} + in + Option.filter_map Hex.to_bytes serialized_certificate + | None -> None + module Coordinator = struct let handle_post_preimage dac_plugin page_store hash_streamer payload = let open Lwt_result_syntax in diff --git a/src/lib_dac_node/RPC_handlers.mli b/src/lib_dac_node/RPC_handlers.mli index ece545af4db8..2dd9ab8550fe 100644 --- a/src/lib_dac_node/RPC_handlers.mli +++ b/src/lib_dac_node/RPC_handlers.mli @@ -82,6 +82,13 @@ module V0 : sig Dac_plugin.raw_hash -> (Certificate_repr.t option, tztrace) result Lwt.t + (** [handle_get_serialized_certificate] is a handler for "GET v0/certificate". *) + val handle_get_serialized_certificate : + Dac_plugin.t -> + [> `Read] Store.Irmin_store.t -> + Dac_plugin.raw_hash -> + (Bytes.t option, tztrace) result Lwt.t + (** [Coordinator] encapsulates, Coordinator's mode specific handlers of [V0] API. *) module Coordinator : sig diff --git a/src/lib_dac_node/RPC_server.ml b/src/lib_dac_node/RPC_server.ml index 8361e24d3699..6aad5aef19c5 100644 --- a/src/lib_dac_node/RPC_server.ml +++ b/src/lib_dac_node/RPC_server.ml @@ -95,6 +95,16 @@ module V0 = struct (fun root_hash () () -> RPC_handlers.V0.handle_get_certificate dac_plugin node_store root_hash) + let register_get_serialized_certificate node_store dac_plugin = + add_service + Tezos_rpc.Directory.register1 + RPC_services.V0.get_serialized_certificate + (fun root_hash () () -> + RPC_handlers.V0.handle_get_serialized_certificate + dac_plugin + node_store + root_hash) + module Coordinator = struct let register_monitor_certificate dac_plugin ro_node_store certificate_streamers committee_members dir = @@ -161,6 +171,7 @@ module V0 = struct rw_store page_store |> register_get_certificate rw_store dac_plugin + |> register_get_serialized_certificate rw_store dac_plugin end module Committee_member = struct @@ -229,6 +240,7 @@ module V0 = struct rw_store page_store |> register_get_certificate rw_store dac_plugin + |> register_get_serialized_certificate rw_store dac_plugin end end -- GitLab From 4aba6425bac87c5f6755b16bff72a00dda5c1f0f Mon Sep 17 00:00:00 2001 From: Gauthier SEBILLE Date: Fri, 26 May 2023 15:19:01 +0200 Subject: [PATCH 2/3] DAC: return binary certif in wait_for_certificate + cleanup DAC: add docstring DAC: docstring + hide type DAC: rework types of API and encoding DAC: rework following review DAC: add unit test on new endpoint DAC: following review DAC: fix typoe --- src/bin_dac_client/command_handlers.ml | 57 +++--------- src/lib_dac/RPC_services.ml | 5 +- src/lib_dac/RPC_services.mli | 7 +- src/lib_dac/certificate_repr.ml | 19 ++-- src/lib_dac/certificate_repr.mli | 22 ++--- src/lib_dac_client/dac_node_client.mli | 6 +- src/lib_dac_node/RPC_handlers.ml | 12 +-- src/lib_dac_node/RPC_handlers.mli | 4 +- src/lib_dac_node/RPC_server.ml | 2 +- tezt/lib_tezos/dac_rpc.ml | 4 + tezt/lib_tezos/dac_rpc.mli | 5 ++ tezt/tests/dac.ml | 115 ++++++++++++++++++++++++- 12 files changed, 175 insertions(+), 83 deletions(-) diff --git a/src/bin_dac_client/command_handlers.ml b/src/bin_dac_client/command_handlers.ml index 1bb3c92b44e5..232a5e2521f9 100644 --- a/src/bin_dac_client/command_handlers.ml +++ b/src/bin_dac_client/command_handlers.ml @@ -23,47 +23,6 @@ (* *) (*****************************************************************************) -let certificate_client_encoding = - let untagged = - Data_encoding.( - conv - (fun certificate -> - ( Dac_plugin.raw_hash_to_bytes - @@ Certificate_repr.get_root_hash certificate, - Certificate_repr.get_aggregate_signature certificate, - Certificate_repr.get_witnesses certificate )) - (fun (root_hash, aggregate_signature, witnesses) -> - Certificate_repr.( - V0 - (V0.make - (Dac_plugin.raw_hash_of_bytes root_hash) - aggregate_signature - witnesses))) - (obj3 - (req "root_hash" (Fixed.bytes 33)) - (req "aggregate_signature" Tezos_crypto.Aggregate_signature.encoding) - (req "witnesses" z))) - in - Data_encoding.( - union - ~tag_size:`Uint8 - [ - case - ~title:"certificate_V0" - (Tag 0) - untagged - (fun certificate -> Some certificate) - (fun certificate -> certificate); - ]) - -let serialize_certificate certificate = - let as_bytes_res = - Data_encoding.Binary.to_bytes certificate_client_encoding certificate - in - match as_bytes_res with - | Ok as_bytes -> Lwt_result_syntax.return @@ Hex.of_bytes as_bytes - | Error _ -> failwith "Error while serializing the certificate" - (* TODO: https://gitlab.com/tezos/tezos/-/issues/5339 rename PUT v1/preimage into v1/payload, and change name of function accordingly. *) @@ -108,17 +67,23 @@ let wait_for_certificate cctxt root_hash threshold = else go certificate_opt num_witnesses in let* certificate_opt = go None 0 in - Option.map_es serialize_certificate certificate_opt + match certificate_opt with + | Some certificate -> + let+ serialize_certificate_opt = + Dac_node_client.V0.get_serialized_certificate + cctxt + ~root_page_hash:(Certificate_repr.get_root_hash certificate) + in + Option.map Hex.of_string serialize_certificate_opt + | None -> return None let get_certificate cctxt root_page_hash = let open Lwt_result_syntax in - let* certificate_opt = + let+ certificate_opt = (* TODO: https://gitlab.com/tezos/tezos/-/issues/5627 Currently we have only one major DAC API version ([V0]). For this reason, client binary can always default to it. This should be revisited once we add another major version. *) Dac_node_client.V0.get_serialized_certificate cctxt ~root_page_hash in - match certificate_opt with - | Some certificate -> return @@ Some (Hex.of_bytes certificate) - | None -> return None + Option.map Hex.of_string certificate_opt diff --git a/src/lib_dac/RPC_services.ml b/src/lib_dac/RPC_services.ml index 13e12a531b1e..6bff0b63c1d0 100644 --- a/src/lib_dac/RPC_services.ml +++ b/src/lib_dac/RPC_services.ml @@ -93,9 +93,10 @@ module V0 = struct Tezos_rpc.Service.get_service ~description: "Retrieve the Dac certificate encoded in binary associated with the \ - given root page hash" + given root page hash. The contained [root_hash] is compatible with \ + Kernel SDK." ~query:Tezos_rpc.Query.empty - ~output:(Data_encoding.option Data_encoding.bytes) + ~output:Data_encoding.(option (string' Hex)) Tezos_rpc.Path.( v0_prefix / "serialized_certificates" /: Dac_plugin.raw_hash_rpc_arg) diff --git a/src/lib_dac/RPC_services.mli b/src/lib_dac/RPC_services.mli index f1f2851db09e..f25013fa8950 100644 --- a/src/lib_dac/RPC_services.mli +++ b/src/lib_dac/RPC_services.mli @@ -75,15 +75,16 @@ module V0 : sig Certificate_repr.t option ) Tezos_rpc.Service.service - (** "GET v0/serialized_certificate" endpoint returns the DAC certificate for the - provided [root_page_hash] encoded in Binary. *) + (** "GET v0/serialized_certificates" endpoint returns the binary encoded DAC + certificate for the provided [root_page_hash] where contained [root_hash] + used encoding is compatible with the Kernel SDK. *) val get_serialized_certificate : ( [`GET], unit, unit * Dac_plugin.raw_hash, unit, unit, - Bytes.t option ) + String.t option ) Tezos_rpc.Service.service (** "GET v0/missing_page/[page_hash]" Observer fetches the missing page diff --git a/src/lib_dac/certificate_repr.ml b/src/lib_dac/certificate_repr.ml index bb1c7ca5ba83..ba4eee3d1009 100644 --- a/src/lib_dac/certificate_repr.ml +++ b/src/lib_dac/certificate_repr.ml @@ -76,12 +76,18 @@ module V0 = struct Z.(equal missing_witnesses zero) module Protocol_dependant = struct - type serialized_certificate = { + (** Very similar to V0.t but using a [root_hash] related to the + active protocol. *) + type t = { root_hash : Dac_plugin.hash; aggregate_signature : Tezos_crypto.Aggregate_signature.signature; witnesses : Z.t; } + (** This encoding is protocol dependant because the SDK Kernel + needs the provided [root_hash] to be 33 bytes long. + This specific encoding should not be on DAC side, + but this is the easiest/faster way to handle it. *) let certificate_client_encoding ((module Plugin) : Dac_plugin.t) = let untagged = Data_encoding.( @@ -103,21 +109,22 @@ module V0 = struct [ case ~title:"certificate_V0" - (Tag 0) + (Tag version) untagged (fun certificate -> Some certificate) (fun certificate -> certificate); ]) - let serialize_certificate dac_plugin serialized_certificate = + let serialize_certificate dac_plugin ~root_hash ~aggregate_signature + ~witnesses = let bytes_as_result = Data_encoding.Binary.to_bytes (certificate_client_encoding dac_plugin) - serialized_certificate + {root_hash; aggregate_signature; witnesses} in match bytes_as_result with - | Ok serialized_certificate -> Some (Hex.of_bytes serialized_certificate) - | Error _ -> None + | Ok serialized_certificate -> serialized_certificate + | Error _ -> Stdlib.failwith "Error while serializing the certificate" end end diff --git a/src/lib_dac/certificate_repr.mli b/src/lib_dac/certificate_repr.mli index d8a35aa031d4..3cb679dff279 100644 --- a/src/lib_dac/certificate_repr.mli +++ b/src/lib_dac/certificate_repr.mli @@ -43,20 +43,20 @@ module V0 : sig Z.t -> t + (** All the following functions are related to the Protocol. + They are used only by the `command_handlers` module + exposing CLI commands for Kernel SDK. *) module Protocol_dependant : sig - type serialized_certificate = { - root_hash : Dac_plugin.hash; - aggregate_signature : Tezos_crypto.Aggregate_signature.signature; - witnesses : Z.t; - } - - val certificate_client_encoding : - Dac_plugin.t -> serialized_certificate Data_encoding.t - + (** Serialize the provided [Dac_plugin.hash], + [Tezos_crypto.Aggregate_signature.signature] and [Z.t] + with the correct encoding. [Dac_plugin.t] is needed to provide + the correct encoding related to the correct protocol. *) val serialize_certificate : Dac_plugin.t -> - serialized_certificate -> - Tezos_base.TzPervasives.Hex.t option + root_hash:Dac_plugin.hash -> + aggregate_signature:Tezos_crypto.Aggregate_signature.signature -> + witnesses:Z.t -> + Bytes.t end end diff --git a/src/lib_dac_client/dac_node_client.mli b/src/lib_dac_client/dac_node_client.mli index 147fa9d6524d..1410ff23e782 100644 --- a/src/lib_dac_client/dac_node_client.mli +++ b/src/lib_dac_client/dac_node_client.mli @@ -78,12 +78,12 @@ module V0 : sig root_page_hash:Dac_plugin.raw_hash -> Certificate_repr.t option tzresult Lwt.t - (** [get_serialized_certificate cctxt ~root_page_hash] fetches the DAC certificate for - the provided [root_page_hash]. *) + (** [get_serialized_certificate cctxt ~root_page_hash] fetches and serialize + the DAC certificate for the provided [root_page_hash]. *) val get_serialized_certificate : #cctxt -> root_page_hash:Dac_plugin.raw_hash -> - Bytes.t option tzresult Lwt.t + String.t option tzresult Lwt.t (** [monitor_certificate cctxt ~root_hash] returns a stream and a stopper for monitoring certificate updates for [root_hash]. *) diff --git a/src/lib_dac_node/RPC_handlers.ml b/src/lib_dac_node/RPC_handlers.ml index 97be02c60958..2f264b955072 100644 --- a/src/lib_dac_node/RPC_handlers.ml +++ b/src/lib_dac_node/RPC_handlers.ml @@ -162,7 +162,6 @@ module V0 = struct let handle_get_certificate dac_plugin node_store raw_root_hash = let open Lwt_result_syntax in let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in - let+ value_opt = Store.Certificate_store.find node_store root_hash in Option.map (fun Store.{aggregate_signature; witnesses} -> @@ -173,17 +172,18 @@ module V0 = struct let handle_get_serialized_certificate dac_plugin node_store raw_root_hash = let open Lwt_result_syntax in let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in - let+ value_opt = Store.Certificate_store.find node_store root_hash in + let* value_opt = Store.Certificate_store.find node_store root_hash in match value_opt with | Some Store.{aggregate_signature; witnesses} -> let serialized_certificate = Certificate_repr.V0.Protocol_dependant.serialize_certificate dac_plugin - Certificate_repr.V0.Protocol_dependant. - {root_hash; aggregate_signature; witnesses} + ~root_hash + ~aggregate_signature + ~witnesses in - Option.filter_map Hex.to_bytes serialized_certificate - | None -> None + return @@ Some (String.of_bytes serialized_certificate) + | None -> return_none module Coordinator = struct let handle_post_preimage dac_plugin page_store hash_streamer payload = diff --git a/src/lib_dac_node/RPC_handlers.mli b/src/lib_dac_node/RPC_handlers.mli index 2dd9ab8550fe..18d5c170beef 100644 --- a/src/lib_dac_node/RPC_handlers.mli +++ b/src/lib_dac_node/RPC_handlers.mli @@ -82,12 +82,12 @@ module V0 : sig Dac_plugin.raw_hash -> (Certificate_repr.t option, tztrace) result Lwt.t - (** [handle_get_serialized_certificate] is a handler for "GET v0/certificate". *) + (** [handle_get_serialized_certificate] is the handler for "GET v0/serialized_certificates". *) val handle_get_serialized_certificate : Dac_plugin.t -> [> `Read] Store.Irmin_store.t -> Dac_plugin.raw_hash -> - (Bytes.t option, tztrace) result Lwt.t + (String.t option, tztrace) result Lwt.t (** [Coordinator] encapsulates, Coordinator's mode specific handlers of [V0] API. *) diff --git a/src/lib_dac_node/RPC_server.ml b/src/lib_dac_node/RPC_server.ml index 6aad5aef19c5..4247b7b7c3a7 100644 --- a/src/lib_dac_node/RPC_server.ml +++ b/src/lib_dac_node/RPC_server.ml @@ -3,7 +3,7 @@ (* Open Source License *) (* Copyright (c) 2022-2023 Trili Tech *) (* Copyright (c) 2022 Nomadic Labs *) -(* Copyright (c) 2023 Marigold *) +(* Copyright (c) 2023 Marigold *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) diff --git a/tezt/lib_tezos/dac_rpc.ml b/tezt/lib_tezos/dac_rpc.ml index cc747722c8ea..4942401a5c03 100644 --- a/tezt/lib_tezos/dac_rpc.ml +++ b/tezt/lib_tezos/dac_rpc.ml @@ -95,6 +95,10 @@ module V0 = struct json |-> "root_hash" |> as_string, json |-> "version" |> as_int ) + let get_serialized_certificate ~hex_root_hash = + let (`Hex page_hash) = hex_root_hash in + make GET [api_prefix; "serialized_certificates"; page_hash] JSON.as_string + module Coordinator = struct let post_preimage ~payload = let preimage = diff --git a/tezt/lib_tezos/dac_rpc.mli b/tezt/lib_tezos/dac_rpc.mli index 003a714e7ac1..a739b8333317 100644 --- a/tezt/lib_tezos/dac_rpc.mli +++ b/tezt/lib_tezos/dac_rpc.mli @@ -67,6 +67,11 @@ module V0 : sig val get_certificate : hex_root_hash:Hex.t -> (Dac_node.t, int * string * string * int) RPC_core.t + (** [get_serialized_certificate ~hex_root_hash] fetches the DAC certificate for the + provided [hex_root_hash] with SDK kernel compatible [root_hash] encoding. *) + val get_serialized_certificate : + hex_root_hash:Hex.t -> (Dac_node.t, string) RPC_core.t + module Coordinator : sig (** [post_preimage ~payload] sends a [payload] to the DAC [Coordinator] via a POST RPC call to v0/preimage. It returns a hex encoded root page hash, diff --git a/tezt/tests/dac.ml b/tezt/tests/dac.ml index 0ddb92e4d506..081a96eaf9b1 100644 --- a/tezt/tests/dac.ml +++ b/tezt/tests/dac.ml @@ -1513,7 +1513,7 @@ module Full_infrastructure = struct 3. The signature is checked against one constructed manually 4. The signature of the root hash is requested again via the client `get certificate` command, - 5. The returned signature is checked to be equvalent to the + 5. The returned signature is checked to be equivalent to the one previously output by the client. *) (* The test requires at least one committee member *) @@ -1590,7 +1590,7 @@ module Full_infrastructure = struct (* 3. The signature is checked against one constructed manually. *) check_raw_certificate last_certificate_update expected_certificate ; (* 4. The signature of the root hash is requested again via the - client `get certificate` command.v*) + client `get certificate` coommand. *) let* get_certificate_output = Dac_client.get_certificate coordinator_client (`Hex expected_rh) in @@ -1603,11 +1603,112 @@ module Full_infrastructure = struct value `Some (Root_hash _ )`. *) assert false in - (* 5. The returned signature is checked to be equvalent to the + (* 5. The returned signature is checked to be equivalent to the one previously output by the client. *) check_raw_certificate get_certificate last_certificate_update ; return () + (** Verifies that the serialized certificate returned in GET /v0/serialized_certificates + is valid, by veryfing its content. *) + let test_serialized_certificate + Scenarios. + { + coordinator_node; + committee_members_nodes; + observer_nodes; + committee_members; + _; + } = + (* 0. Coordinator node is already running when the this function is + executed by the test + 1. Run committee members and observers + 2. Dac client posts a preimage to coordinator, and waits for all + committee members to sign + 3. The signature is checked against one constructed manually + 4. The signature of the root hash is requested again via the + client `get certificate` command, + 5. The returned signature is checked to be equivalent to the + one previously output by the client. + *) + (* The test requires at least one committee member *) + assert (List.length committee_members > 0) ; + let coordinator_client = Dac_client.create coordinator_node in + let payload, expected_rh = sample_payload "preimage" in + let committee_members_opt = + List.map (fun committee_member -> Some committee_member) committee_members + in + let expected_certificate = + build_raw_certificate + committee_members_opt + (Hex.to_bytes (`Hex expected_rh)) + in + let wait_for_node_subscribed_to_data_streamer () = + wait_for_handle_new_subscription_to_hash_streamer coordinator_node + in + (* Initialize configuration of all nodes. *) + let* _ = + Lwt_list.iter_s + (fun committee_member_node -> + let* _ = Dac_node.init_config committee_member_node in + return ()) + committee_members_nodes + in + let* _ = + Lwt_list.iter_s + (fun observer_node -> + let* _ = Dac_node.init_config observer_node in + return ()) + observer_nodes + in + (* 1. Run committee member and observer nodes. + Because the event resolution loop in the Daemon always resolves + all promises matching an event filter, when a new event is received, + we cannot wait for multiple subscription to the hash streamer, as + events of this kind are indistinguishable one from the other. + Instead, we wait for the subscription of one observer/committe_member + node to be notified before running the next node. *) + let* () = + Lwt_list.iter_s + (fun node -> + let node_is_subscribed = + wait_for_node_subscribed_to_data_streamer () + in + let* () = Dac_node.run ~wait_ready:true node in + let* () = check_liveness_and_readiness node in + node_is_subscribed) + (committee_members_nodes @ observer_nodes) + in + (* 2. Dac client posts a preimage to coordinator, and waits for all + committee members to sign. *) + let* client_sends_payload_output = + Dac_client.send_hex_payload + coordinator_client + (Hex.of_string payload) + ~threshold:(List.length committee_members) + in + let last_certificate_update = + match client_sends_payload_output with + | Root_hash rh -> + Stdlib.failwith @@ "Expected certificate, found root hash" + ^ Hex.show rh + | Certificate c -> c + in + (* 3. The signature is checked against one constructed manually. *) + check_raw_certificate last_certificate_update expected_certificate ; + (* 4. The signature of the root hash is requested again via the + client `get certificate` command. *) + let* get_certificate = + RPC.call + coordinator_node + (Dac_rpc.V0.get_serialized_certificate + ~hex_root_hash:(`Hex expected_rh)) + in + let certif = `Hex get_certificate in + (* 5. The returned signature is checked to be equivalent to the + one previously output by the client. *) + check_raw_certificate certif last_certificate_update ; + return () + (* 1. Observer should fetch missing page from Committee Members when GET /missing_page/{hash} is called. 2. As a side effect, Observer should save fetched page into its page store before @@ -2920,6 +3021,14 @@ let register ~protocols = "test client commands (binary payload from file)" (Full_infrastructure.test_client ~send_payload_from_file:true) protocols ; + scenario_with_full_dac_infrastructure + ~__FILE__ + ~observers:0 + ~committee_size:2 + ~tags:["dac"; "dac_node"] + "test serialized certificate" + Full_infrastructure.test_serialized_certificate + protocols ; Tx_kernel_e2e.test_tx_kernel_e2e_with_dac_observer_synced_with_dac protocols ; Tx_kernel_e2e.test_tx_kernel_e2e_with_dac_observer_missing_pages protocols ; scenario_with_full_dac_infrastructure -- GitLab From 0c4e5a48055f0e1ad7caeb3e57b6873e0e9f2f09 Mon Sep 17 00:00:00 2001 From: Gauthier SEBILLE Date: Mon, 5 Jun 2023 16:39:06 +0200 Subject: [PATCH 3/3] DAC: suggestions from code review --- src/lib_dac/certificate_repr.ml | 8 ++++---- src/lib_dac/certificate_repr.mli | 2 +- src/lib_dac_node/RPC_handlers.ml | 11 ++++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/lib_dac/certificate_repr.ml b/src/lib_dac/certificate_repr.ml index ba4eee3d1009..332294f8ad69 100644 --- a/src/lib_dac/certificate_repr.ml +++ b/src/lib_dac/certificate_repr.ml @@ -88,7 +88,7 @@ module V0 = struct needs the provided [root_hash] to be 33 bytes long. This specific encoding should not be on DAC side, but this is the easiest/faster way to handle it. *) - let certificate_client_encoding ((module Plugin) : Dac_plugin.t) = + let certificate_client_encoding root_hash_encoding = let untagged = Data_encoding.( conv @@ -97,7 +97,7 @@ module V0 = struct (fun (root_hash, aggregate_signature, witnesses) -> {root_hash; aggregate_signature; witnesses}) (obj3 - (req "root_hash" Plugin.encoding) + (req "root_hash" root_hash_encoding) (req "aggregate_signature" Tezos_crypto.Aggregate_signature.encoding) @@ -115,11 +115,11 @@ module V0 = struct (fun certificate -> certificate); ]) - let serialize_certificate dac_plugin ~root_hash ~aggregate_signature + let serialize_certificate root_hash_encoding ~root_hash ~aggregate_signature ~witnesses = let bytes_as_result = Data_encoding.Binary.to_bytes - (certificate_client_encoding dac_plugin) + (certificate_client_encoding root_hash_encoding) {root_hash; aggregate_signature; witnesses} in match bytes_as_result with diff --git a/src/lib_dac/certificate_repr.mli b/src/lib_dac/certificate_repr.mli index 3cb679dff279..36d147f87774 100644 --- a/src/lib_dac/certificate_repr.mli +++ b/src/lib_dac/certificate_repr.mli @@ -52,7 +52,7 @@ module V0 : sig with the correct encoding. [Dac_plugin.t] is needed to provide the correct encoding related to the correct protocol. *) val serialize_certificate : - Dac_plugin.t -> + Dac_plugin.hash Data_encoding.t -> root_hash:Dac_plugin.hash -> aggregate_signature:Tezos_crypto.Aggregate_signature.signature -> witnesses:Z.t -> diff --git a/src/lib_dac_node/RPC_handlers.ml b/src/lib_dac_node/RPC_handlers.ml index 2f264b955072..617253b8935a 100644 --- a/src/lib_dac_node/RPC_handlers.ml +++ b/src/lib_dac_node/RPC_handlers.ml @@ -171,19 +171,20 @@ module V0 = struct let handle_get_serialized_certificate dac_plugin node_store raw_root_hash = let open Lwt_result_syntax in + let ((module Plugin) : Dac_plugin.t) = dac_plugin in let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in let* value_opt = Store.Certificate_store.find node_store root_hash in - match value_opt with - | Some Store.{aggregate_signature; witnesses} -> + Option.map_es + (fun Store.{aggregate_signature; witnesses} -> let serialized_certificate = Certificate_repr.V0.Protocol_dependant.serialize_certificate - dac_plugin + Plugin.encoding ~root_hash ~aggregate_signature ~witnesses in - return @@ Some (String.of_bytes serialized_certificate) - | None -> return_none + return @@ String.of_bytes serialized_certificate) + value_opt module Coordinator = struct let handle_post_preimage dac_plugin page_store hash_streamer payload = -- GitLab