diff --git a/src/lib_dac/RPC_services.ml b/src/lib_dac/RPC_services.ml index a8d5821dfcedf5ede1e068fe33f754c1a10093aa..0a2bf7920e23c1e70a068f25b5d5ac302f0ba3b8 100644 --- a/src/lib_dac/RPC_services.ml +++ b/src/lib_dac/RPC_services.ml @@ -24,6 +24,25 @@ (* *) (*****************************************************************************) +module Api = struct + type version = V0 | V1 + + let version_rpc_arg = + let construct = function V0 -> "v0" | V1 -> "v1" in + let destruct version = + match version with + | "v0" -> Ok V0 + | "v1" -> Ok V1 + | invalid_version -> Error invalid_version + in + Tezos_rpc.Arg.make + ~descr:"API version" + ~name:"api_version" + ~destruct + ~construct + () +end + (* A variant of [Sc_rollup_reveal_hash.encoding] that prefers hex encoding over b58check encoding for JSON. *) let store_preimage_request_encoding = @@ -51,7 +70,7 @@ let post_store_preimage = ~query:Tezos_rpc.Query.empty ~input:store_preimage_request_encoding ~output:store_preimage_response_encoding - Tezos_rpc.Path.(open_root / "store_preimage") + Tezos_rpc.Path.(open_root /: Api.version_rpc_arg / "store_preimage") (* DAC/FIXME: https://gitlab.com/tezos/tezos/-/issues/4263 remove this endpoint once end-to-end tests are in place. *) @@ -60,14 +79,16 @@ let get_verify_signature = ~description:"Verify signature of an external message to inject in L1" ~query:external_message_query ~output:Data_encoding.bool - Tezos_rpc.Path.(open_root / "verify_signature") + Tezos_rpc.Path.(open_root /: Api.version_rpc_arg / "verify_signature") let get_preimage = Tezos_rpc.Service.get_service ~description:"Retrieves a page by its page hash and returns its contents" ~query:Tezos_rpc.Query.empty ~output:Data_encoding.bytes - Tezos_rpc.Path.(open_root / "preimage" /: Dac_plugin.raw_hash_rpc_arg) + Tezos_rpc.Path.( + open_root /: Api.version_rpc_arg / "preimage" + /: Dac_plugin.raw_hash_rpc_arg) let put_dac_member_signature = Tezos_rpc.Service.put_service @@ -76,7 +97,7 @@ let put_dac_member_signature = ~query:Tezos_rpc.Query.empty ~input:Signature_repr.encoding ~output:Data_encoding.empty - Tezos_rpc.Path.(open_root / "dac_member_signature") + Tezos_rpc.Path.(open_root /: Api.version_rpc_arg / "dac_member_signature") let get_certificate = Tezos_rpc.Service.get_service @@ -84,7 +105,9 @@ let get_certificate = "Retrieve the Dac certificate associated with the given root page hash" ~query:Tezos_rpc.Query.empty ~output:(Data_encoding.option Certificate_repr.encoding) - Tezos_rpc.Path.(open_root / "certificates" /: Dac_plugin.raw_hash_rpc_arg) + Tezos_rpc.Path.( + open_root /: Api.version_rpc_arg / "certificates" + /: Dac_plugin.raw_hash_rpc_arg) let get_missing_page = Tezos_rpc.Service.get_service @@ -95,7 +118,9 @@ let get_missing_page = Observer mode." ~query:Tezos_rpc.Query.empty ~output:Data_encoding.bytes - Tezos_rpc.Path.(open_root / "missing_page" /: Dac_plugin.raw_hash_rpc_arg) + Tezos_rpc.Path.( + open_root /: Api.version_rpc_arg / "missing_page" + /: Dac_plugin.raw_hash_rpc_arg) (* TODO: https://gitlab.com/tezos/tezos/-/issues/4935 Coordinator's "POST /preimage" endpoint should in addition to root page hash @@ -115,5 +140,5 @@ module Coordinator = struct ~query:Tezos_rpc.Query.empty ~input:Data_encoding.bytes ~output:Dac_plugin.raw_hash_encoding - Tezos_rpc.Path.(open_root / "preimage") + Tezos_rpc.Path.(open_root /: Api.version_rpc_arg / "preimage") end diff --git a/src/lib_dac/RPC_services.mli b/src/lib_dac/RPC_services.mli index 0c7a8eb445052eead5fe0017b7ffbee62c37b46d..e1a5df0571c5b9613e7d024fbc824b8299b9e5d2 100644 --- a/src/lib_dac/RPC_services.mli +++ b/src/lib_dac/RPC_services.mli @@ -1,72 +1,125 @@ -(** POST dac/store_preimage to post a payload using a given [pagination_scheme]. - It returns the base58 encoded root page hash - and the raw bytes. *) +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2023 Trili Tech, *) +(* 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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +(** [Api] module is used for versioning DAC API. *) +module Api : sig + (** [version] type is used to version DAC API. *) + type version = + | V0 + (** [V0] will get deprecated soon, once we refactor old legacy tests. + Do not use! *) + | V1 + (** [V1] is a version that corresponds to the first public release of + the DAC API. *) + + (** [version_rpc_arg] is a API version argument for the RPCs.*) + val version_rpc_arg : version Tezos_rpc.Arg.arg +end + +(** "POST [api_version]/store_preimage" serializes a payload using a given + [pagination_scheme]. It returns the base58 encoded root page hash + and the raw bytes. *) val post_store_preimage : ( [`POST], unit, - unit, + unit * Api.version, unit, Bytes.t * Pagination_scheme.t, Dac_plugin.raw_hash * Bytes.t ) Tezos_rpc.Service.service -(** GET dac/verify_signature endpoint requests the DAL node to verify - the signature of the external message [external_message]. The DAC committee - of the DAL node must be the same that was used to produce the - [external_message]. *) +(** "GET [api_version]/verify_signature" endpoint requests the DAL node to verify + the signature of the external message [external_message]. The DAC committee + of the DAL node must be the same that was used to produce the + [external_message]. *) val get_verify_signature : - ([`GET], unit, unit, string option, unit, bool) Tezos_rpc.Service.service + ( [`GET], + unit, + unit * Api.version, + string option, + unit, + bool ) + Tezos_rpc.Service.service -(** GET dac/preimage requests the preimage of hash, consisting of a +(** "GET [api_version]/preimage" requests the preimage of hash, consisting of a single page, from cctxt. When the request succeeds, the raw page will be returned as a sequence of bytes. *) val get_preimage : ( [`GET], unit, - unit * Dac_plugin.raw_hash, + (unit * Api.version) * Dac_plugin.raw_hash, unit, unit, Bytes.t ) Tezos_rpc.Service.service -(** PUT dac/member_signature endpoint stores the [signature] - generated from signing [hex_root_hash] by [dac_member_pkh]. *) +(** "PUT [api_version]/member_signature" endpoint stores the [signature] + generated from signing [hex_root_hash] by [dac_member_pkh]. *) val put_dac_member_signature : - ([`PUT], unit, unit, unit, Signature_repr.t, unit) Tezos_rpc.Service.service + ( [`PUT], + unit, + unit * Api.version, + unit, + Signature_repr.t, + unit ) + Tezos_rpc.Service.service -(** GET dac/certificate endpoint returns the DAC certificate for the - provided [root_page_hash]. *) +(** "GET [api_version]/certificate" endpoint returns the DAC certificate for the + provided [root_page_hash]. *) val get_certificate : ( [`GET], unit, - unit * Dac_plugin.raw_hash, + (unit * Api.version) * Dac_plugin.raw_hash, unit, unit, Certificate_repr.t option ) Tezos_rpc.Service.service -(** GET dac/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. *) +(** "GET [api_version]/missing_page/[page_hash]" 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. *) val get_missing_page : ( [`GET], unit, - unit * Dac_plugin.raw_hash, + (unit * Api.version) * Dac_plugin.raw_hash, unit, unit, Bytes.t ) Tezos_rpc.Service.service module Coordinator : sig - (** POST dac/preimage sends a [payload] to the DAC - [Coordinator]. It returns a hex encoded root page hash, - produced by [Merkle_tree_V0] pagination scheme. - On the backend side it also pushes root page hash of the preimage to all - the subscribed DAC Members and Observers. *) + (** "POST [api_version]/preimage" sends a [payload] to the DAC + [Coordinator]. It returns a hex encoded root page hash, + produced by [Merkle_tree_V0] pagination scheme. + On the backend side it also pushes root page hash of the preimage to all + the subscribed DAC Members and Observers. *) val post_preimage : ( [`POST], unit, - unit, + unit * Api.version, unit, Bytes.t, Dac_plugin.raw_hash ) diff --git a/src/lib_dac_client/dac_node_client.ml b/src/lib_dac_client/dac_node_client.ml index 7ecc19953eef871f7f4b686e672884fd0409c756..ff502537e190a9d02029c72c0963098d19da8558 100644 --- a/src/lib_dac_client/dac_node_client.ml +++ b/src/lib_dac_client/dac_node_client.ml @@ -44,32 +44,89 @@ let make_unix_cctxt ~scheme ~host ~port = in new unix_cctxt ~rpc_config -(* FIXME: https://gitlab.com/tezos/tezos/-/issues/4895 - If the preimage was generated using a different plugin, the computation of - the hash might fail. In practice it would be better to retrieve the - hash of the protocol that the coordinator was using when the page hash - was computed. -*) -let get_preimage (cctxt : #cctxt) ~page_hash = - cctxt#call_service RPC_services.get_preimage ((), page_hash) () () - -let post_store_preimage (cctxt : #cctxt) ~payload ~pagination_scheme = - cctxt#call_service - RPC_services.post_store_preimage - () - () - (payload, pagination_scheme) - -let get_verify_signature (cctxt : #cctxt) ~external_message = - cctxt#call_service RPC_services.get_verify_signature () external_message () - -let put_dac_member_signature (cctxt : #cctxt) ~signature = - cctxt#call_service RPC_services.put_dac_member_signature () () signature - -let get_certificate (cctxt : #cctxt) ~root_page_hash = - cctxt#call_service RPC_services.get_certificate ((), root_page_hash) () () - -module Coordinator = struct - let post_preimage (cctxt : #cctxt) ~payload = - cctxt#call_service RPC_services.Coordinator.post_preimage () () payload +module Shared = struct + (* FIXME: https://gitlab.com/tezos/tezos/-/issues/4895 + If the preimage was generated using a different plugin, the computation of + the hash might fail. In practice it would be better to retrieve the + hash of the protocol that the coordinator was using when the page hash + was computed. + *) + let get_preimage (cctxt : #cctxt) api_version ~page_hash = + cctxt#call_service + RPC_services.get_preimage + (((), api_version), page_hash) + () + () + + let post_store_preimage (cctxt : #cctxt) ~payload ~pagination_scheme + api_version = + cctxt#call_service + RPC_services.post_store_preimage + ((), api_version) + () + (payload, pagination_scheme) + + let get_verify_signature (cctxt : #cctxt) ~external_message api_version = + cctxt#call_service + RPC_services.get_verify_signature + ((), api_version) + external_message + () + + let put_dac_member_signature (cctxt : #cctxt) ~signature api_version = + cctxt#call_service + RPC_services.put_dac_member_signature + ((), api_version) + () + signature + + let get_certificate (cctxt : #cctxt) ~root_page_hash api_version = + cctxt#call_service + RPC_services.get_certificate + (((), api_version), root_page_hash) + () + () + + module Coordinator = struct + let post_preimage (cctxt : #cctxt) ~payload api_version = + cctxt#call_service + RPC_services.Coordinator.post_preimage + ((), api_version) + () + payload + end +end + +module V0 = struct + let get_preimage cctxt = Shared.get_preimage cctxt RPC_services.Api.V0 + + let put_dac_member_signature cctxt = + Shared.put_dac_member_signature cctxt RPC_services.Api.V0 + + let get_certificate cctxt = Shared.get_certificate cctxt RPC_services.Api.V0 + + module Coordinator = struct + let post_preimage cctxt = + Shared.Coordinator.post_preimage cctxt RPC_services.Api.V0 + end + + let post_store_preimage cctxt = + Shared.post_store_preimage cctxt RPC_services.Api.V0 + + let get_verify_signature cctxt = + Shared.get_verify_signature cctxt RPC_services.Api.V0 +end + +module V1 = struct + let get_preimage cctxt = Shared.get_preimage cctxt RPC_services.Api.V1 + + let put_dac_member_signature cctxt = + Shared.put_dac_member_signature cctxt RPC_services.Api.V1 + + let get_certificate cctxt = Shared.get_certificate cctxt RPC_services.Api.V1 + + module Coordinator = struct + let post_preimage cctxt = + Shared.Coordinator.post_preimage cctxt RPC_services.Api.V1 + end end diff --git a/src/lib_dac_client/dac_node_client.mli b/src/lib_dac_client/dac_node_client.mli index ee2665283bfb882d9420c6a74e524812e9466aa4..20716e864cec5e899d3b3604a0647064c20a9dfe 100644 --- a/src/lib_dac_client/dac_node_client.mli +++ b/src/lib_dac_client/dac_node_client.mli @@ -39,48 +39,83 @@ class unix_cctxt : the client configuration parameters. *) val make_unix_cctxt : scheme:string -> host:string -> port:int -> cctxt -(** [get_preimage cctxt ~hash] requests the preimage of hash, consisting of a - single page, from cctxt. When the request succeeds, the raw page will be - returned as a sequence of bytes. *) -val get_preimage : - #cctxt -> page_hash:Dac_plugin.raw_hash -> bytes tzresult Lwt.t +(** [V0] is a module that provides a client specification for interacting with + [Tezos_lib_dac.Rpc_services.Api.V0] API. *) +module V0 : sig + (** [get_preimage cctxt ~hash] requests the preimage of hash, consisting of a + single page, from cctxt. When the request succeeds, the raw page will be + returned as a sequence of bytes. *) + val get_preimage : + #cctxt -> page_hash:Dac_plugin.raw_hash -> bytes tzresult Lwt.t -(** [post_store_preimage cctxt ~payload ~pagination_scheme] posts a [payload] to dac/store_preimage - using a given [pagination_scheme]. It returns the base58 encoded root page hash - and the raw bytes. *) -val post_store_preimage : - #cctxt -> - payload:bytes -> - pagination_scheme:Pagination_scheme.t -> - (Dac_plugin.raw_hash * bytes) tzresult Lwt.t + (** [post_store_preimage cctxt ~payload ~pagination_scheme] posts + a [payload] to dac/store_preimage using a given [pagination_scheme]. + It returns the base58 encoded root page hash and the raw bytes. *) + val post_store_preimage : + #cctxt -> + payload:bytes -> + pagination_scheme:Pagination_scheme.t -> + (Dac_plugin.raw_hash * bytes) tzresult Lwt.t -(** [get_verify_signature cctxt ~external_message] requests the DAL node to verify - the signature of the external message [external_message] via - the plugin/dac/verify_signature endpoint. The DAC committee - of the DAL node must be the same that was used to produce the - [external_message]. *) -val get_verify_signature : - #cctxt -> external_message:string option -> bool tzresult Lwt.t + (** [get_verify_signature cctxt ~external_message] requests the + DAC node to verify the signature of the external message + [external_message] via the plugin/dac/verify_signature endpoint. + The DAC committee of the DAL node must be the same that was used to + produce the [external_message]. *) + val get_verify_signature : + #cctxt -> external_message:string option -> bool tzresult Lwt.t -(** [put_dac_member_signature cctxt ~signature:Signature_repr.t] - stores the [signature] generated from signing [hex_root_hash] by - [dac_member_pkh]. *) -val put_dac_member_signature : - #cctxt -> signature:Signature_repr.t -> unit tzresult Lwt.t + (** [put_dac_member_signature cctxt ~signature:Signature_repr.t] stores the + [signature] generated from signing [hex_root_hash] by [dac_member_pkh]. *) + val put_dac_member_signature : + #cctxt -> signature:Signature_repr.t -> unit tzresult Lwt.t -(** [get_certificate cctxt ~root_page_hash] fetches the DAC certificate for the - provided [root_page_hash]. *) -val get_certificate : - #cctxt -> - root_page_hash:Dac_plugin.raw_hash -> - Certificate_repr.t option tzresult Lwt.t + (** [get_certificate cctxt ~root_page_hash] fetches the DAC certificate + for the provided [root_page_hash]. *) + val get_certificate : + #cctxt -> + root_page_hash:Dac_plugin.raw_hash -> + Certificate_repr.t option tzresult Lwt.t -module Coordinator : sig - (** [post_preimage cctxt ~payload] sends a [payload] to the DAC - [Coordinator] via a POST RPC call to dac/preimage. It returns a hex - encoded root page hash, produced by [Merkle_tree_V0] pagination scheme. - On the backend side it also pushes root page hash of the preimage to all - the subscribed DAC Members and Observers. *) - val post_preimage : - #cctxt -> payload:bytes -> Dac_plugin.raw_hash tzresult Lwt.t + module Coordinator : sig + (** [post_preimage cctxt ~payload] sends a [payload] to the DAC + [Coordinator] via a POST RPC call to dac/preimage. It returns a hex + encoded root page hash, produced by [Merkle_tree_V0] pagination scheme. + On the backend side it also pushes root page hash of the preimage to all + the subscribed DAC Members and Observers. *) + val post_preimage : + #cctxt -> payload:bytes -> Dac_plugin.raw_hash tzresult Lwt.t + end +end + +(** [V1] is a module that provides a client specification for interacting with + [Tezos_lib_dac.Rpc_services.Api.V1] API. *) +module V1 : sig + (** [get_preimage cctxt ~hash] requests the preimage of hash, consisting of a + single page, from cctxt. When the request succeeds, the raw page will be + returned as a sequence of bytes. *) + val get_preimage : + #cctxt -> page_hash:Dac_plugin.raw_hash -> bytes tzresult Lwt.t + + (** [put_dac_member_signature cctxt ~signature:Signature_repr.t] stores the + [signature] generated from signing [hex_root_hash] by [dac_member_pkh]. *) + val put_dac_member_signature : + #cctxt -> signature:Signature_repr.t -> unit tzresult Lwt.t + + (** [get_certificate cctxt ~root_page_hash] fetches the DAC certificate + for the provided [root_page_hash]. *) + val get_certificate : + #cctxt -> + root_page_hash:Dac_plugin.raw_hash -> + Certificate_repr.t option tzresult Lwt.t + + module Coordinator : sig + (** [post_preimage cctxt ~payload] sends a [payload] to the DAC + [Coordinator] via a POST RPC call to dac/preimage. It returns a hex + encoded root page hash, produced by [Merkle_tree_V0] pagination scheme. + On the backend side it also pushes root page hash of the preimage to all + the subscribed DAC Members and Observers. *) + val post_preimage : + #cctxt -> payload:bytes -> Dac_plugin.raw_hash tzresult Lwt.t + end end diff --git a/src/lib_dac_node/RPC_server.ml b/src/lib_dac_node/RPC_server.ml index 2e9f1602afd6ff803e4b707acb366475f24b2e7a..8b8ed37c3043435b4c31f8a6eba827c8c705c783 100644 --- a/src/lib_dac_node/RPC_server.ml +++ b/src/lib_dac_node/RPC_server.ml @@ -58,234 +58,300 @@ let add_service registerer service handler directory = registerer directory service handler let handle_post_store_preimage dac_plugin cctxt dac_sk_uris page_store - hash_streamer (data, pagination_scheme) = - let open Lwt_result_syntax in - let open Pages_encoding in - let* root_hash = - match pagination_scheme with - | Pagination_scheme.Merkle_tree_V0 -> - (* FIXME: https://gitlab.com/tezos/tezos/-/issues/4897 - Once new "PUT /preimage" endpoint is implemented, pushing - a new root hash to the data streamer should be moved there. - Tezt for testing streaming of root hashes should also use - the new endpoint. *) - let* root_hash = - Merkle_tree.V0.Filesystem.serialize_payload - dac_plugin - ~page_store - data - in - let () = - Data_streamer.publish hash_streamer (Dac_plugin.hash_to_raw root_hash) - in - let*! () = - Event.emit_root_hash_pushed_to_data_streamer dac_plugin root_hash - in - return root_hash - | Pagination_scheme.Hash_chain_V0 -> - Hash_chain.V0.serialize_payload + hash_streamer (data, pagination_scheme) api_version = + match api_version with + | RPC_services.Api.V0 -> ( + let open Lwt_result_syntax in + let open Pages_encoding in + let* root_hash = + match pagination_scheme with + | Pagination_scheme.Merkle_tree_V0 -> + (* FIXME: https://gitlab.com/tezos/tezos/-/issues/4897 + Once new "PUT /preimage" endpoint is implemented, pushing + a new root hash to the data streamer should be moved there. + Tezt for testing streaming of root hashes should also use + the new endpoint. *) + let* root_hash = + Merkle_tree.V0.Filesystem.serialize_payload + dac_plugin + ~page_store + data + in + let () = + Data_streamer.publish + hash_streamer + (Dac_plugin.hash_to_raw root_hash) + in + let*! () = + Event.emit_root_hash_pushed_to_data_streamer dac_plugin root_hash + in + return root_hash + | Pagination_scheme.Hash_chain_V0 -> + Hash_chain.V0.serialize_payload + dac_plugin + ~for_each_page:(fun (hash, content) -> + Page_store.Filesystem.save dac_plugin page_store ~hash ~content) + data + in + let* signature, witnesses = + Signature_manager.Legacy.sign_root_hash dac_plugin - ~for_each_page:(fun (hash, content) -> - Page_store.Filesystem.save dac_plugin page_store ~hash ~content) - data - in - let* signature, witnesses = - Signature_manager.Legacy.sign_root_hash - dac_plugin - cctxt - dac_sk_uris - root_hash - in - let raw_root_hash = Dac_plugin.hash_to_raw root_hash in - let*! external_message = - External_message.Default.make dac_plugin root_hash signature witnesses - in - match external_message with - | Ok external_message -> return @@ (raw_root_hash, external_message) - | Error _ -> tzfail @@ Cannot_construct_external_message + cctxt + dac_sk_uris + root_hash + in + let raw_root_hash = Dac_plugin.hash_to_raw root_hash in + let*! external_message = + External_message.Default.make dac_plugin root_hash signature witnesses + in + match external_message with + | Ok external_message -> return @@ (raw_root_hash, external_message) + | Error _ -> tzfail @@ Cannot_construct_external_message) + | RPC_services.Api.V1 -> raise Not_found -let handle_get_verify_signature dac_plugin public_keys_opt encoded_l1_message = - let open Lwt_result_syntax in - let ((module Plugin) : Dac_plugin.t) = dac_plugin in - let external_message = - let open Option_syntax in - let* encoded_l1_message in - let* as_bytes = Hex.to_bytes @@ `Hex encoded_l1_message in - External_message.Default.of_bytes Plugin.encoding as_bytes - in - match external_message with - | None -> tzfail @@ Cannot_deserialize_external_message - | Some {root_hash; signature; witnesses} -> - Signature_manager.verify - dac_plugin - ~public_keys_opt - (Dac_plugin.hash_to_raw root_hash) - signature - witnesses +let handle_get_verify_signature dac_plugin public_keys_opt encoded_l1_message + api_version = + match api_version with + | RPC_services.Api.V0 -> ( + let open Lwt_result_syntax in + let ((module Plugin) : Dac_plugin.t) = dac_plugin in + let external_message = + let open Option_syntax in + let* encoded_l1_message in + let* as_bytes = Hex.to_bytes @@ `Hex encoded_l1_message in + External_message.Default.of_bytes Plugin.encoding as_bytes + in + match external_message with + | None -> tzfail @@ Cannot_deserialize_external_message + | Some {root_hash; signature; witnesses} -> + Signature_manager.verify + dac_plugin + ~public_keys_opt + (Dac_plugin.hash_to_raw root_hash) + signature + witnesses) + | RPC_services.Api.V1 -> raise Not_found + +let assert_legacy_mode_cannot_run_v1 node_ctxt api_version = + match api_version with + | RPC_services.Api.V0 -> Result_syntax.return_unit + | RPC_services.Api.V1 -> ( + match Node_context.get_mode node_ctxt with + | Node_context.Legacy _ -> raise Not_found + | _ -> Result_syntax.return_unit) -let handle_get_preimage dac_plugin page_store raw_hash = +let handle_get_preimage node_ctxt dac_plugin page_store api_version raw_hash = let open Lwt_result_syntax in - let*? hash = Dac_plugin.raw_to_hash dac_plugin raw_hash in - Page_store.Filesystem.load dac_plugin page_store hash + let*? () = assert_legacy_mode_cannot_run_v1 node_ctxt api_version in + match api_version with + | RPC_services.Api.V0 | RPC_services.Api.V1 -> + let open Lwt_result_syntax in + let*? hash = Dac_plugin.raw_to_hash dac_plugin raw_hash in + Page_store.Filesystem.load dac_plugin page_store hash (* Handler for subscribing to the streaming of root hashes via GET monitor/root_hashes RPC call. *) -let handle_monitor_root_hashes hash_streamer = - let open Lwt_syntax in - let stream, stopper = Data_streamer.handle_subscribe hash_streamer in - let shutdown () = Lwt_watcher.shutdown stopper in - let next () = Lwt_stream.get stream in - let* () = Event.(emit handle_new_subscription_to_hash_streamer ()) in - Tezos_rpc.Answer.return_stream {next; shutdown} - -let handle_get_certificate dac_plugin node_store raw_root_hash = +let handle_monitor_root_hashes node_ctxt hash_streamer api_version = let open Lwt_result_syntax in - let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in + let*? () = assert_legacy_mode_cannot_run_v1 node_ctxt api_version in + match api_version with + | RPC_services.Api.V0 | RPC_services.Api.V1 -> + let stream, stopper = Data_streamer.handle_subscribe hash_streamer in + let shutdown () = Lwt_watcher.shutdown stopper in + let next () = Lwt_stream.get stream in + let*! () = Event.(emit handle_new_subscription_to_hash_streamer ()) in + return (next, shutdown) - let+ value_opt = Store.Certificate_store.find node_store root_hash in - Option.map - (fun Store.{aggregate_signature; witnesses} -> - Certificate_repr. - {aggregate_signature; witnesses; root_hash = raw_root_hash}) - value_opt +let handle_get_certificate dac_plugin ctx raw_root_hash api_version = + let open Lwt_result_syntax in + let*? () = assert_legacy_mode_cannot_run_v1 ctx api_version in + match api_version with + | RPC_services.Api.V0 | RPC_services.Api.V1 -> + let open Lwt_result_syntax in + let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in + let node_store = Node_context.get_node_store ctx Store_sigs.Read_write in + let+ value_opt = Store.Certificate_store.find node_store root_hash in + Option.map + (fun Store.{aggregate_signature; witnesses} -> + Certificate_repr. + {aggregate_signature; witnesses; root_hash = raw_root_hash}) + value_opt -let handle_get_missing_page cctxt page_store dac_plugin raw_root_hash = +let handle_get_missing_page node_ctxt cctxt page_store dac_plugin raw_root_hash + api_version = let open Lwt_result_syntax in + let*? () = assert_legacy_mode_cannot_run_v1 node_ctxt api_version in let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in - let remote_store = Page_store.Remote.(init {cctxt; page_store}) in - let* preimage = - (* TODO: https://gitlab.com/tezos/tezos/-/issues/5142 - Retrieve missing page from dac committee via "flooding". *) - Page_store.Remote.load dac_plugin remote_store root_hash + let (module Remote_store : Page_store.S + with type configuration = Page_store.remote_configuration) = + Page_store.remote_store api_version in - let*! () = Event.(emit fetched_missing_page raw_root_hash) in - return preimage + let remote_store = Remote_store.init {cctxt; page_store} in + match api_version with + | RPC_services.Api.V0 | RPC_services.Api.V1 -> + let* preimage = + (* TODO: https://gitlab.com/tezos/tezos/-/issues/5142 + Retrieve missing page from dac committee via "flooding". *) + Remote_store.load dac_plugin remote_store root_hash + in + let*! () = Event.(emit fetched_missing_page raw_root_hash) in + return preimage let register_post_store_preimage ctx cctxt dac_sk_uris page_store hash_streamer directory = directory |> add_service - Tezos_rpc.Directory.register0 + Tezos_rpc.Directory.register1 RPC_services.post_store_preimage - (fun () input -> + (fun api_version () input -> handle_post_store_preimage ctx cctxt dac_sk_uris page_store hash_streamer - input) + input + api_version) let register_get_verify_signature dac_plugin public_keys_opt directory = directory |> add_service - Tezos_rpc.Directory.register0 + Tezos_rpc.Directory.register1 RPC_services.get_verify_signature - (fun external_message () -> - handle_get_verify_signature dac_plugin public_keys_opt external_message) + (fun api_version external_message () -> + handle_get_verify_signature + dac_plugin + public_keys_opt + external_message + api_version) -let register_get_preimage dac_plugin page_store = +let register_get_preimage node_ctxt dac_plugin page_store = add_service - Tezos_rpc.Directory.register1 + Tezos_rpc.Directory.register2 RPC_services.get_preimage - (fun hash () () -> handle_get_preimage dac_plugin page_store hash) + (fun api_version hash () () -> + handle_get_preimage node_ctxt dac_plugin page_store api_version hash) -let register_monitor_root_hashes hash_streamer dir = +let register_monitor_root_hashes node_ctxt hash_streamer dir = Tezos_rpc.Directory.gen_register dir Monitor_services.S.root_hashes - (fun () () () -> handle_monitor_root_hashes hash_streamer) + (fun ((), api_version) () () -> + let open Lwt_result_syntax in + let*! handler = + handle_monitor_root_hashes node_ctxt hash_streamer api_version + in + match handler with + | Ok (next, shutdown) -> Tezos_rpc.Answer.return_stream {next; shutdown} + | Error e -> Tezos_rpc.Answer.fail e) let register_get_certificate node_store dac_plugin = add_service - Tezos_rpc.Directory.register1 + Tezos_rpc.Directory.register2 RPC_services.get_certificate - (fun root_hash () () -> - handle_get_certificate dac_plugin node_store root_hash) + (fun api_version root_hash () () -> + handle_get_certificate dac_plugin node_store root_hash api_version) -let register_get_missing_page dac_plugin page_store cctxt = +let register_get_missing_page node_ctxt dac_plugin page_store cctxt = add_service - Tezos_rpc.Directory.register1 + Tezos_rpc.Directory.register2 RPC_services.get_missing_page - (fun root_hash () () -> - handle_get_missing_page cctxt page_store dac_plugin root_hash) + (fun api_version root_hash () () -> + handle_get_missing_page + node_ctxt + cctxt + page_store + dac_plugin + root_hash + api_version) module Coordinator = struct - let handle_post_preimage dac_plugin page_store hash_streamer payload = - let open Lwt_result_syntax in - let* root_hash = - Pages_encoding.Merkle_tree.V0.Filesystem.serialize_payload - dac_plugin - ~page_store - payload - in - let () = - Data_streamer.publish hash_streamer (Dac_plugin.hash_to_raw root_hash) - in - let*! () = - Event.emit_root_hash_pushed_to_data_streamer dac_plugin root_hash - in - return @@ Dac_plugin.hash_to_raw root_hash + let handle_post_preimage dac_plugin page_store hash_streamer payload + api_version = + match api_version with + | RPC_services.Api.V0 | RPC_services.Api.V1 -> + let open Lwt_result_syntax in + let* root_hash = + Pages_encoding.Merkle_tree.V0.Filesystem.serialize_payload + dac_plugin + ~page_store + payload + in + let () = + Data_streamer.publish hash_streamer (Dac_plugin.hash_to_raw root_hash) + in + let*! () = + Event.emit_root_hash_pushed_to_data_streamer dac_plugin root_hash + in + return @@ Dac_plugin.hash_to_raw root_hash let handle_monitor_certificate dac_plugin ro_node_store certificate_streamers - raw_root_hash committee_members = - let open Lwt_result_syntax in - let*? stream, stopper = - Certificate_streamers.handle_subscribe - dac_plugin - certificate_streamers - raw_root_hash - in - let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in - let*! () = Event.emit_new_subscription_to_certificate_updates root_hash in - let shutdown () = Lwt_watcher.shutdown stopper in - let next () = Lwt_stream.get stream in - (* Add the current certificate to the streamer, if any, to ensure that - a certificate is returned even in the case that no updates to the - certificate happen for a long time. *) - let*! current_certificate_store_value_res = - Store.Certificate_store.find ro_node_store root_hash - in - match current_certificate_store_value_res with - | Ok current_certificate_store_value -> - let () = - Option.iter - (fun Store.{aggregate_signature; witnesses} -> - let certificate = - Certificate_repr. - {root_hash = raw_root_hash; aggregate_signature; witnesses} - in - let _ = - Certificate_streamers.push - dac_plugin - certificate_streamers - raw_root_hash - certificate - in - if - Certificate_repr.all_committee_members_have_signed - committee_members - certificate - then - let _ = - Certificate_streamers.close - dac_plugin - certificate_streamers - raw_root_hash - in - () - else ()) - current_certificate_store_value + raw_root_hash committee_members api_version = + match api_version with + | RPC_services.Api.V0 | RPC_services.Api.V1 -> ( + let open Lwt_result_syntax in + let*? stream, stopper = + Certificate_streamers.handle_subscribe + dac_plugin + certificate_streamers + raw_root_hash + in + let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in + let*! () = + Event.emit_new_subscription_to_certificate_updates root_hash in - return (next, shutdown) - | Error e -> fail e + let shutdown () = Lwt_watcher.shutdown stopper in + let next () = Lwt_stream.get stream in + (* Add the current certificate to the streamer, if any, to ensure that + a certificate is returned even in the case that no updates to the + certificate happen for a long time. *) + let*! current_certificate_store_value_res = + Store.Certificate_store.find ro_node_store root_hash + in + match current_certificate_store_value_res with + | Ok current_certificate_store_value -> + let () = + Option.iter + (fun Store.{aggregate_signature; witnesses} -> + let certificate = + Certificate_repr. + { + root_hash = raw_root_hash; + aggregate_signature; + witnesses; + } + in + let _ = + Certificate_streamers.push + dac_plugin + certificate_streamers + raw_root_hash + certificate + in + if + Certificate_repr.all_committee_members_have_signed + committee_members + certificate + then + let _ = + Certificate_streamers.close + dac_plugin + certificate_streamers + raw_root_hash + in + () + else ()) + current_certificate_store_value + in + return (next, shutdown) + | Error e -> fail e) let register_monitor_certificate dac_plugin ro_node_store certificate_streamers committee_members dir = Tezos_rpc.Directory.gen_register dir Monitor_services.S.certificate - (fun ((), root_hash) () () -> + (fun (((), api_version), root_hash) () () -> let open Lwt_result_syntax in let*! handler = handle_monitor_certificate @@ -294,6 +360,7 @@ module Coordinator = struct certificate_streamers root_hash committee_members + api_version in match handler with | Ok (next, shutdown) -> Tezos_rpc.Answer.return_stream {next; shutdown} @@ -301,40 +368,42 @@ module Coordinator = struct let register_post_preimage dac_plugin hash_streamer page_store = add_service - Tezos_rpc.Directory.register0 + Tezos_rpc.Directory.register1 RPC_services.Coordinator.post_preimage - (fun () payload -> - handle_post_preimage dac_plugin page_store hash_streamer payload) + (fun api_version () payload -> + handle_post_preimage + dac_plugin + page_store + hash_streamer + payload + api_version) let register_put_dac_member_signature ctx dac_plugin rw_node_store page_store cctxt = add_service - Tezos_rpc.Directory.register0 + Tezos_rpc.Directory.register1 RPC_services.put_dac_member_signature - (fun () dac_member_signature -> + (fun api_version () dac_member_signature -> Signature_manager.Coordinator.handle_put_dac_member_signature ctx dac_plugin rw_node_store page_store cctxt - dac_member_signature) + dac_member_signature + api_version) - let dynamic_rpc_dir dac_plugin rw_store page_store cctxt coordinator_node_ctxt - = + let dynamic_rpc_dir node_ctxt dac_plugin rw_store page_store cctxt + coordinator_node_ctxt = let hash_streamer = coordinator_node_ctxt.Node_context.Coordinator.hash_streamer in - let public_keys_opt = - Node_context.Coordinator.public_keys_opt coordinator_node_ctxt - in let certificate_streamers = coordinator_node_ctxt.certificate_streamers in let committee_members = coordinator_node_ctxt.committee_members in Tezos_rpc.Directory.empty |> register_post_preimage dac_plugin hash_streamer page_store - |> register_get_verify_signature dac_plugin public_keys_opt - |> register_get_preimage dac_plugin page_store - |> register_monitor_root_hashes hash_streamer + |> register_get_preimage node_ctxt dac_plugin page_store + |> register_monitor_root_hashes node_ctxt hash_streamer |> register_monitor_certificate dac_plugin rw_store @@ -346,37 +415,45 @@ module Coordinator = struct rw_store page_store cctxt - |> register_get_certificate rw_store dac_plugin + |> register_get_certificate node_ctxt dac_plugin end module Committee_member = struct - let dynamic_rpc_dir dac_plugin page_store = - Tezos_rpc.Directory.empty |> register_get_preimage dac_plugin page_store + let dynamic_rpc_dir dac_plugin node_ctxt = + let page_store = Node_context.get_page_store node_ctxt in + Tezos_rpc.Directory.empty + |> register_get_preimage node_ctxt dac_plugin page_store end module Observer = struct - let dynamic_rpc_dir dac_plugin coordinator_cctxt page_store = + let dynamic_rpc_dir node_ctxt dac_plugin coordinator_cctxt page_store = Tezos_rpc.Directory.empty - |> register_get_preimage dac_plugin page_store - |> register_get_missing_page dac_plugin page_store coordinator_cctxt + |> register_get_preimage node_ctxt dac_plugin page_store + |> register_get_missing_page + node_ctxt + dac_plugin + page_store + coordinator_cctxt end module Legacy = struct let register_put_dac_member_signature ctx dac_plugin rw_node_store page_store cctxt = add_service - Tezos_rpc.Directory.register0 + Tezos_rpc.Directory.register1 RPC_services.put_dac_member_signature - (fun () dac_member_signature -> + (fun api_version () dac_member_signature -> Signature_manager.Legacy.handle_put_dac_member_signature ctx dac_plugin rw_node_store page_store cctxt - dac_member_signature) + dac_member_signature + api_version) - let dynamic_rpc_dir dac_plugin rw_store page_store cctxt legacy_node_ctxt = + let dynamic_rpc_dir node_ctxt dac_plugin rw_store page_store cctxt + legacy_node_ctxt = let hash_streamer = legacy_node_ctxt.Node_context.Legacy.hash_streamer in let public_keys_opt = Node_context.Legacy.public_keys_opt legacy_node_ctxt @@ -389,7 +466,8 @@ module Legacy = struct | None -> fun dir -> dir | Some cctxt -> fun dir -> - dir |> register_get_missing_page dac_plugin page_store cctxt + dir + |> register_get_missing_page node_ctxt dac_plugin page_store cctxt in Tezos_rpc.Directory.empty |> register_post_store_preimage @@ -399,15 +477,15 @@ module Legacy = struct page_store hash_streamer |> register_get_verify_signature dac_plugin public_keys_opt - |> register_get_preimage dac_plugin page_store - |> register_monitor_root_hashes hash_streamer + |> register_get_preimage node_ctxt dac_plugin page_store + |> register_monitor_root_hashes node_ctxt hash_streamer |> register_put_dac_member_signature legacy_node_ctxt dac_plugin rw_store page_store cctxt - |> register_get_certificate rw_store dac_plugin + |> register_get_certificate node_ctxt dac_plugin |> register_get_missing_page end @@ -420,17 +498,23 @@ let start ~rpc_address ~rpc_port node_ctxt = match Node_context.get_mode node_ctxt with | Coordinator coordinator_node_ctxt -> Coordinator.dynamic_rpc_dir + node_ctxt dac_plugin rw_store page_store cctxt coordinator_node_ctxt | Committee_member _committee_member_node_ctxt -> - Committee_member.dynamic_rpc_dir dac_plugin page_store + Committee_member.dynamic_rpc_dir dac_plugin node_ctxt | Observer {coordinator_cctxt; _} -> - Observer.dynamic_rpc_dir dac_plugin coordinator_cctxt page_store + Observer.dynamic_rpc_dir + node_ctxt + dac_plugin + coordinator_cctxt + page_store | Legacy legacy_node_ctxt -> Legacy.dynamic_rpc_dir + node_ctxt dac_plugin rw_store page_store diff --git a/src/lib_dac_node/handler.ml b/src/lib_dac_node/handler.ml index 472fea25d6afc0d9fde87192f92224ba898a41a9..8b6515d07459a907ba4d391aa1ed3967728b18b6 100644 --- a/src/lib_dac_node/handler.ml +++ b/src/lib_dac_node/handler.ml @@ -136,7 +136,7 @@ let new_head ctxt = } *) module Committee_member = struct let push_payload_signature coordinator_cctxt wallet_cctxt committee_member - root_hash = + root_hash api_version = let open Lwt_result_syntax in let signer_pkh = committee_member.Wallet_account.Committee_member.public_key_hash @@ -154,26 +154,25 @@ module Committee_member = struct {root_hash = Dac_plugin.hash_to_raw root_hash; signature; signer_pkh} in let* () = - Dac_node_client.put_dac_member_signature + (match api_version with + | RPC_services.Api.V0 -> Dac_node_client.V0.put_dac_member_signature + | RPC_services.Api.V1 -> Dac_node_client.V1.put_dac_member_signature) coordinator_cctxt ~signature:signature_repr in let*! () = Event.emit_signature_pushed_to_coordinator signature in return_unit - let new_root_hash ctxt wallet_cctxt dac_plugin page_store = + let new_root_hash ctxt wallet_cctxt dac_plugin page_store api_version = let open Lwt_result_syntax in let coordinator_cctxt = ctxt.Node_context.Committee_member.coordinator_cctxt in - let handler dac_plugin remote_store _stopper root_hash = + let handler dac_plugin remote_store deserialize_payload _stopper root_hash = let*? root_hash = Dac_plugin.raw_to_hash dac_plugin root_hash in let*! () = Event.emit_new_root_hash_received dac_plugin root_hash in let*! payload_result = - Pages_encoding.Merkle_tree.V0.Remote.deserialize_payload - dac_plugin - ~page_store:remote_store - root_hash + deserialize_payload dac_plugin ~page_store:remote_store root_hash in match payload_result with | Ok _ -> @@ -188,6 +187,7 @@ module Committee_member = struct wallet_cctxt committee_member root_hash + api_version | Error errs -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/4930. Improve handling of errors. *) @@ -196,13 +196,29 @@ module Committee_member = struct in return () in - let remote_store = - Page_store.(Remote.init {cctxt = coordinator_cctxt; page_store}) - in - let*! () = Event.(emit subscribed_to_root_hashes_stream ()) in - make_stream_daemon - (handler dac_plugin remote_store) - (Monitor_services.root_hashes coordinator_cctxt) + match api_version with + | RPC_services.Api.V0 -> + let remote_store = + Page_store.Remote_V0.init {cctxt = coordinator_cctxt; page_store} + in + let*! () = Event.(emit subscribed_to_root_hashes_stream ()) in + make_stream_daemon + (handler + dac_plugin + remote_store + Pages_encoding.Merkle_tree.V0.Remote.deserialize_payload) + (Monitor_services.root_hashes coordinator_cctxt api_version) + | RPC_services.Api.V1 -> + let remote_store = + Page_store.Remote_V1.init {cctxt = coordinator_cctxt; page_store} + in + let*! () = Event.(emit subscribed_to_root_hashes_stream ()) in + make_stream_daemon + (handler + dac_plugin + remote_store + Pages_encoding.Merkle_tree.V1.Remote.deserialize_payload) + (Monitor_services.root_hashes coordinator_cctxt api_version) end (** Handlers specific to an [Observer]. An [Observer] is responsible for @@ -213,17 +229,14 @@ end } *) module Observer = struct - let new_root_hash ctxt dac_plugin page_store = + let new_root_hash ctxt dac_plugin page_store api_version = let open Lwt_result_syntax in let coordinator_cctxt = ctxt.Node_context.Observer.coordinator_cctxt in - let handler dac_plugin remote_store _stopper root_hash = + let handler dac_plugin remote_store deserialize_payload _stopper root_hash = let*? root_hash = Dac_plugin.raw_to_hash dac_plugin root_hash in let*! () = Event.emit_new_root_hash_received dac_plugin root_hash in let*! payload_result = - Pages_encoding.Merkle_tree.V0.Remote.deserialize_payload - dac_plugin - ~page_store:remote_store - root_hash + deserialize_payload dac_plugin ~page_store:remote_store root_hash in match payload_result with | Ok _ -> @@ -239,13 +252,29 @@ module Observer = struct in return () in - let remote_store = - Page_store.(Remote.init {cctxt = coordinator_cctxt; page_store}) - in - let*! () = Event.(emit subscribed_to_root_hashes_stream ()) in - make_stream_daemon - (handler dac_plugin remote_store) - (Monitor_services.root_hashes coordinator_cctxt) + match api_version with + | RPC_services.Api.V0 -> + let remote_store = + Page_store.Remote_V0.init {cctxt = coordinator_cctxt; page_store} + in + let*! () = Event.(emit subscribed_to_root_hashes_stream ()) in + make_stream_daemon + (handler + dac_plugin + remote_store + Pages_encoding.Merkle_tree.V0.Remote.deserialize_payload) + (Monitor_services.root_hashes coordinator_cctxt api_version) + | RPC_services.Api.V1 -> + let remote_store = + Page_store.Remote_V1.init {cctxt = coordinator_cctxt; page_store} + in + let*! () = Event.(emit subscribed_to_root_hashes_stream ()) in + make_stream_daemon + (handler + dac_plugin + remote_store + Pages_encoding.Merkle_tree.V1.Remote.deserialize_payload) + (Monitor_services.root_hashes coordinator_cctxt api_version) end (** Handlers specific to a [Legacy] DAC node. If no @@ -295,7 +324,7 @@ module Legacy = struct } in let* () = - Dac_node_client.put_dac_member_signature + Dac_node_client.V0.put_dac_member_signature coordinator_cctxt ~signature:signature_repr in @@ -349,12 +378,12 @@ module Legacy = struct return () in let remote_store = - Page_store.(Remote.init {cctxt = coordinator_cctxt; page_store}) + Page_store.Remote_V0.init {cctxt = coordinator_cctxt; page_store} in let*! () = Event.(emit subscribed_to_root_hashes_stream ()) in make_stream_daemon (handler dac_plugin remote_store) - (Monitor_services.root_hashes coordinator_cctxt) + (Monitor_services.root_hashes coordinator_cctxt RPC_services.Api.V0) end let handlers node_ctxt = @@ -362,16 +391,26 @@ let handlers node_ctxt = let*? plugin = Node_context.get_dac_plugin node_ctxt in let page_store = Node_context.get_page_store node_ctxt in let wallet_cctxt = Node_context.get_tezos_node_cctxt node_ctxt in + let api_version = Node_context.get_api_version node_ctxt in match Node_context.get_mode node_ctxt with | Coordinator _ -> return [new_head node_ctxt] | Committee_member ctxt -> return [ new_head node_ctxt; - Committee_member.new_root_hash ctxt wallet_cctxt plugin page_store; + Committee_member.new_root_hash + ctxt + wallet_cctxt + plugin + page_store + api_version; ] | Observer ctxt -> - return [new_head node_ctxt; Observer.new_root_hash ctxt plugin page_store] + return + [ + new_head node_ctxt; + Observer.new_root_hash ctxt plugin page_store api_version; + ] | Legacy ctxt -> let coordinator_cctxt_opt = ctxt.Node_context.Legacy.coordinator_cctxt in let root_hash_handler = diff --git a/src/lib_dac_node/monitor_services.ml b/src/lib_dac_node/monitor_services.ml index 41a38017d90ba10f711927d89fcae44bb94fc073..cc0e7d2a6974a28880dc45b140fa0c9cdde34d07 100644 --- a/src/lib_dac_node/monitor_services.ml +++ b/src/lib_dac_node/monitor_services.ml @@ -31,7 +31,9 @@ module S = struct responsible for the serialization of the dac payload (coordinator). " ~query:Tezos_rpc.Query.empty ~output:Dac_plugin.raw_hash_encoding - Tezos_rpc.Path.(open_root / "monitor" / "root_hashes") + Tezos_rpc.Path.( + open_root /: RPC_services.Api.version_rpc_arg / "monitor" + / "root_hashes") let certificate = Tezos_rpc.Service.get_service @@ -45,16 +47,22 @@ module S = struct ~query:Tezos_rpc.Query.empty ~output:Certificate_repr.encoding Tezos_rpc.Path.( - open_root / "monitor" / "certificate" /: Dac_plugin.raw_hash_rpc_arg) + open_root /: RPC_services.Api.version_rpc_arg / "monitor" + / "certificate" /: Dac_plugin.raw_hash_rpc_arg) end -let root_hashes dac_node_cctxt = - Tezos_rpc.Context.make_streamed_call S.root_hashes dac_node_cctxt () () () +let root_hashes dac_node_cctxt api_version = + Tezos_rpc.Context.make_streamed_call + S.root_hashes + dac_node_cctxt + ((), api_version) + () + () -let certificate dac_node_cctxt root_hash = +let certificate dac_node_cctxt root_hash api_version = Tezos_rpc.Context.make_streamed_call S.certificate dac_node_cctxt - ((), root_hash) + (((), api_version), root_hash) () () diff --git a/src/lib_dac_node/monitor_services.mli b/src/lib_dac_node/monitor_services.mli index 63cc638a05fd12ff887ef5b2c461e40c84a52b47..83e4450eb57e596ed8ae6edc1bc0a943e404398d 100644 --- a/src/lib_dac_node/monitor_services.mli +++ b/src/lib_dac_node/monitor_services.mli @@ -24,41 +24,48 @@ (*****************************************************************************) module S : sig - (** Define RPC GET /monitor/root_hashes. *) + (** Define RPC GET [api_version]/monitor/root_hashes. *) val root_hashes : ( [`GET], unit, - unit, + unit * RPC_services.Api.version, unit, unit, Dac_plugin.raw_hash ) Tezos_rpc.Service.service - (** Define RPC GET /monitor/certificate/{hex_root_hash}. *) + (** Define RPC GET [api_version]/monitor/certificate/{hex_root_hash}. *) val certificate : ( [`GET], unit, - unit * Dac_plugin.raw_hash, + (unit * RPC_services.Api.version) * Dac_plugin.raw_hash, unit, unit, Certificate_repr.t ) Tezos_rpc.Service.service end -(** [root_hashes streamed_cctxt dac_plugin] returns a stream of root hashes +(** [root_hashes streamed_cctxt api_version] returns a stream of root hashes and a stopper for it. - Stream is produced by calling RPC GET /monitor/root_hashes. + Stream is produced by calling RPC GET [api_version]/monitor/root_hashes. *) val root_hashes : #Tezos_rpc.Context.streamed -> + RPC_services.Api.version -> (Dac_plugin.raw_hash Lwt_stream.t * Tezos_rpc.Context.stopper) Error_monad.tzresult Lwt.t +(** [certificate streamed_cctxt raw_hash api_version] returns a stream of + updates to certificate of a given root hash and a stopper for it. + + Stream is produced by calling RPC GET [api_version]/monitor/certificate. +*) val certificate : #Tezos_rpc.Context.streamed -> Dac_plugin.raw_hash -> + RPC_services.Api.version -> (Certificate_repr.t Lwt_stream.t * Tezos_rpc.Context.stopper) Error_monad.tzresult Lwt.t diff --git a/src/lib_dac_node/node_context.ml b/src/lib_dac_node/node_context.ml index 15a2306d9c6d5bd4d83667f39cbb9355c0375e2c..f3981e362afbb4a9819f2b69157ea75e29700278 100644 --- a/src/lib_dac_node/node_context.ml +++ b/src/lib_dac_node/node_context.ml @@ -225,6 +225,7 @@ type t = { page_store : Page_store.Filesystem.t; node_store : Store_sigs.rw Store.Irmin_store.t; mode : mode; + api_version : RPC_services.Api.version; } let init_mode Configuration.{mode; _} cctxt = @@ -259,6 +260,7 @@ let init config cctxt = Page_store.Filesystem.init (Configuration.reveal_data_dir config); node_store; mode; + api_version = RPC_services.Api.V1; } let get_mode node_ctxt = node_ctxt.mode @@ -312,3 +314,5 @@ let get_node_store (type a) ctxt (access_mode : a Store_sigs.mode) : match access_mode with | Store_sigs.Read_only -> Store.Irmin_store.readonly ctxt.node_store | Store_sigs.Read_write -> ctxt.node_store + +let get_api_version ctxt = ctxt.api_version diff --git a/src/lib_dac_node/node_context.mli b/src/lib_dac_node/node_context.mli index bcd6a697c66da7cdbe641c6c6f10fa7ede099fbf..f23cb569c536a7ac05ab883524d474d610367ab0 100644 --- a/src/lib_dac_node/node_context.mli +++ b/src/lib_dac_node/node_context.mli @@ -180,3 +180,9 @@ val get_page_store : t -> Page_store.Filesystem.t (** [get_node_store ctxt access_mode] returns the [Store.Irmin_store.t] with [access_mode] used by Dac components. *) val get_node_store : t -> 'a Store_sigs.mode -> 'a Store.Irmin_store.t + +(** [get_api_version] returns the api version that the node is running in. + [api_version] corresponds to the latest api version supported by the + node and is also used for interacting with other nodes APIs. The exception + being [Legacy] mode, where the node always uses [API.V0]. *) +val get_api_version : t -> RPC_services.Api.version diff --git a/src/lib_dac_node/page_store.ml b/src/lib_dac_node/page_store.ml index a7045287d27687b202244a55dfaaeb8985f660c3..5ea32120257807062f7bb13047a7efd0841a3499 100644 --- a/src/lib_dac_node/page_store.ml +++ b/src/lib_dac_node/page_store.ml @@ -257,12 +257,17 @@ end) content end +module type API_VERSION = sig + val version : RPC_services.Api.version +end + type remote_configuration = { cctxt : Dac_node_client.cctxt; page_store : Filesystem.t; } -module Remote : S with type configuration = remote_configuration = struct +module Make_Remote (A : API_VERSION) : + S with type configuration = remote_configuration = struct module F = Filesystem_with_integrity_check module Internal : @@ -274,7 +279,9 @@ module Remote : S with type configuration = remote_configuration = struct type remote_context = Dac_node_client.cctxt let fetch _dac_plugin remote_context hash = - Dac_node_client.get_preimage + (match A.version with + | RPC_services.Api.V0 -> Dac_node_client.V0.get_preimage + | RPC_services.Api.V1 -> Dac_node_client.V1.get_preimage) remote_context ~page_hash:(Dac_plugin.hash_to_raw hash) end) @@ -289,6 +296,20 @@ module Remote : S with type configuration = remote_configuration = struct let init {cctxt; page_store} = Internal.init (cctxt, page_store) end +module Remote_V0 = Make_Remote (struct + let version = RPC_services.Api.V0 +end) + +module Remote_V1 = Make_Remote (struct + let version = RPC_services.Api.V1 +end) + +let remote_store api_version = + (module Make_Remote (struct + let version = api_version + end) : S + with type configuration = remote_configuration) + module Internal_for_tests = struct module With_data_integrity_check (P : S) : S with type configuration = P.configuration and type t = P.t = diff --git a/src/lib_dac_node/page_store.mli b/src/lib_dac_node/page_store.mli index 97ce6e12174e2af680ea63568be63cc286a07312..111740a15541cdc760d22979730c92b0aaeb707c 100644 --- a/src/lib_dac_node/page_store.mli +++ b/src/lib_dac_node/page_store.mli @@ -92,10 +92,17 @@ type remote_configuration = { (** A [Page_store] implementation backed by the local filesystem, which uses a connection to a Dac node to retrieve pages that are not - saved locally. *) -module Remote : S with type configuration = remote_configuration + saved locally. [V0] api is used in case of retrieving the pages remotely. *) +module Remote_V0 : S with type configuration = remote_configuration -(**/**) +(** A [Page_store] implementation backed by the local filesystem, which + uses a connection to a Dac node to retrieve pages that are not + saved locally. [V1] api is used in case of retrieving the pages remotely. *) +module Remote_V1 : S with type configuration = remote_configuration + +val remote_store : + RPC_services.Api.version -> + (module S with type configuration = remote_configuration) module Internal_for_tests : sig (** [With_data_integrity_check] tweaks a module [P] of type [Page_store] diff --git a/src/lib_dac_node/pages_encoding.ml b/src/lib_dac_node/pages_encoding.ml index 7c090bea980bac744d0b8d4b2d092e89dfdf7885..12e8036765b10ba7acf6dfceb49ae51eb1451607 100644 --- a/src/lib_dac_node/pages_encoding.ml +++ b/src/lib_dac_node/pages_encoding.ml @@ -554,7 +554,7 @@ module Merkle_tree = struct module Filesystem = Make_buffered (Page_store.Filesystem) (V0_metadata) (V0_page_size) module Remote = - Make_buffered (Page_store.Remote) (V0_metadata) (V0_page_size) + Make_buffered (Page_store.Remote_V0) (V0_metadata) (V0_page_size) end module V0 = struct @@ -562,6 +562,18 @@ module Merkle_tree = struct module Remote = Make (V0_Buffered.Remote) end + module V1_Buffered = struct + module Filesystem = + Make_buffered (Page_store.Filesystem) (V0_metadata) (V0_page_size) + module Remote = + Make_buffered (Page_store.Remote_V1) (V0_metadata) (V0_page_size) + end + + module V1 = struct + module Filesystem = Make (V1_Buffered.Filesystem) + module Remote = Make (V1_Buffered.Remote) + end + module Internal_for_tests = struct module Make_buffered = Make_buffered module Make = Make diff --git a/src/lib_dac_node/pages_encoding.mli b/src/lib_dac_node/pages_encoding.mli index 662d27731ff8372a9e442a41325d99b8bd95eac8..e6d512cbbf6947a810e95df8c151b9746ac84230 100644 --- a/src/lib_dac_node/pages_encoding.mli +++ b/src/lib_dac_node/pages_encoding.mli @@ -157,9 +157,14 @@ module Merkle_tree : sig module V0 : sig module Filesystem : Dac_codec with type page_store = Page_store.Filesystem.t - module Remote : Dac_codec with type page_store = Page_store.Remote.t + module Remote : Dac_codec with type page_store = Page_store.Remote_V0.t end + module V1 : sig + module Filesystem : Dac_codec with type page_store = Page_store.Filesystem.t + + module Remote : Dac_codec with type page_store = Page_store.Remote_V1.t + end module Internal_for_tests : sig module Make_buffered (S : Page_store.S) (V : VERSION) (C : CONFIG) : Buffered_dac_codec with type page_store = S.t diff --git a/src/lib_dac_node/signature_manager.ml b/src/lib_dac_node/signature_manager.ml index 8b35e08a52993d3f5f454927db5abfc07257b83d..5c119cb40b863120b7a072a2afd15d833d6283ba 100644 --- a/src/lib_dac_node/signature_manager.ml +++ b/src/lib_dac_node/signature_manager.ml @@ -353,47 +353,51 @@ let stream_certificate_update dac_plugin committee_members let handle_put_dac_member_signature dac_plugin certificate_streamers_opt rw_node_store page_store cctxt committee_members committee_member_signature - = - let open Lwt_result_syntax in - let ((module Plugin) : Dac_plugin.t) = dac_plugin in - let Signature_repr.{root_hash = raw_root_hash; _} = - committee_member_signature - in - let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in - let* () = check_coordinator_knows_root_hash dac_plugin page_store root_hash in - let* should_update_certificate = - should_update_certificate - dac_plugin - cctxt - rw_node_store - committee_members - committee_member_signature - in - if should_update_certificate then - let* () = - add_dac_member_signature - dac_plugin - rw_node_store + api_version = + match api_version with + | RPC_services.Api.V0 | RPC_services.Api.V1 -> + let open Lwt_result_syntax in + let ((module Plugin) : Dac_plugin.t) = dac_plugin in + let Signature_repr.{root_hash = raw_root_hash; _} = committee_member_signature - in - let* aggregate_signature, witnesses = - update_aggregate_sig_store rw_node_store committee_members root_hash - in - let*? () = - Option.iter_e - (stream_certificate_update - dac_plugin - committee_members - Certificate_repr. - {root_hash = raw_root_hash; aggregate_signature; witnesses}) - certificate_streamers_opt - in - return () - else return () + in + let*? root_hash = Dac_plugin.raw_to_hash dac_plugin raw_root_hash in + let* () = + check_coordinator_knows_root_hash dac_plugin page_store root_hash + in + let* should_update_certificate = + should_update_certificate + dac_plugin + cctxt + rw_node_store + committee_members + committee_member_signature + in + if should_update_certificate then + let* () = + add_dac_member_signature + dac_plugin + rw_node_store + committee_member_signature + in + let* aggregate_signature, witnesses = + update_aggregate_sig_store rw_node_store committee_members root_hash + in + let*? () = + Option.iter_e + (stream_certificate_update + dac_plugin + committee_members + Certificate_repr. + {root_hash = raw_root_hash; aggregate_signature; witnesses}) + certificate_streamers_opt + in + return () + else return () module Coordinator = struct let handle_put_dac_member_signature ctx dac_plugin rw_node_store page_store - cctxt dac_member_signature = + cctxt dac_member_signature api_version = let committee_members = Node_context.Coordinator.committee_members ctx in handle_put_dac_member_signature dac_plugin @@ -403,6 +407,7 @@ module Coordinator = struct cctxt committee_members dac_member_signature + api_version end module Legacy = struct @@ -428,7 +433,14 @@ module Legacy = struct | Some signature -> return @@ (signature, witnesses)) let handle_put_dac_member_signature ctx dac_plugin rw_node_store page_store - cctxt dac_member_signature = + cctxt dac_member_signature api_version = + let assert_legacy_mode_cannot_run_v1 api_version = + match api_version with + | RPC_services.Api.V0 -> Result_syntax.return_unit + | RPC_services.Api.V1 -> raise Not_found + in + let open Lwt_result_syntax in + let*? () = assert_legacy_mode_cannot_run_v1 api_version in let committee_members = Node_context.Legacy.committee_members ctx in handle_put_dac_member_signature dac_plugin @@ -438,4 +450,5 @@ module Legacy = struct cctxt committee_members dac_member_signature + api_version end diff --git a/src/lib_dac_node/signature_manager.mli b/src/lib_dac_node/signature_manager.mli index 9afa9f5bb76a3ace976d480fb4ab135ebb2e123b..90b7e51756f830e8ba45001ea251d7e171c83382 100644 --- a/src/lib_dac_node/signature_manager.mli +++ b/src/lib_dac_node/signature_manager.mli @@ -71,6 +71,7 @@ module Coordinator : sig Page_store.Filesystem.t -> #Client_context.wallet -> Signature_repr.t -> + RPC_services.Api.version -> unit tzresult Lwt.t end @@ -103,5 +104,6 @@ module Legacy : sig Page_store.Filesystem.t -> #Client_context.wallet -> Signature_repr.t -> + RPC_services.Api.version -> unit tzresult Lwt.t end diff --git a/src/proto_017_PtNairob/lib_dac_plugin/test/test_dac_pages_encoding.ml b/src/proto_017_PtNairob/lib_dac_plugin/test/test_dac_pages_encoding.ml index a9483a551538dbf4837de8678c675074601cc3e8..8a91291d16c937edf20b5294b8d55762393145f8 100644 --- a/src/proto_017_PtNairob/lib_dac_plugin/test/test_dac_pages_encoding.ml +++ b/src/proto_017_PtNairob/lib_dac_plugin/test/test_dac_pages_encoding.ml @@ -238,7 +238,8 @@ module Double_hash_map_backend : (struct type remote_context = Hashes_map_backend.t - let fetch = Hashes_map_backend.load + let fetch dac_plugin remote_context hash = + Hashes_map_backend.load dac_plugin remote_context hash end) (With_hash_check) diff --git a/src/proto_alpha/lib_dac_plugin/test/test_dac_pages_encoding.ml b/src/proto_alpha/lib_dac_plugin/test/test_dac_pages_encoding.ml index 2a6018905deccb04d4f06bcbf7aa9cd8c1df572a..4bcca03fed5e0d1d40411cae6a52a52b3219b7f9 100644 --- a/src/proto_alpha/lib_dac_plugin/test/test_dac_pages_encoding.ml +++ b/src/proto_alpha/lib_dac_plugin/test/test_dac_pages_encoding.ml @@ -238,7 +238,8 @@ module Double_hash_map_backend : (struct type remote_context = Hashes_map_backend.t - let fetch = Hashes_map_backend.load + let fetch dac_plugin remote_context hash = + Hashes_map_backend.load dac_plugin remote_context hash end) (With_hash_check) diff --git a/tezt/lib_tezos/dac_rpc.ml b/tezt/lib_tezos/dac_rpc.ml index 6314faed3f95e3082fd93370d92afc50070a9766..59527cb8e9138679bf9c7aecd181ec1e76894e47 100644 --- a/tezt/lib_tezos/dac_rpc.ml +++ b/tezt/lib_tezos/dac_rpc.ml @@ -23,6 +23,17 @@ (* *) (*****************************************************************************) +(** [Api] module is used for versioning DAC API. *) +module Api = struct + (** [v0] is a version that will get deprecated once old legacy tests are + refactored. Do not use it!. *) + let v0 = "v0" + + (** [v1] is a version that corresponds to the first public release of the DAC + API. *) + let v1 = "v1" +end + let make ?data ?query_string = RPC.make ?data @@ -40,9 +51,10 @@ let decode_hex_string_to_bytes s = Hex.to_string (`Hex s) let get_bytes_from_json_string_node json = JSON.as_string json |> decode_hex_string_to_bytes -let get_preimage page_hash = make GET ["preimage"; page_hash] JSON.as_string +let get_preimage page_hash ~api_version = + make GET [api_version; "preimage"; page_hash] JSON.as_string -let post_store_preimage ~payload ~pagination_scheme = +let post_store_preimage ~payload ~pagination_scheme ~api_version = let preimage = JSON.parse ~origin:"dal_node_dac_store_preimage_rpc" @@ -52,18 +64,19 @@ let post_store_preimage ~payload ~pagination_scheme = pagination_scheme) in let data : RPC_core.data = Data (JSON.unannotate preimage) in - make ~data POST ["store_preimage"] @@ fun json -> + make ~data POST [api_version; "store_preimage"] @@ fun json -> JSON. ( json |-> "root_hash" |> as_string, json |-> "external_message" |> get_bytes_from_json_string_node ) -let get_verify_signature external_msg = +let get_verify_signature external_msg ~api_version = let query_string = [("external_message", match Hex.of_string external_msg with `Hex s -> s)] in - make ~query_string GET ["verify_signature"] JSON.as_bool + make ~query_string GET [api_version; "verify_signature"] JSON.as_bool -let put_dac_member_signature ~hex_root_hash ~dac_member_pkh ~signature = +let put_dac_member_signature ~hex_root_hash ~dac_member_pkh ~signature + ~api_version = let (`Hex root_hash) = hex_root_hash in let payload = `O @@ -75,26 +88,26 @@ let put_dac_member_signature ~hex_root_hash ~dac_member_pkh ~signature = ] in let data : RPC_core.data = Data payload in - make ~data PUT ["dac_member_signature"] @@ fun _resp -> () + make ~data PUT [api_version; "dac_member_signature"] @@ fun _resp -> () -let get_certificate ~hex_root_hash = +let get_certificate ~hex_root_hash ~api_version = let (`Hex page_hash) = hex_root_hash in - make GET ["certificates"; page_hash] @@ fun json -> + make GET [api_version; "certificates"; page_hash] @@ fun json -> JSON. ( json |-> "witnesses" |> as_int, json |-> "aggregate_signature" |> as_string, json |-> "root_hash" |> as_string ) -let get_missing_page ~hex_root_hash = - make GET ["missing_page"; Hex.show hex_root_hash] JSON.as_string +let get_missing_page ~hex_root_hash ~api_version = + make GET [api_version; "missing_page"; Hex.show hex_root_hash] JSON.as_string module Coordinator = struct - let post_preimage ~payload = + let post_preimage ~payload ~api_version = let preimage = JSON.parse ~origin:"Rollup.DAC.RPC.coordinator_post_preimage" (encode_bytes_to_hex_string payload) in let data : RPC_core.data = Data (JSON.unannotate preimage) in - make ~data POST ["preimage"] JSON.as_string + make ~data POST [api_version; "preimage"] JSON.as_string end diff --git a/tezt/lib_tezos/dac_rpc.mli b/tezt/lib_tezos/dac_rpc.mli index 652f605a64f474297d7cb4b0b1a15458332250b2..406491982da95f685a283ca0dcb674ee07625e7a 100644 --- a/tezt/lib_tezos/dac_rpc.mli +++ b/tezt/lib_tezos/dac_rpc.mli @@ -23,50 +23,70 @@ (* *) (*****************************************************************************) -(** [get_preimage hash] requests the preimage of hash, consisting of a +(** [Api] module is used for versioning DAC API. *) +module Api : sig + (** [v0] is a version that will get deprecated once old legacy tests are + refactored. Do not use it!. *) + val v0 : string + + (** [v1] is a version that corresponds to the first public release of the DAC + API. *) + val v1 : string +end + +(** [get_preimage hash ~api_version] requests the preimage of hash, consisting of a single page, from cctxt. When the request succeeds, the raw page will be returned as a sequence of bytes. *) -val get_preimage : string -> (Dac_node.t, string) RPC_core.t +val get_preimage : + string -> api_version:string -> (Dac_node.t, string) RPC_core.t -(** [post_store_preimage cctxt ~payload ~pagination_scheme] posts a [payload] to dac/store_preimage - using a given [pagination_scheme]. It returns the base58 encoded root page hash - and the raw bytes that can be used as contents of a rollup message to trigger the request of - the payload in a WASM rollup. *) +(** [post_store_preimage cctxt ~payload ~pagination_scheme ~api_version] posts + a [payload] to dac/store_preimage using a given [pagination_scheme]. + It returns the base58 encoded root page hash and the raw bytes that can be + used as contents of a rollup message to trigger the request of the payload + in a WASM rollup. *) val post_store_preimage : payload:string -> pagination_scheme:string -> + api_version:string -> (Dac_node.t, string * string) RPC_core.t -(** [get_verify_signature cctxt external_message] requests the DAL node to verify - the signature of the external message [external_message] via +(** [get_verify_signature cctxt external_message ~api_version] requests the DAL + node to verify the signature of the external message [external_message] via the plugin/dac/verify_signature endpoint. The DAC committee of the DAL node must be the same that was used to produce the [external_message]. *) -val get_verify_signature : string -> (Dac_node.t, bool) RPC_core.t +val get_verify_signature : + string -> api_version:string -> (Dac_node.t, bool) RPC_core.t -(** [put_dac_member_signature hex_root_hash dac_member_pkh signature] +(** [put_dac_member_signature hex_root_hash dac_member_pkh signature ~api_version] stores the [signature] generated from signing [hex_root_hash] by [dac_member_pkh]. *) val put_dac_member_signature : hex_root_hash:Hex.t -> dac_member_pkh:string -> signature:Tezos_crypto.Aggregate_signature.t -> + api_version:string -> (Dac_node.t, unit) RPC_core.t -(** [get_certificate ~hex_root_hash] fetches the DAC certificate for the - provided [hex_root_hash]. *) +(** [get_certificate ~hex_root_hash ~api_version] fetches the DAC certificate + for the provided [hex_root_hash]. *) val get_certificate : - hex_root_hash:Hex.t -> (Dac_node.t, int * string * string) RPC_core.t + hex_root_hash:Hex.t -> + api_version:string -> + (Dac_node.t, int * string * string) RPC_core.t -(** [get_missing_page ~hex_root_hash] calls GET missing_page/[page_hash] - endpoint. *) -val get_missing_page : hex_root_hash:Hex.t -> (Dac_node.t, string) RPC_core.t +(** [get_missing_page ~hex_root_hash ~api_version] calls GET missing_page/[page_hash] + endpoint. *) +val get_missing_page : + hex_root_hash:Hex.t -> api_version:string -> (Dac_node.t, string) RPC_core.t module Coordinator : sig - (** [post_preimage ~payload] sends a [payload] to the DAC + (** [post_preimage ~payload ~api_version] sends a [payload] to the DAC [Coordinator] via a POST RPC call to dac/preimage. It returns a hex encoded root page hash, produced by [Merkle_tree_V0] pagination scheme. On the backend side it also pushes root page hash of the preimage to all the subscribed DAC Members and Observers. *) - val post_preimage : payload:string -> (Dac_node.t, string) RPC_core.t + val post_preimage : + payload:string -> api_version:string -> (Dac_node.t, string) RPC_core.t end diff --git a/tezt/tests/dac.ml b/tezt/tests/dac.ml index d3afe4f6cad160c233f7d181c30391020845dbbb..b9826b0498437b1d069e5ffc6c19248226a3342c 100644 --- a/tezt/tests/dac.ml +++ b/tezt/tests/dac.ml @@ -82,7 +82,11 @@ let init_hex_root_hash ?payload coordinator_node = let* root_hash, _l1_op = RPC.call coordinator_node - (Dac_rpc.post_store_preimage ~payload ~pagination_scheme:"Merkle_tree_V0") + Dac_rpc.( + post_store_preimage + ~payload + ~pagination_scheme:"Merkle_tree_V0" + ~api_version:Api.v0) in let hex_root_hash = `Hex root_hash in return hex_root_hash @@ -126,12 +130,13 @@ let parse_certificate json = (* Helper process that listens to certificate updates through a RPC request. Upon termination, the list of certificate updates is returned *) -let streamed_certificates_client coordinator_node root_hash = +let streamed_certificates_client coordinator_node root_hash api_version = let endpoint = Format.sprintf - "http://%s:%d/monitor/certificate/%s" + "http://%s:%d/%s/monitor/certificate/%s" (Dac_node.rpc_host coordinator_node) (Dac_node.rpc_port coordinator_node) + api_version root_hash in RPC.Curl.get_raw endpoint @@ -593,20 +598,21 @@ let check_preimage expected_preimage actual_preimage = ~error_msg: "Preimage does not match expected value (Current: %L <> Expected: %R)") -(** [check_downloaded_page coordinator observer page_hash] checks that the - [observer] has downloaded a page with [page_hash] from the [coordinator], - that the contents of the page corresponds to the ones of the - [coordinator]. It returns the list of the hashes contained in the - [page_hash], if the page corresponds to a hash page. Otherwise, it returns - the empty list. *) -let check_downloaded_page coordinator observer page_hash = +(** [check_downloaded_page coordinator observer page_hash api_version] checks + that the [observer] has downloaded a page with [page_hash] from the + [coordinator], that the contents of the page corresponds to the ones of the + [coordinator]. It returns the list of the hashes contained in the + [page_hash], if the page corresponds to a hash page. Otherwise, it returns + the empty list. *) +let check_downloaded_page coordinator observer page_hash ~api_version = + (* TODO this function is called by both legacy and non legacy tests. *) let* coordinator_hex_encoded_page = - RPC.call coordinator (Dac_rpc.get_preimage page_hash) + RPC.call coordinator Dac_rpc.(get_preimage page_hash ~api_version) in let coordinator_page = Hex.to_string (`Hex coordinator_hex_encoded_page) in (* Check that the page has been saved by the observer. *) let* observer_hex_encoded_page = - RPC.call observer (Dac_rpc.get_preimage page_hash) + RPC.call observer Dac_rpc.(get_preimage page_hash ~api_version) in let observer_page = Hex.to_string (`Hex observer_hex_encoded_page) in (* Check that the raw page for the root hash stored in the coordinator @@ -642,12 +648,14 @@ let check_downloaded_page coordinator observer page_hash = in return @@ split_hashes concatenated_hashes [] -let check_downloaded_preimage coordinator observer root_hash = +let check_downloaded_preimage coordinator observer root_hash ~api_version = let rec go hashes = match hashes with | [] -> return () | hash :: hashes -> - let* next_hashes = check_downloaded_page coordinator observer hash in + let* next_hashes = + check_downloaded_page coordinator observer hash ~api_version + in go (hashes @ next_hashes) in go [root_hash] @@ -713,9 +721,11 @@ module Legacy = struct let* actual_rh, _l1_operation = RPC.call coordinator - (Dac_rpc.post_store_preimage - ~payload - ~pagination_scheme:"Merkle_tree_V0") + Dac_rpc.( + post_store_preimage + ~payload + ~pagination_scheme:"Merkle_tree_V0" + ~api_version:Api.v0) in return @@ check_valid_root_hash expected_rh actual_rh @@ -833,9 +843,11 @@ module Legacy = struct let* actual_rh, l1_operation = RPC.call dac_node - (Dac_rpc.post_store_preimage - ~payload - ~pagination_scheme:"Merkle_tree_V0") + Dac_rpc.( + post_store_preimage + ~payload + ~pagination_scheme:"Merkle_tree_V0" + ~api_version:Api.v0) in (* Expected reveal hash equals to the result of [Tezos_dac_alpha.Dac_pages_encoding.Merkle_tree.V0.serialize_payload "test"]. @@ -858,7 +870,9 @@ module Legacy = struct in check_preimage payload recovered_preimage ; let* is_signature_valid = - RPC.call dac_node (Dac_rpc.get_verify_signature l1_operation) + RPC.call + dac_node + Dac_rpc.(get_verify_signature l1_operation ~api_version:Api.v0) in Check.( (is_signature_valid = true) @@ -873,9 +887,11 @@ module Legacy = struct let* actual_rh, _l1_operation = RPC.call dac_node - (Dac_rpc.post_store_preimage - ~payload - ~pagination_scheme:"Hash_chain_V0") + Dac_rpc.( + post_store_preimage + ~payload + ~pagination_scheme:"Hash_chain_V0" + ~api_version:Api.v0) in (* Expected reveal hash equals to the result of [Tezos_dac_alpha.Dac_pages_encoding.Hash_chain.V0.serialize_payload "test"]. @@ -905,9 +921,11 @@ module Legacy = struct let* actual_rh, _l1_operation = RPC.call dac_node - (Dac_rpc.post_store_preimage - ~payload - ~pagination_scheme:"Merkle_tree_V0") + Dac_rpc.( + post_store_preimage + ~payload + ~pagination_scheme:"Merkle_tree_V0" + ~api_version:Api.v0) in (* Expected reveal hash equals to the result of [Tezos_dac_alpha.Dac_pages_encoding.Merkle_tree.V0.serialize_payload "test"]. @@ -925,7 +943,9 @@ module Legacy = struct let recovered_payload = really_input_string cin (in_channel_length cin) in let () = close_in cin in let recovered_preimage = Hex.of_string recovered_payload in - let* preimage = RPC.call dac_node (Dac_rpc.get_preimage expected_rh) in + let* preimage = + RPC.call dac_node Dac_rpc.(get_preimage expected_rh ~api_version:Api.v0) + in Check.( (preimage = Hex.show recovered_preimage) string @@ -963,9 +983,11 @@ module Legacy = struct let* actual_rh, _l1_operation = RPC.call dac_node - (Dac_rpc.post_store_preimage - ~payload - ~pagination_scheme:"Hash_chain_V0") + Dac_rpc.( + post_store_preimage + ~payload + ~pagination_scheme:"Hash_chain_V0" + ~api_version:Api.v0) in let expected_rh = "0027782d2a7020be332cc42c4e66592ec50305f559a4011981f1d5af81428e7aa3" @@ -1005,9 +1027,11 @@ module Legacy = struct let _actual_rh = RPC.call dac_node - (Dac_rpc.post_store_preimage - ~payload - ~pagination_scheme:"Hash_chain_V0") + Dac_rpc.( + post_store_preimage + ~payload + ~pagination_scheme:"Hash_chain_V0" + ~api_version:Api.v0) in let errorneous_hash = "0027782d2a7020be332cc42c4e66592ec50305f559a4011981f1d5af81428ecafe" @@ -1265,7 +1289,11 @@ module Legacy = struct let* () = push_promise in (* Assert [observer] emitted event of received [expected_rh]. *) let* () = fetch_root_hash_promise in - check_downloaded_preimage coordinator observer expected_rh + check_downloaded_preimage + coordinator + observer + expected_rh + ~api_version:Dac_rpc.Api.v0 (* 1. Observer should fetch missing page from Coordinator when GET /missing_page/{hash} is called. @@ -1303,17 +1331,25 @@ module Legacy = struct assert_lwt_failure ~__LOC__ "Expected retrieve_preimage" - (RPC.call observer (Dac_rpc.get_preimage (Hex.show hex_root_hash))) + (RPC.call + observer + Dac_rpc.(get_preimage (Hex.show hex_root_hash) ~api_version:Api.v0)) in let* missing_page = - RPC.call observer (Dac_rpc.get_missing_page ~hex_root_hash) + RPC.call + observer + Dac_rpc.(get_missing_page ~hex_root_hash ~api_version:Api.v0) in let* coordinator_page = - RPC.call coordinator (Dac_rpc.get_preimage (Hex.show hex_root_hash)) + RPC.call + coordinator + Dac_rpc.(get_preimage (Hex.show hex_root_hash) ~api_version:Api.v0) in check_preimage coordinator_page missing_page ; let* observer_preimage = - RPC.call observer (Dac_rpc.get_preimage (Hex.show hex_root_hash)) + RPC.call + observer + Dac_rpc.(get_preimage (Hex.show hex_root_hash) ~api_version:Api.v0) in check_preimage coordinator_page observer_preimage ; unit @@ -1328,10 +1364,12 @@ module Legacy = struct let result = RPC.call coordinator_node - (Dac_rpc.put_dac_member_signature - ~hex_root_hash - ~dac_member_pkh:invalid_signer_key.aggregate_public_key_hash - ~signature) + Dac_rpc.( + put_dac_member_signature + ~hex_root_hash + ~dac_member_pkh:invalid_signer_key.aggregate_public_key_hash + ~signature + ~api_version:Api.v0) in assert_lwt_failure ~__LOC__ @@ -1353,10 +1391,12 @@ module Legacy = struct let result = RPC.call coordinator_node - (Dac_rpc.put_dac_member_signature - ~hex_root_hash - ~dac_member_pkh:memberj.aggregate_public_key_hash - ~signature) + Dac_rpc.( + put_dac_member_signature + ~hex_root_hash + ~dac_member_pkh:memberj.aggregate_public_key_hash + ~signature + ~api_version:Api.v0) in assert_lwt_failure ~__LOC__ @@ -1384,17 +1424,21 @@ module Legacy = struct let* () = RPC.call coordinator_node - (Dac_rpc.put_dac_member_signature - ~hex_root_hash - ~dac_member_pkh:member.aggregate_public_key_hash - ~signature) + Dac_rpc.( + put_dac_member_signature + ~hex_root_hash + ~dac_member_pkh:member.aggregate_public_key_hash + ~signature + ~api_version:Api.v0) in return (member :: keys)) (return []) members in let* witnesses, certificate, _root_hash = - RPC.call coordinator_node (Dac_rpc.get_certificate ~hex_root_hash) + RPC.call + coordinator_node + Dac_rpc.(get_certificate ~hex_root_hash ~api_version:Api.v0) in assert_witnesses ~__LOC__ 3 witnesses ; assert_verify_aggregate_signature members_keys hex_root_hash certificate ; @@ -1412,15 +1456,19 @@ module Legacy = struct let call () = RPC.call coordinator_node - (Dac_rpc.put_dac_member_signature - ~hex_root_hash - ~dac_member_pkh - ~signature) + Dac_rpc.( + put_dac_member_signature + ~hex_root_hash + ~dac_member_pkh + ~signature + ~api_version:Api.v0) in let* () = call () in let* () = call () in let* witnesses, certificate, _root_hash = - RPC.call coordinator_node (Dac_rpc.get_certificate ~hex_root_hash) + RPC.call + coordinator_node + Dac_rpc.(get_certificate ~hex_root_hash ~api_version:Api.v0) in assert_witnesses ~__LOC__ 4 witnesses ; assert_verify_aggregate_signature [member] hex_root_hash certificate ; @@ -1439,10 +1487,12 @@ module Legacy = struct let result = RPC.call coordinator_node - (Dac_rpc.put_dac_member_signature - ~hex_root_hash:false_root_hash - ~dac_member_pkh - ~signature) + Dac_rpc.( + put_dac_member_signature + ~hex_root_hash:false_root_hash + ~dac_member_pkh + ~signature + ~api_version:Api.v0) in assert_lwt_failure ~__LOC__ @@ -1480,25 +1530,132 @@ module Legacy = struct let* () = RPC.call coordinator - (Dac_rpc.put_dac_member_signature - ~hex_root_hash - ~dac_member_pkh:member.aggregate_public_key_hash - ~signature) + Dac_rpc.( + put_dac_member_signature + ~hex_root_hash + ~dac_member_pkh:member.aggregate_public_key_hash + ~signature + ~api_version:Api.v0) in let* witnesses, certificate, _root_hash = - RPC.call coordinator (Dac_rpc.get_certificate ~hex_root_hash) + RPC.call + coordinator + Dac_rpc.(get_certificate ~hex_root_hash ~api_version:Api.v0) in let expected_witnesses = Z.shift_left Z.one i in assert_witnesses ~__LOC__ (Z.to_int expected_witnesses) witnesses ; assert_verify_aggregate_signature [member] hex_root_hash certificate ; unit end + + module Api_versioning = struct + (** [test_legacy_mode_does_not_support_v1] tests that dac node running in + the [Legacy] mode does not support [Api.v1]. *) + let test_legacy_mode_does_not_support_v1 _protocol node client coordinator + threshold dac_committee = + (* Unfortunatelly, validation that [Legacy] mode only supports [v0] + API happens as part of the handlers inside [RPC_server]. This means, + that it is not sufficient to simply ping the endpoints with dummy data, + as deserialization of RPC arguments and input parameters happens before + the actual API validation. *) + let expected_root_hash = + "00a3703854279d2f377d689163d1ec911a840d84b56c4c6f6cafdf0610394df7c6" + in + let root_hash_stream_promise = + wait_for_root_hash_pushed_to_data_streamer + coordinator + expected_root_hash + in + let* hex_root_hash = init_hex_root_hash ~payload:"test" coordinator in + assert (expected_root_hash = Hex.show hex_root_hash) ; + let* () = root_hash_stream_promise in + (* [observer] node is needeed to test "GET /v1/missing_page". The reason + is that this endpoint triggers validation that coordinator of the + current node is specified in the the configuration file. + Unfortunatelly, this validation happens before the actual validation + of the appropriate API version, thus the boilerplate inside this test. + *) + let observer = + Dac_node.create_legacy + ~name:"Observer" + ~threshold + ~committee_members: + (List.map + (fun (dc : Account.aggregate_key) -> + dc.aggregate_public_key_hash) + dac_committee) + ~node + ~client + () + in + let* _ = Dac_node.init_config observer in + let () = set_coordinator observer coordinator in + let* () = Dac_node.run observer in + assert (List.length dac_committee > 0) ; + let member_key : Account.aggregate_key = List.nth dac_committee 0 in + let dac_member_pkh = member_key.aggregate_public_key_hash in + let member_signature = bls_sign_hex_hash member_key hex_root_hash in + (* Test starts here.*) + (* We call endpoints supported by [Legacy] mode with [v1] API version. + Since [Legacy] mode does not support [v1] API, we assert that each one + of the responses returns 404 (not found) code. Additionally, we also + call endpoint only supported by [Coordinator] mode with both [v0] and + [v1] API version and assert both return not found response. *) + let invalid_legacy_mode_calls = + [ + (* GET /v1/preimage *) + RPC.call_raw + coordinator + Dac_rpc.(get_preimage (Hex.show hex_root_hash) ~api_version:Api.v1); + (* GET /v1/verify_signature *) + RPC.call_raw + coordinator + Dac_rpc.(get_verify_signature "dummy signature" ~api_version:Api.v1); + (* GET /v1/missing_page *) + RPC.call_raw + observer + Dac_rpc.(get_missing_page ~hex_root_hash ~api_version:Api.v1); + (* PUT /v1/dac_member_signature *) + RPC.call_raw + coordinator + Dac_rpc.( + put_dac_member_signature + ~hex_root_hash + ~dac_member_pkh + ~signature:member_signature + ~api_version:Api.v1); + (* GET /v1/certificates *) + RPC.call_raw + coordinator + Dac_rpc.(get_certificate ~hex_root_hash ~api_version:Api.v1); + (* [Legacy] mode fails calling [Coordinator]'s mode specific + PUT v0/preimage*) + RPC.call_raw + coordinator + Dac_rpc.( + Coordinator.post_preimage ~payload:"Test" ~api_version:Api.v0); + (* [Legacy] mode fails calling [Coordinator]'s mode specific + PUT v1/preimage*) + RPC.call_raw + coordinator + Dac_rpc.( + Coordinator.post_preimage ~payload:"Test" ~api_version:Api.v1); + ] + in + Lwt_list.iter_s + (fun invalid_response -> + let* response = invalid_response in + return @@ RPC.check_string_response ~code:404 response) + invalid_legacy_mode_calls + end end module Full_infrastructure = struct let coordinator_serializes_payload coordinator ~payload ~expected_rh = let* actual_rh = - RPC.call coordinator (Dac_rpc.Coordinator.post_preimage ~payload) + RPC.call + coordinator + Dac_rpc.(Coordinator.post_preimage ~payload ~api_version:Api.v1) in return @@ check_valid_root_hash expected_rh actual_rh @@ -1515,7 +1672,9 @@ module Full_infrastructure = struct wait_for_root_hash_pushed_to_data_streamer coordinator_node expected_rh in let* actual_rh = - RPC.call coordinator_node (Dac_rpc.Coordinator.post_preimage ~payload) + RPC.call + coordinator_node + Dac_rpc.(Coordinator.post_preimage ~payload ~api_version:Api.v1) in let () = check_valid_root_hash expected_rh actual_rh in let* () = root_hash_pushed_to_data_streamer_promise in @@ -1598,7 +1757,11 @@ module Full_infrastructure = struct This might be inefficient *) Lwt_list.iter_s (fun dac_node -> - check_downloaded_preimage coordinator_node dac_node expected_rh) + check_downloaded_preimage + coordinator_node + dac_node + expected_rh + ~api_version:Dac_rpc.Api.v1) (committee_members_nodes @ observer_nodes) let test_streaming_certificates @@ -1627,7 +1790,11 @@ module Full_infrastructure = struct assert (List.length committee_members > 0) ; let payload, expected_rh = sample_payload "preimage" in let certificate_stream_client = - Runnable.run @@ streamed_certificates_client coordinator_node expected_rh + Runnable.run + @@ streamed_certificates_client + coordinator_node + expected_rh + Dac_rpc.Api.v1 in let push_promise = wait_for_root_hash_pushed_to_data_streamer coordinator_node expected_rh @@ -1696,14 +1863,19 @@ module Full_infrastructure = struct let* get_certificate = RPC.call coordinator_node - (Dac_rpc.get_certificate ~hex_root_hash:(`Hex expected_rh)) + Dac_rpc.( + get_certificate ~hex_root_hash:(`Hex expected_rh) ~api_version:Api.v1) in check_certificate get_certificate last_certificate_update ; (* 6. Request certificate via streamed endpoints again, check that one item is returned with the same certificate returned by the GET endpoint. *) let* second_certificates_stream = - Runnable.run @@ streamed_certificates_client coordinator_node expected_rh + Runnable.run + @@ streamed_certificates_client + coordinator_node + expected_rh + Dac_rpc.Api.v1 in Check.( (1 = List.length second_certificates_stream) @@ -1718,6 +1890,30 @@ module Full_infrastructure = struct in check_certificate get_certificate certificate_update_from_second_stream ; return () + + module Api = struct + (** Test that [V0] API specific endpoints are not supported in + [V1] API. I.e. [V1] no longer support "POST /store_preimage" and + "GET /verify_signature". *) + let test_v1_does_not_support_v0_specific_endpoints + Scenarios.{coordinator_node; _} = + let* response = + RPC.call_raw + coordinator_node + Dac_rpc.( + post_store_preimage + ~payload:"test" + ~pagination_scheme:"Merkle_tree_V0" + ~api_version:Api.v1) + in + let () = RPC.check_string_response ~code:404 response in + let* response = + RPC.call_raw + coordinator_node + Dac_rpc.(get_verify_signature "test" ~api_version:Dac_rpc.Api.v1) + in + return @@ RPC.check_string_response ~code:404 response + end end let register ~protocols = @@ -1795,6 +1991,13 @@ let register ~protocols = "dac_store_member_signature" Legacy.Signature_manager.test_handle_store_signature protocols ; + scenario_with_layer1_and_legacy_dac_nodes + ~threshold:0 + ~committee_members:1 + ~tags:["dac"; "dac_node"] + "dac_legacy_mode_only_supports_v0" + Legacy.Api_versioning.test_legacy_mode_does_not_support_v1 + protocols ; scenario_with_full_dac_infrastructure ~observers:0 ~committee_members:0 @@ -1822,4 +2025,11 @@ let register ~protocols = ~tags:["dac"; "dac_node"] "certificates are updated in streaming endpoint" Full_infrastructure.test_streaming_certificates + protocols ; + scenario_with_full_dac_infrastructure + ~observers:0 + ~committee_members:0 + ~tags:["dac"; "dac_node"] + "v1 does not support v0 specific endpoints" + Full_infrastructure.Api.test_v1_does_not_support_v0_specific_endpoints protocols