diff --git a/manifest/main.ml b/manifest/main.ml index 0faa002567c0ff278ba19d7b759eee820d5a76e9..4428b0d3efa3e60a41b31c581fe017a12871a225 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -3295,6 +3295,7 @@ let octez_dac_node_lib = octez_protocol_updater |> open_; octez_client_base_unix |> open_; octez_stdlib_unix |> open_; + octez_rpc_http_server; ] let _octez_dac_node_lib_tests = @@ -5186,7 +5187,6 @@ module Protocol = Protocol octez_dac_node_lib |> open_; client |> if_some |> open_; embedded |> open_; - layer2_utils |> if_some |> open_; main |> open_; ] ~inline_tests:ppx_expect diff --git a/opam/tezos-dac-alpha.opam b/opam/tezos-dac-alpha.opam index d3dff693e506008491e9c263b183f8eea8602bc1..e5feb6cfd0a86225e83a24ad29068e5b715d5123 100644 --- a/opam/tezos-dac-alpha.opam +++ b/opam/tezos-dac-alpha.opam @@ -17,7 +17,6 @@ depends: [ "tezos-dac-node-lib" "tezos-client-alpha" "tezos-embedded-protocol-alpha" - "tezos-layer2-utils-alpha" "tezos-protocol-alpha" "tezos-base-test-helpers" {with-test} "tezos-alpha-test-helpers" {with-test} diff --git a/opam/tezos-dac-node-lib.opam b/opam/tezos-dac-node-lib.opam index 34938fcacd8ed5f58353874bd6bd22cd93b03591..dd3ad2e5de0562e25973b530bcbb20d6bbcbd89a 100644 --- a/opam/tezos-dac-node-lib.opam +++ b/opam/tezos-dac-node-lib.opam @@ -15,6 +15,7 @@ depends: [ "tezos-protocol-updater" "tezos-client-base-unix" "tezos-stdlib-unix" + "tezos-rpc-http-server" ] build: [ ["rm" "-r" "vendors"] diff --git a/src/lib_dac_node/RPC_server.ml b/src/lib_dac_node/RPC_server.ml new file mode 100644 index 0000000000000000000000000000000000000000..00137e6fddaef04dfc1c1602eae27ba06577831b --- /dev/null +++ b/src/lib_dac_node/RPC_server.ml @@ -0,0 +1,198 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022-2023 Trili Tech *) +(* Copyright (c) 2022 Nomadic Labs *) +(* 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. *) +(* *) +(*****************************************************************************) + +open Tezos_rpc_http +open Tezos_rpc_http_server + +type error += + | Cannot_construct_external_message + | Cannot_deserialize_external_message + +let () = + register_error_kind + `Permanent + ~id:"dac_cannot_construct_external_message" + ~title:"External rollup message could not be constructed" + ~description:"External rollup message could not be constructed" + ~pp:(fun ppf () -> + Format.fprintf ppf "External rollup message could not be constructed") + Data_encoding.unit + (function Cannot_construct_external_message -> Some () | _ -> None) + (fun () -> Cannot_construct_external_message) ; + register_error_kind + `Permanent + ~id:"dac_cannot_deserialize_rollup_external_message" + ~title:"External rollup message could not be deserialized" + ~description:"External rollup message could not be deserialized" + ~pp:(fun ppf () -> + Format.fprintf ppf "External rollup message could not be deserialized") + Data_encoding.unit + (function Cannot_deserialize_external_message -> Some () | _ -> None) + (fun () -> Cannot_deserialize_external_message) + +let add_service registerer service handler directory = + registerer directory service handler + +let handle_serialize_dac_store_preimage dac_plugin cctxt dac_sk_uris page_store + (data, pagination_scheme) = + let open Lwt_result_syntax in + let open Pages_encoding in + let* root_hash = + match pagination_scheme with + | Merkle_tree_V0 -> + Merkle_tree.V0.serialize_payload dac_plugin ~page_store data + | 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.sign_root_hash dac_plugin cctxt dac_sk_uris 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 @@ (root_hash, external_message) + | Error _ -> tzfail @@ Cannot_construct_external_message + +let handle_verify_external_message_signature dac_plugin public_keys_opt + encoded_l1_message = + let open Lwt_result_syntax 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 + let ((module P) : Dac_plugin.t) = dac_plugin in + External_message.Default.of_bytes P.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 + root_hash + signature + witnesses + +let handle_retrieve_preimage dac_plugin page_store hash = + Page_store.Filesystem.load dac_plugin page_store ~hash + +let register_serialize_dac_store_preimage ctx cctxt dac_sk_uris page_store + directory = + directory + |> add_service + Tezos_rpc.Directory.register0 + (RPC_services.dac_store_preimage ctx) + (fun () input -> + handle_serialize_dac_store_preimage + ctx + cctxt + dac_sk_uris + page_store + input) + +let register_verify_external_message_signature ctx public_keys_opt directory = + directory + |> add_service + Tezos_rpc.Directory.register0 + RPC_services.verify_external_message_signature + (fun external_message () -> + handle_verify_external_message_signature + ctx + public_keys_opt + external_message) + +let register_retrieve_preimage dac_plugin page_store = + add_service + Tezos_rpc.Directory.register1 + (RPC_services.retrieve_preimage dac_plugin) + (fun hash () () -> handle_retrieve_preimage dac_plugin page_store hash) + +let register dac_plugin reveal_data_dir cctxt dac_public_keys_opt dac_sk_uris = + let page_store = Page_store.Filesystem.init reveal_data_dir in + Tezos_rpc.Directory.empty + |> register_serialize_dac_store_preimage + dac_plugin + cctxt + dac_sk_uris + page_store + |> register_verify_external_message_signature dac_plugin dac_public_keys_opt + |> register_retrieve_preimage dac_plugin page_store + +(* TODO: https://gitlab.com/tezos/tezos/-/issues/4750 + Move this to RPC_server.Legacy once all operating modes are supported. *) +let start_legacy ~rpc_address ~rpc_port ~reveal_data_dir ~threshold cctxt ctxt + dac_pks_opt dac_sk_uris = + let open Lwt_syntax in + let dir = + Tezos_rpc.Directory.register_dynamic_directory + Tezos_rpc.Directory.empty + Tezos_rpc.Path.open_root + (fun () -> + match Node_context.get_status ctxt with + | Ready {dac_plugin = (module Dac_plugin); _} -> + let _threshold = threshold in + Lwt.return + (register + (module Dac_plugin) + reveal_data_dir + cctxt + dac_pks_opt + dac_sk_uris) + | Starting -> Lwt.return Tezos_rpc.Directory.empty) + in + let rpc_address = P2p_addr.of_string_exn rpc_address in + let host = Ipaddr.V6.to_string rpc_address in + let node = `TCP (`Port rpc_port) in + let acl = RPC_server.Acl.default rpc_address in + let server = + RPC_server.init_server dir ~acl ~media_types:Media_type.all_media_types + in + Lwt.catch + (fun () -> + let* () = + RPC_server.launch + ~host + server + ~callback:(RPC_server.resto_callback server) + node + in + return_ok server) + fail_with_exn + +let shutdown = RPC_server.shutdown + +let install_finalizer rpc_server = + let open Lwt_syntax in + Lwt_exit.register_clean_up_callback ~loc:__LOC__ @@ fun exit_status -> + let* () = shutdown rpc_server in + let* () = Event.(emit shutdown_node exit_status) in + Tezos_base_unix.Internal_event_unix.close () diff --git a/src/lib_dac_node/RPC_services.ml b/src/lib_dac_node/RPC_services.ml new file mode 100644 index 0000000000000000000000000000000000000000..3af3feee3b917b3b48dc84ef76452761937ef598 --- /dev/null +++ b/src/lib_dac_node/RPC_services.ml @@ -0,0 +1,83 @@ +(*****************************************************************************) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(* A variant of [Sc_rollup_reveal_hash.encoding] that prefers hex + encoding over b58check encoding for JSON. *) +let root_hash_encoding ((module P) : Dac_plugin.t) = + let binary = P.encoding in + Data_encoding.( + splitted + ~binary + ~json: + (conv_with_guard + P.to_hex + (fun str -> + Result.of_option ~error:"Not a valid hash" (P.of_hex str)) + (string' Plain))) + +let store_preimage_request_encoding = + let pagination_scheme_encoding = Pages_encoding.pagination_scheme_encoding in + Data_encoding.( + obj2 + (req "payload" Data_encoding.(bytes' Hex)) + (req "pagination_scheme" pagination_scheme_encoding)) + +let store_preimage_response_encoding ctx = + Data_encoding.( + obj2 + (req "root_hash" (root_hash_encoding ctx)) + (req "external_message" (bytes' Hex))) + +let external_message_query = + let open Tezos_rpc.Query in + query (fun hex_string -> hex_string) + |+ opt_field "external_message" Tezos_rpc.Arg.string (fun s -> s) + |> seal + +let dac_store_preimage ctx = + Tezos_rpc.Service.put_service + ~description:"Split DAC reveal data" + ~query:Tezos_rpc.Query.empty + ~input:store_preimage_request_encoding + ~output:(store_preimage_response_encoding ctx) + Tezos_rpc.Path.(open_root / "store_preimage") + +(* DAC/FIXME: https://gitlab.com/tezos/tezos/-/issues/4263 + remove this endpoint once end-to-end tests are in place. *) +let verify_external_message_signature : + ([`GET], unit, unit, string option, unit, bool) Tezos_rpc.Service.service = + Tezos_rpc.Service.get_service + ~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") + +let retrieve_preimage ((module P) : Dac_plugin.t) = + 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" /: P.hash_rpc_arg) diff --git a/src/bin_dac_node/configuration.ml b/src/lib_dac_node/configuration.ml similarity index 100% rename from src/bin_dac_node/configuration.ml rename to src/lib_dac_node/configuration.ml diff --git a/src/bin_dac_node/configuration.mli b/src/lib_dac_node/configuration.mli similarity index 100% rename from src/bin_dac_node/configuration.mli rename to src/lib_dac_node/configuration.mli diff --git a/src/bin_dac_node/dac_manager.ml b/src/lib_dac_node/dac_manager.ml similarity index 100% rename from src/bin_dac_node/dac_manager.ml rename to src/lib_dac_node/dac_manager.ml diff --git a/src/bin_dac_node/dac_manager.mli b/src/lib_dac_node/dac_manager.mli similarity index 100% rename from src/bin_dac_node/dac_manager.mli rename to src/lib_dac_node/dac_manager.mli diff --git a/src/lib_dac_node/dac_plugin.ml b/src/lib_dac_node/dac_plugin.ml index 262909b471188fbfaf281668eaa9f1911bdface5..728df19025a7921708307b56cb4b617259792954 100644 --- a/src/lib_dac_node/dac_plugin.ml +++ b/src/lib_dac_node/dac_plugin.ml @@ -23,33 +23,38 @@ (* *) (*****************************************************************************) -module Dac_hash = struct - type t = bytes +type hash = bytes - let to_bytes = Fun.id -end +let hash_to_bytes = Fun.id + +type supported_hashes = Blake2B module type T = sig - module Dac_hash : sig - val encoding : Dac_hash.t Data_encoding.t - end + val encoding : hash Data_encoding.t - module Proto : Registered_protocol.T + val hash_string : + scheme:supported_hashes -> ?key:string -> string list -> hash + + val hash_bytes : scheme:supported_hashes -> ?key:bytes -> bytes list -> hash + + val scheme_of_hash : hash -> supported_hashes + + val of_hex : string -> hash option - module RPC : sig - val rpc_services : - reveal_data_dir:string -> - #Client_context.wallet -> - Tezos_crypto.Aggregate_signature.public_key option list -> - Client_keys.aggregate_sk_uri option list -> - int -> - unit Tezos_rpc.Directory.directory - end + val to_hex : hash -> string + + val size : scheme:supported_hashes -> int + + val hash_rpc_arg : hash Tezos_rpc.Arg.arg + + module Proto : Registered_protocol.T end -let table : (module T) Protocol_hash.Table.t = Protocol_hash.Table.create 5 +type t = (module T) + +let table : t Protocol_hash.Table.t = Protocol_hash.Table.create 5 -let register (make_plugin : (bytes -> Dac_hash.t) -> (module T)) : unit = +let register (make_plugin : (bytes -> hash) -> t) : unit = let dac_plugin = make_plugin Fun.id in let module Plugin = (val dac_plugin) in assert (not (Protocol_hash.Table.mem table Plugin.Proto.hash)) ; diff --git a/src/lib_dac_node/dac_plugin.mli b/src/lib_dac_node/dac_plugin.mli index 794aabba2f4256095d83f6b909a7e35c5cd97e8e..97e4af331ad005db810ed0496bc61804726de904 100644 --- a/src/lib_dac_node/dac_plugin.mli +++ b/src/lib_dac_node/dac_plugin.mli @@ -23,43 +23,63 @@ (* *) (*****************************************************************************) -(** [Protocol.Sc_protocol_reveal_hash.t] is unknown to modules outside the +(** [Protocol.Sc_protocol_reveal_hash.t] is unknown to modules outside the protocol and only known at runtime. To avoid the proliferation of functors - in the dac node, [Dac_hash.t] hides the dynamic [Protocol.Sc_protocol_reveal_hash.t] - behind an abstract static type. An instance of [Dac_plugin.T] implement - the [Dac_plugin.Dac_hash] which are the functions that can operate on [Dac_hash.t]. + in the dac node, [hash] hides the dynamic [Protocol.Sc_protocol_reveal_hash.t] + behind an abstract static type. An instance of [Dac_plugin.T] behaviour + of operations on [hash]. *) -module Dac_hash : sig - type t - val to_bytes : t -> bytes -end +type hash + +val hash_to_bytes : hash -> bytes + +(** FIXME: https://gitlab.com/tezos/tezos/-/issues/4856 + Fix static supported_hashes type *) +type supported_hashes = Blake2B module type T = sig - (** [Dac_hash.t] operations that need to be derived from the protocol *) - module Dac_hash : sig - val encoding : Dac_hash.t Data_encoding.t - end + (** The encoding of reveal hashes. *) + val encoding : hash Data_encoding.t - module Proto : Registered_protocol.T + (** [hash_string ~scheme ?key strings] hashes [strings] using the + supported hashing [scheme] given in input. *) + val hash_string : + scheme:supported_hashes -> ?key:string -> string list -> hash + + (** [hash_bytes ~scheme ?key strings] hashes [bytes] using the + supported hashing [scheme] given in input. *) + val hash_bytes : scheme:supported_hashes -> ?key:bytes -> bytes list -> hash + + (** [scheme_of_hash] hash returns the supported hashing scheme + that was used to obtain [hash]. *) + val scheme_of_hash : hash -> supported_hashes - module RPC : sig - val rpc_services : - reveal_data_dir:string -> - #Client_context.wallet -> - Tezos_crypto.Aggregate_signature.public_key option list -> - Client_keys.aggregate_sk_uri option list -> - int -> - unit Tezos_rpc.Directory.directory - end + (** [of_hex hex] decodes a hex into hash. *) + val of_hex : string -> hash option + + (** [to_hex hash] encodes hash into hex. *) + val to_hex : hash -> string + + (** [size ~scheme] returns the size of reveal hashes using the [scheme] + specified in input. *) + val size : scheme:supported_hashes -> int + + (** Hash argument definition for RPC *) + val hash_rpc_arg : hash Tezos_rpc.Arg.arg + + module Proto : Registered_protocol.T end -(** [register make_plugin] derives and registers a new [Dac_plugin.T] given an - [of_bytes]. Implementers of plugin are responsible for providing the - definition of this derivation. Functions that expose - [Protocol.Sc_protocol_reveal_hash.t] can be wrapped into [Dac_hash.t] via +(** Dac plugin module type *) +type t = (module T) + +(** [register make_plugin] derives and registers a new [Dac_plugin.T] given an + [of_bytes]. Implementers of plugin are responsible for providing the + definition of this derivation. Functions that expose + [Protocol.Sc_protocol_reveal_hash.t] can be wrapped into [hash] via [Dac_hash.to_bytes] and [of_bytes]. *) -val register : ((bytes -> Dac_hash.t) -> (module T)) -> unit +val register : ((bytes -> hash) -> (module T)) -> unit val get : Protocol_hash.Table.key -> (module T) option diff --git a/src/bin_dac_node/daemon.ml b/src/lib_dac_node/daemon.ml similarity index 100% rename from src/bin_dac_node/daemon.ml rename to src/lib_dac_node/daemon.ml diff --git a/src/lib_dac_node/dune b/src/lib_dac_node/dune index 6f84f91dad8653803a49cbd3812c711c8ad0d8ed..28cf020145f305b5b2ef89f7aece0d3497837ae4 100644 --- a/src/lib_dac_node/dune +++ b/src/lib_dac_node/dune @@ -11,7 +11,8 @@ tezos-client-base tezos-protocol-updater tezos-client-base-unix - tezos-stdlib-unix) + tezos-stdlib-unix + tezos-rpc-http-server) (flags (:standard) -open Tezos_base.TzPervasives diff --git a/src/bin_dac_node/event.ml b/src/lib_dac_node/event.ml similarity index 100% rename from src/bin_dac_node/event.ml rename to src/lib_dac_node/event.ml diff --git a/src/proto_alpha/lib_dac/dac_external_message_manager.ml b/src/lib_dac_node/external_message.ml similarity index 68% rename from src/proto_alpha/lib_dac/dac_external_message_manager.ml rename to src/lib_dac_node/external_message.ml index 56b6557e296d50b28dc49bef8ba6de64497d8080..c85fbb3537291fb463f517672a854cc3e1944990 100644 --- a/src/proto_alpha/lib_dac/dac_external_message_manager.ml +++ b/src/lib_dac_node/external_message.ml @@ -1,7 +1,7 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2022 Trili Tech *) +(* Copyright (c) 2022-2023 Trili Tech *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -23,9 +23,6 @@ (* *) (*****************************************************************************) -open Protocol -open Environment.Error_monad - type error += Could_not_serialize_rollup_external_message of string let () = @@ -48,36 +45,35 @@ let () = | _ -> None) (fun b58_hash -> Could_not_serialize_rollup_external_message b58_hash) -module type REVEAL_HASH = module type of Sc_rollup_reveal_hash +type t = { + root_hash : Dac_plugin.hash; + signature : Tezos_crypto.Aggregate_signature.signature; + witnesses : Z.t; + (** TODO: https://gitlab.com/tezos/tezos/-/issues/4853 + Switch to BitSet.t which is ideal but it is not exposed outside + the Protocol. *) +} -module Make - (Hashing_scheme : REVEAL_HASH) (Encoding_metadata : sig - val tag : int +module Make (Encoding_metadata : sig + val tag : int - val title : string - end) = + val title : string +end) = struct - type dac_message = - | Dac_message of { - root_hash : Hashing_scheme.t; - signature : Tezos_crypto.Aggregate_signature.t; - witnesses : Bitset.t; - } - - let untagged_encoding = + let untagged_encoding dac_hash_encoding = Data_encoding.( conv (function - | Dac_message {root_hash; signature; witnesses} -> + | {root_hash; signature; witnesses} -> (root_hash, signature, witnesses)) (fun (root_hash, signature, witnesses) -> - Dac_message {root_hash; signature; witnesses}) + {root_hash; signature; witnesses}) (obj3 - (req "root_hash" Hashing_scheme.encoding) + (req "root_hash" dac_hash_encoding) (req "signature" Tezos_crypto.Aggregate_signature.encoding) - (req "witnesses" Bitset.encoding))) + (req "witnesses" z))) - let dac_message_encoding = + let dac_message_encoding encoding = Data_encoding.( union ~tag_size:`Uint8 @@ -85,30 +81,33 @@ struct case ~title:("dac_message_" ^ Encoding_metadata.title) (Tag Encoding_metadata.tag) - untagged_encoding + (untagged_encoding encoding) (fun msg -> Some msg) (fun msg -> msg); ]) - let make root_hash signature witnesses = - let message = Dac_message {root_hash; signature; witnesses} in - let res = Data_encoding.Binary.to_bytes dac_message_encoding message in + let make ((module P) : Dac_plugin.t) root_hash signature witnesses = + let open Lwt_result_syntax in + let message = {root_hash; signature; witnesses} in + let res = + Data_encoding.Binary.to_bytes (dac_message_encoding P.encoding) message + in match res with - | Ok bytes -> Ok bytes + | Ok bytes -> return bytes | Error _ -> - error - @@ Could_not_serialize_rollup_external_message - (Hashing_scheme.to_hex root_hash) + tzfail + @@ Could_not_serialize_rollup_external_message (P.to_hex root_hash) - let of_bytes encoded_message = - Data_encoding.Binary.of_bytes_opt dac_message_encoding encoded_message + let of_bytes dac_hash_encoding encoded_message = + Data_encoding.Binary.of_bytes_opt + (dac_message_encoding dac_hash_encoding) + encoded_message end -module Reveal_hash = - Make - (Sc_rollup_reveal_hash) - (struct - let tag = 42 +module Default = Make (struct + (** TODO: https://gitlab.com/tezos/tezos/-/issues/4854 + Use a more ideal tag. *) + let tag = 42 - let title = "reveal_hash_v0" - end) + let title = "reveal_hash_v0" +end) diff --git a/src/proto_alpha/lib_dac/dac_manager.ml b/src/lib_dac_node/external_message.mli similarity index 69% rename from src/proto_alpha/lib_dac/dac_manager.ml rename to src/lib_dac_node/external_message.mli index 40fcb467009a005ef11f2b240cf478c545034f06..1237f2c4a9bb81f0e762ceda302e2ac40959225f 100644 --- a/src/proto_alpha/lib_dac/dac_manager.ml +++ b/src/lib_dac_node/external_message.mli @@ -1,7 +1,7 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2022 Trili Tech *) +(* Copyright (c) 2022-2023 Trili Tech *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -23,8 +23,29 @@ (* *) (*****************************************************************************) -module Reveal_hash = struct - module Storage = Page_store.Filesystem - module Signatures = Dac_signature_manager.Reveal_hash - module External_message = Dac_external_message_manager.Reveal_hash +(** External Message module. *) + +type error += Could_not_serialize_rollup_external_message of string + +type t = { + root_hash : Dac_plugin.hash; + signature : Tezos_crypto.Aggregate_signature.signature; + witnesses : Z.t; +} + +(** Default implementation. *) +module Default : sig + (** [make dac_plugin root_hash signature witnesses] returns the L1 external message + as a byte sequence of the form [tag] ^ [root_hash] ^ [signature] ^ [witnesses]. + *) + val make : + Dac_plugin.t -> + Dac_plugin.hash -> + Tezos_crypto.Aggregate_signature.signature -> + Z.t -> + bytes tzresult Lwt.t + + (** [of_bytes dac_hash_encoding payload] deserializes [payload] into a + [dac_message]. *) + val of_bytes : Dac_plugin.hash Data_encoding.t -> bytes -> t option end diff --git a/src/bin_dac_node/node_context.ml b/src/lib_dac_node/node_context.ml similarity index 96% rename from src/bin_dac_node/node_context.ml rename to src/lib_dac_node/node_context.ml index f306f4814d41ce87e332cb3a368120be5f5a81c0..bb416dbc6911e46242fd4155e01c400b0608a733 100644 --- a/src/bin_dac_node/node_context.ml +++ b/src/lib_dac_node/node_context.ml @@ -77,8 +77,8 @@ let get_status ctxt = ctxt.status let get_tezos_node_cctxt ctxt = ctxt.tezos_node_cctxt -let get_dac_hash_encoding ctxt = +let get_dac_plugin ctxt = let open Result_syntax in match ctxt.status with - | Ready {dac_plugin = (module Dac_plugin)} -> Ok Dac_plugin.Dac_hash.encoding + | Ready {dac_plugin} -> Ok dac_plugin | Starting -> tzfail Node_not_ready diff --git a/src/bin_dac_node/node_context.mli b/src/lib_dac_node/node_context.mli similarity index 94% rename from src/bin_dac_node/node_context.mli rename to src/lib_dac_node/node_context.mli index 278048961a9e19393e9b385b216b5f3a043840fd..1a039db028dd58db07a862ff20cf65f0935ab134 100644 --- a/src/bin_dac_node/node_context.mli +++ b/src/lib_dac_node/node_context.mli @@ -66,6 +66,6 @@ val get_status : t -> status (** [get_tezos_node_cctxt ctxt] returns the Tezos node's client context. *) val get_tezos_node_cctxt : t -> Client_context.full -(** [get_dac_hash_encoding ctxt] returns the [Dac_hash.t] encoding derived from - [Dac_plugin.Protocol_reveal_hash.encoding] *) -val get_dac_hash_encoding : t -> Dac_plugin.Dac_hash.t Data_encoding.t tzresult +(** [get_dac_plugin ctxt] returns the [Dac_plugin.t] used in the node context. + Fails with [Node_not_ready] when [Node_context.status] is not [Ready]. *) +val get_dac_plugin : t -> Dac_plugin.t tzresult diff --git a/src/proto_alpha/lib_dac/page_store.ml b/src/lib_dac_node/page_store.ml similarity index 77% rename from src/proto_alpha/lib_dac/page_store.ml rename to src/lib_dac_node/page_store.ml index a7658b3cca85d2d3d7c00f958bf00e85bff60b38..56c543cede4f5c576aff2dc9acadc29dbb8eec43 100644 --- a/src/proto_alpha/lib_dac/page_store.ml +++ b/src/lib_dac_node/page_store.ml @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2023 Marigold *) +(* Copyright (c) 2023 Trili Tech *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -23,15 +24,9 @@ (* *) (*****************************************************************************) -open Protocol -open Environment.Error_monad - type error += - | Cannot_write_page_to_page_storage of { - hash : Sc_rollup_reveal_hash.t; - content : bytes; - } - | Cannot_read_page_from_page_storage of Sc_rollup_reveal_hash.t + | Cannot_write_page_to_page_storage of {hash : string; content : bytes} + | Cannot_read_page_from_page_storage of string let () = register_error_kind @@ -44,10 +39,9 @@ let () = ppf "Unable to write the following DAC page to the page storage: {key=%s; \ content=%s}" - (Sc_rollup_reveal_hash.to_hex key) + key (Bytes.to_string content)) - Data_encoding.( - obj2 (req "key" Sc_rollup_reveal_hash.encoding) (req "content" bytes)) + Data_encoding.(obj2 (req "key" Data_encoding.string) (req "content" bytes)) (function | Cannot_write_page_to_page_storage {hash; content} -> Some (hash, content) | _ -> None) @@ -61,8 +55,8 @@ let () = Format.fprintf ppf "Unable to read DAC page with {key=%s} from the page storage" - (Sc_rollup_reveal_hash.to_hex key)) - Data_encoding.(obj1 (req "hash" Sc_rollup_reveal_hash.encoding)) + key) + Data_encoding.(obj1 (req "hash" Data_encoding.string)) (function | Cannot_read_page_from_page_storage hash -> Some hash | _ -> None) (fun hash -> Cannot_read_page_from_page_storage hash) @@ -70,50 +64,53 @@ let () = module type S = sig type t - type hash - type configuration val init : configuration -> t - val save : t -> hash:hash -> content:bytes -> unit tzresult Lwt.t + val save : + Dac_plugin.t -> + t -> + hash:Dac_plugin.hash -> + content:bytes -> + unit tzresult Lwt.t - val load : t -> hash:hash -> bytes tzresult Lwt.t + val load : Dac_plugin.t -> t -> hash:Dac_plugin.hash -> bytes tzresult Lwt.t end (** Implementation of dac pages storage using filesystem. *) -module Filesystem : - S with type configuration = string and type hash = Sc_rollup_reveal_hash.t = -struct +module Filesystem : S with type configuration = string = struct (** [t] represents directory path *) type t = string type configuration = string - type hash = Protocol.Sc_rollup_reveal_hash.t - let init data_dir = data_dir - let path data_dir key = - Filename.(concat data_dir @@ Protocol.Sc_rollup_reveal_hash.to_hex key) + let path data_dir hash_string_key = + Filename.(concat data_dir @@ hash_string_key) - let save data_dir ~hash ~content = + let save ((module P) : Dac_plugin.t) data_dir ~hash ~content = let open Lwt_result_syntax in - let path = path data_dir hash in + let hash_string = P.to_hex hash in + let path = path data_dir hash_string in let*! result = Lwt_utils_unix.with_atomic_open_out path @@ fun chan -> Lwt_utils_unix.write_bytes chan content in match result with | Ok () -> return () - | Error _ -> tzfail @@ Cannot_write_page_to_page_storage {hash; content} + | Error _ -> + tzfail + @@ Cannot_write_page_to_page_storage {hash = hash_string; content} - let load data_dir ~hash = + let load ((module P) : Dac_plugin.t) data_dir ~hash = let open Lwt_result_syntax in - let path = path data_dir hash in + let hash_string = P.to_hex hash in + let path = path data_dir hash_string in Lwt.catch (fun () -> let*! result = Lwt_utils_unix.read_file path in return @@ String.to_bytes result) - (fun _exn -> tzfail @@ Cannot_read_page_from_page_storage hash) + (fun _exn -> tzfail @@ Cannot_read_page_from_page_storage hash_string) end diff --git a/src/proto_alpha/lib_dac/page_store.mli b/src/lib_dac_node/page_store.mli similarity index 81% rename from src/proto_alpha/lib_dac/page_store.mli rename to src/lib_dac_node/page_store.mli index 2e4b971fe59dc30ffd7d37f180112ebd3cf803e9..862e650b69b73fc18d88ca8449e15d5de67cc058 100644 --- a/src/proto_alpha/lib_dac/page_store.mli +++ b/src/lib_dac_node/page_store.mli @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2023 Marigold *) +(* Copyright (c) 2023 Trili Tech *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -23,17 +24,12 @@ (* *) (*****************************************************************************) -open Environment.Error_monad - (* FIXME: https://gitlab.com/tezos/tezos/-/issues/4651 Integrate the pages backend with preimage data manager. *) type error += - | Cannot_write_page_to_page_storage of { - hash : Protocol.Sc_rollup_reveal_hash.t; - content : bytes; - } - | Cannot_read_page_from_page_storage of Protocol.Sc_rollup_reveal_hash.t + | Cannot_write_page_to_page_storage of {hash : string; content : bytes} + | Cannot_read_page_from_page_storage of string (** [S] is the module type defining the backend required for persisting DAC pages data onto the page storage. *) @@ -45,33 +41,32 @@ module type S = sig be used to initialize the store.*) type configuration - (** [hash] is the hash of preimages.*) - type hash - (** [init configuration] initializes a page store using the information provided in [configuration]. *) val init : configuration -> t - (** [save t ~hash content] writes [content] of page onto storage + (** [save dac_plugin t ~hash ~content] writes [content] of page onto storage backend [t] under given [key]. When writing fails it returns a [Cannot_write_page_to_page_storage] error. - We require [write_page] to be atomic.*) - val save : t -> hash:hash -> content:bytes -> unit tzresult Lwt.t + We require [save] to be atomic. *) + val save : + Dac_plugin.t -> + t -> + hash:Dac_plugin.hash -> + content:bytes -> + unit tzresult Lwt.t - (** [load t ~hash] returns [content] of the storage backend [t] + (** [load dac_plugin t ~hash] returns [content] of the storage backend [t] represented by a key. When reading fails it returns a [Cannot_read_page_from_page_storage] error. *) - val load : t -> hash:hash -> bytes tzresult Lwt.t + val load : Dac_plugin.t -> t -> hash:Dac_plugin.hash -> bytes tzresult Lwt.t end (** [Filesystem] is an implementation of the page store backed up by the local filesystem. The configuration is a string denoting the path where files will be saved. A page is saved as a file whose name is the hex encoded hash of its content. *) -module Filesystem : - S - with type configuration = string - and type hash = Protocol.Sc_rollup_reveal_hash.t +module Filesystem : S with type configuration = string diff --git a/src/proto_alpha/lib_dac/dac_pages_encoding.ml b/src/lib_dac_node/pages_encoding.ml similarity index 82% rename from src/proto_alpha/lib_dac/dac_pages_encoding.ml rename to src/lib_dac_node/pages_encoding.ml index e7616fbc3c6adfda40349233b19807a13ef6e7d1..272a2e7779f4b6e9b82f8d547dc42c2d7f1887c5 100644 --- a/src/proto_alpha/lib_dac/dac_pages_encoding.ml +++ b/src/lib_dac_node/pages_encoding.ml @@ -1,7 +1,7 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2022 Trili Tech *) +(* Copyright (c) 2022-2023 Trili Tech *) (* Copyright (c) 2022 Marigold *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) @@ -28,13 +28,9 @@ decoded by the Sc-rollup kernels. *) -open Protocol - (* FIXME: https://gitlab.com/tezos/tezos/-/issues/4682 Remove `open Environment.Error_monad` *) -open Environment.Error_monad - type error += | Payload_cannot_be_empty | Cannot_serialize_page_payload @@ -64,19 +60,10 @@ module type VERSION = sig val hashes_version : version end -module type Hashing_scheme = sig - include module type of Sc_rollup_reveal_hash - - val scheme : supported_hashes -end - (** [Dac_codec] is a module for encoding a payload as a whole and returnining the calculated root hash. *) module type Dac_codec = sig - (* Hash type used to represent pages *) - type hash - (* Page store type *) type page_store @@ -85,16 +72,23 @@ module type Dac_codec = sig [page_store]. The serialization scheme is some hash-based encoding scheme such that a root hash is produced that represents the payload. *) - val serialize_payload : page_store:page_store -> bytes -> hash tzresult Lwt.t + val serialize_payload : + Dac_plugin.t -> + page_store:page_store -> + bytes -> + Dac_plugin.hash tzresult Lwt.t - (** [deserialize_payload page_store hash] deserializes a payload from [hash] + (** [deserialize_payload dac_plugin page_store hash] deserializes a payload from [hash] using some hash-based encoding scheme. Any payload serialized by [serialize_payload] can de deserialized from its root hash by [deserialize_payload], that is, these functions are inverses of each other. *) val deserialize_payload : - page_store:page_store -> hash -> bytes tzresult Lwt.t + Dac_plugin.t -> + page_store:page_store -> + Dac_plugin.hash -> + bytes tzresult Lwt.t end (** [Buffered_dac_codec] partially constructs a Dac external message payload by @@ -107,34 +101,36 @@ module type Buffered_dac_codec = sig (* Buffer type *) type t - (* Hash type used to represent pages *) - type hash - (* Page store type *) type page_store (* Returns an empty buffer *) val empty : unit -> t - (** [add page_store buffer message] adds a [message] to [buffer]. The + (** [add dac_plugin page_store buffer message] adds a [message] to [buffer]. The [buffer] is serialized to [page_store] when it is full. Serialization logic is dependent on the encoding scheme. *) - val add : page_store:page_store -> t -> bytes -> unit tzresult Lwt.t + val add : + Dac_plugin.t -> page_store:page_store -> t -> bytes -> unit tzresult Lwt.t - (** [finalize page_store buffer] serializes the [buffer] to [page_store] and + (** [finalize dac_plugin page_store buffer] serializes the [buffer] to [page_store] and returns a root hash that represents the final payload. The serialization logic is dependent on the encoding scheme. [buffer] is emptied after this call. *) - val finalize : page_store:page_store -> t -> hash tzresult Lwt.t + val finalize : + Dac_plugin.t -> page_store:page_store -> t -> Dac_plugin.hash tzresult Lwt.t - (** [deserialize_payload page_store hash] deserializes a payload from [hash] + (** [deserialize_payload dac_plugin page_store hash] deserializes a payload from [hash] using some hash-based encoding scheme. Any payload serialized by [add] + [finalize] can be deserialized by this function. *) val deserialize_payload : - page_store:page_store -> hash -> bytes tzresult Lwt.t + Dac_plugin.t -> + page_store:page_store -> + Dac_plugin.hash -> + bytes tzresult Lwt.t end let () = @@ -245,12 +241,7 @@ module Merkle_tree = struct one chunk, provided it could fit into the memory. *) - module Make_buffered - (H : Hashing_scheme) - (S : Page_store.S with type hash := H.t) - (V : VERSION) - (C : CONFIG) = - struct + module Make_buffered (S : Page_store.S) (V : VERSION) (C : CONFIG) = struct (* Even numbers are used for versioning Contents pages, odd numbers are used for versioning Hashes pages. *) module V = struct @@ -265,26 +256,24 @@ module Merkle_tree = struct (** [page_data] is either [Cont_data] or [Hash_data] where each represents data not bound in size for [Contents] or [Hashes] page respectively. *) - type page_data = Cont_data of bytes | Hash_data of H.t list + type page_data = Cont_data of bytes | Hash_data of Dac_plugin.hash list type t = page_data Stack.t - type hash = H.t - type page_store = S.t - let hash bytes = H.hash_bytes [bytes] ~scheme:H.scheme + let hash ((module P) : Dac_plugin.t) bytes = + P.hash_bytes [bytes] ~scheme:Blake2B - let hash_encoding = H.encoding - - let hashes_encoding = Data_encoding.list hash_encoding + let hashes_encoding ((module P) : Dac_plugin.t) = + Data_encoding.list P.encoding (* The preamble of a serialized page contains 1 byte denoting the version, and 4 bytes encoding the size of the rest of the page. In total, 5 bytes. *) let page_preamble_size = 5 - let hash_bytes_size = H.size ~scheme:H.scheme + let hash_bytes_size ((module P) : Dac_plugin.t) = P.size ~scheme:Blake2B (** Payload pages are encoded as follows: the first byte is an integer, which is corresponds to either `payload_version` (for payload pages) or @@ -293,7 +282,7 @@ module Merkle_tree = struct list of raw bytes (in the case of a payload page), or a list of hashes, which occupy 33 bytes each. I.e. 32 bytes for the inner hash and 1 byte for the tag identifying the hashing scheme. *) - let page_encoding = + let page_encoding dac_plugin = Data_encoding.( union ~tag_size:`Uint8 @@ -307,7 +296,7 @@ module Merkle_tree = struct case ~title:"hashes" (Tag V.hashes_version_tag) - hashes_encoding + (hashes_encoding dac_plugin) (function Hashes hashes -> Some hashes | _ -> None) (fun hashes -> Hashes hashes); ]) @@ -315,34 +304,34 @@ module Merkle_tree = struct (** Serialization function for a single page. It converts a page to a sequence of bytes using [page_encoding]. It also checks that the serialized page does not exceed [page_size] bytes. *) - let serialize_page page = + let serialize_page dac_plugin page = match Data_encoding.Binary.to_bytes - (Data_encoding.check_size C.max_page_size page_encoding) + (Data_encoding.check_size C.max_page_size (page_encoding dac_plugin)) page with | Ok raw_page -> Ok raw_page - | Error _ -> error Cannot_serialize_page_payload + | Error _ -> Result_syntax.tzfail Cannot_serialize_page_payload - let store_page ~page_store raw_page = + let store_page dac_plugin ~page_store raw_page = let open Lwt_result_syntax in - let*? serialized_page = serialize_page raw_page in + let*? serialized_page = serialize_page dac_plugin raw_page in (* Hashes are computed from raw pages, each of which consists of a preamble of 5 bytes followed by a page payload - a raw sequence of bytes from the original payload for Contents pages, and a a sequence of serialized hashes for hashes pages. The preamble bytes is part of the sequence of bytes which is hashed. *) - let hash = hash serialized_page in - let* () = S.save page_store ~hash ~content:serialized_page in + let hash = hash dac_plugin serialized_page in + let* () = S.save dac_plugin page_store ~hash ~content:serialized_page in return hash let is_empty_page_data = function | Cont_data cont -> Bytes.empty = cont | Hash_data ls -> List.is_empty ls - let max_hashes_per_page = - (C.max_page_size - page_preamble_size) / hash_bytes_size + let max_hashes_per_page dac_plugin = + (C.max_page_size - page_preamble_size) / hash_bytes_size dac_plugin (** Represents data that is held in-memory and is yet to be persisted to the disk - effectively acting as a buffer, making sure that all [pages] of the given @@ -368,21 +357,21 @@ module Merkle_tree = struct let open Result_syntax in (* 1 byte for the version size, 4 bytes for the size of the payload. *) let actual_page_size = C.max_page_size - page_preamble_size in - if actual_page_size <= 0 then error Non_positive_size_of_payload + if actual_page_size <= 0 then tzfail Non_positive_size_of_payload else let+ splitted_payload = String.chunk_bytes actual_page_size payload in List.map (fun cont -> Contents (String.to_bytes cont)) splitted_payload - let split_hashes hashes = - let number_of_hashes = max_hashes_per_page in + let split_hashes dac_plugin hashes = + let number_of_hashes = max_hashes_per_page dac_plugin in (* Requiring a branching factor of at least 2 is necessary to ensure that the serialization process terminates. If only one hash were stored per page, then the number of pages at height `n-1` could be potentially equal to the number of pages at height `n`. *) if number_of_hashes < 2 then - error Merkle_tree_branching_factor_not_high_enough + Result_syntax.tzfail Merkle_tree_branching_factor_not_high_enough else let rec go aux list = match list with @@ -393,7 +382,7 @@ module Merkle_tree = struct in Ok (go [] hashes) - let split page_data = + let split dac_plugin page_data = let leftover_with_full_pages ls = let open Lwt_result_syntax in match List.rev ls with @@ -406,7 +395,7 @@ module Merkle_tree = struct let*? pages = match page_data with | Cont_data cont -> split_payload cont - | Hash_data ls -> split_hashes ls + | Hash_data ls -> split_hashes dac_plugin ls in leftover_with_full_pages pages @@ -421,13 +410,13 @@ module Merkle_tree = struct (* We dont expect to get here *) tzfail Cannot_combine_pages_data_of_different_type - let process ~page_store level = + let process dac_plugin ~page_store level = let open Lwt_result_syntax in - let* level, pages = split level in + let* level, pages = split dac_plugin level in let* rev_hashes = List.fold_left_es (fun accm page -> - let* hash = store_page ~page_store page in + let* hash = store_page dac_plugin ~page_store page in return @@ (hash :: accm)) [] pages @@ -448,17 +437,19 @@ module Merkle_tree = struct Throws [Payload_cannot_be_empty] if [page_data] is empty. *) - let rec add_rec ~page_store stack page_data = + let rec add_rec dac_plugin ~page_store stack page_data = let open Lwt_result_syntax in let open Merkle_level in let* merkle_level = if Stack.is_empty stack then return (init_level page_data) else add_page_data ~level:(Stack.pop stack) ~page_data in - let* merkle_level, page_data = process ~page_store merkle_level in + let* merkle_level, page_data = + process dac_plugin ~page_store merkle_level + in let* () = if not @@ is_empty_page_data page_data then - add_rec ~page_store stack page_data + add_rec dac_plugin ~page_store stack page_data else return () in return @@ Stack.push merkle_level stack @@ -469,12 +460,12 @@ module Merkle_tree = struct partially filled. Throws [Payload_cannot_be_empty] in case of empty payload *) - let add ~page_store stack payload = + let add dac_plugin ~page_store stack payload = let open Lwt_result_syntax in let* () = fail_unless (Bytes.length payload > 0) Payload_cannot_be_empty in - let number_of_hashes = max_hashes_per_page in + let number_of_hashes = max_hashes_per_page dac_plugin in (* Requiring a branching factor of at least 2 is necessary to ensure that the serialization process terminates. If only one hash were stored per page, then the number of pages at height `n-1` could be potentially equal to the number of @@ -484,19 +475,19 @@ module Merkle_tree = struct (number_of_hashes > 1) Merkle_tree_branching_factor_not_high_enough in - add_rec ~page_store stack (Cont_data payload) + add_rec dac_plugin ~page_store stack (Cont_data payload) (** [finalize] returns a root hash of agggregated data, by eagerly storing every partially filled level on the stack. Starting at the bottom and adding stored page hash to next level, repeating this procedure until left with a single (root) hash. *) - let rec finalize ~page_store stack = + let rec finalize dac_plugin ~page_store stack = let open Merkle_level in let finalize_level page_data = let store_page = store_page ~page_store in match page_data with - | Cont_data cont -> store_page (Contents cont) - | Hash_data ls -> store_page (Hashes ls) + | Cont_data cont -> store_page dac_plugin (Contents cont) + | Hash_data ls -> store_page dac_plugin (Hashes ls) in let open Lwt_result_syntax in let* () = @@ -506,21 +497,23 @@ module Merkle_tree = struct let merkle_level = Stack.pop stack in if is_empty merkle_level then (* If level is empty, there is nothing to hash *) - finalize ~page_store stack + finalize dac_plugin ~page_store stack else if Stack.is_empty stack && has_single_hash merkle_level then (* If top level of the tree and only one hash, then this is a root hash *) get_single_hash merkle_level else let* hash = finalize_level merkle_level in - let* () = add_rec ~page_store stack (Hash_data [hash]) in - finalize ~page_store stack + let* () = add_rec dac_plugin ~page_store stack (Hash_data [hash]) in + finalize dac_plugin ~page_store stack (** Deserialization function for a single page. A sequence of bytes is converted to a page using [page_encoding]. *) - let deserialize_page raw_page = - match Data_encoding.Binary.of_bytes page_encoding raw_page with + let deserialize_page dac_plugin raw_page = + match + Data_encoding.Binary.of_bytes (page_encoding dac_plugin) raw_page + with | Ok page -> Ok page - | Error _ -> error Cannot_deserialize_page + | Error _ -> Result_syntax.tzfail Cannot_deserialize_page (** Deserialization function for reconstructing the original payload from its Merkle tree root hash. The function [retrieve_page_from_hash] @@ -537,14 +530,14 @@ module Merkle_tree = struct hash and pages are computed using the serialized_payload function outlined above, but it is not guaranteed in more general cases. *) - let deserialize_payload ~page_store root_hash = + let deserialize_payload dac_plugin ~page_store root_hash = let rec go retrieved_hashes retrieved_content = let open Lwt_result_syntax in match retrieved_hashes with | [] -> return @@ Bytes.concat Bytes.empty retrieved_content | hash :: hashes -> ( - let* serialized_page = S.load page_store ~hash in - let*? page = deserialize_page serialized_page in + let* serialized_page = S.load dac_plugin page_store ~hash in + let*? page = deserialize_page dac_plugin serialized_page in match page with | Hashes page_hashes -> (* Hashes are saved in reverse order. *) @@ -564,8 +557,6 @@ module Merkle_tree = struct (** Derives a [Dac_codec] from [Buffered_dac_codec] sharing the same underlying encoding scheme. *) module Make (B : Buffered_dac_codec) = struct - type hash = B.hash - type page_store = B.page_store (** Main function for computing the pages of a Merkle tree from a sequence @@ -573,22 +564,17 @@ module Merkle_tree = struct the original payload can be reconstructed from its Merkle tree root; The function [serialize_payload] returns the root hash of the Merkle tree constructed. *) - let serialize_payload ~page_store payload = + let serialize_payload dac_plugin ~page_store payload = let open Lwt_result_syntax in let stack = B.empty () in - let* () = B.add ~page_store stack payload in - B.finalize ~page_store stack + let* () = B.add dac_plugin ~page_store stack payload in + B.finalize dac_plugin ~page_store stack let deserialize_payload = B.deserialize_payload end module V0_Buffered = Make_buffered - (struct - include Sc_rollup_reveal_hash - - let scheme = Sc_rollup_reveal_hash.Blake2B - end) (Page_store.Filesystem) (struct (* Cntents_version_tag used in content pages is 0. *) @@ -610,25 +596,19 @@ module Merkle_tree = struct end module Hash_chain = struct - module type PAGE_FMT = sig - type h - - type page = {succ_hash : h; content : string} + module V0 = struct + type page = {succ_hash : Dac_plugin.hash; content : string} - val content_limit : int - - (** Serializes a single page (an element in the hash link). *) - val serialize_page : page -> string - end + let hash ((module P) : Dac_plugin.t) bytes = + P.hash_bytes ~scheme:Blake2B [bytes] - module Make (H : Hashing_scheme) (P : PAGE_FMT with type h = H.t) = struct - type h = H.t + let content_limit = + (4 * 1024) - 100 (* We reserve 100 bytes for the continuation hash. *) - let hash bytes = H.hash_bytes ~scheme:H.scheme [bytes] + let serialize_page ((module P) : Dac_plugin.t) page = + Format.asprintf "%s hash:%s" page.content (P.to_hex page.succ_hash) - let to_hex = H.to_hex - - let link_chunks chunks : (H.t * bytes) list = + let link_chunks dac_plugin chunks : (Dac_plugin.hash * bytes) list = let rec link_chunks_rev linked_pages rev_pages = match rev_pages with | [] -> linked_pages @@ -637,10 +617,10 @@ module Hash_chain = struct match linked_pages with | [] -> chunk | (succ_hash, _) :: _ -> - P.serialize_page {succ_hash; content = chunk} + serialize_page dac_plugin {succ_hash; content = chunk} in let page = Bytes.of_string page in - let hash = hash page in + let hash = hash dac_plugin page in (link_chunks_rev [@tailcall]) ((hash, page) :: linked_pages) rev_chunks @@ -648,44 +628,22 @@ module Hash_chain = struct let rev_chunks = List.rev chunks in link_chunks_rev [] rev_chunks - let make_hash_chain data = + let make_hash_chain dac_plugin data = let open Result_syntax in - let+ chunks = String.chunk_bytes P.content_limit data in - link_chunks chunks + let+ chunks = String.chunk_bytes content_limit data in + link_chunks dac_plugin chunks (** Main function for computing a hash chain from a byte sequence. Returns the chain head hash.[for_each_page] may be supplied to run post processing tasks on each page, for example, to persisit a serialized page to disk. *) - let serialize_payload ~for_each_page payload = + let serialize_payload dac_plugin ~for_each_page payload = let open Lwt_result_syntax in let* () = fail_unless (Bytes.length payload > 0) Payload_cannot_be_empty in - let*? hash_chain = make_hash_chain payload in + let*? hash_chain = make_hash_chain dac_plugin payload in let+ () = List.iter_es for_each_page hash_chain in Stdlib.List.hd hash_chain |> fst end - - module V0 = - Make - (struct - include Sc_rollup_reveal_hash - - let scheme = Sc_rollup_reveal_hash.Blake2B - end) - (struct - type h = Sc_rollup_reveal_hash.t - - type page = {succ_hash : h; content : string} - - let content_limit = - (4 * 1024) - 100 (* We reserve 100 bytes for the continuation hash. *) - - let serialize_page page = - Format.asprintf - "%s hash:%s" - page.content - (Sc_rollup_reveal_hash.to_hex page.succ_hash) - end) end diff --git a/src/proto_alpha/lib_dac/dac_pages_encoding.mli b/src/lib_dac_node/pages_encoding.mli similarity index 79% rename from src/proto_alpha/lib_dac/dac_pages_encoding.mli rename to src/lib_dac_node/pages_encoding.mli index 33f5a05c07bd344ee56b2bdf0a0a05a49fec17be..766e8576ce27d7adff2e53551e22aff85d15d930 100644 --- a/src/proto_alpha/lib_dac/dac_pages_encoding.mli +++ b/src/lib_dac_node/pages_encoding.mli @@ -1,7 +1,7 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2023 TriliTech *) +(* Copyright (c) 2022-2023 Trili Tech *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -25,8 +25,6 @@ (** DAC pages encoding schemes *) -open Environment.Error_monad - type error += | Payload_cannot_be_empty | Cannot_serialize_page_payload @@ -54,19 +52,10 @@ module type VERSION = sig val hashes_version : version end -module type Hashing_scheme = sig - include module type of Protocol.Sc_rollup_reveal_hash - - val scheme : supported_hashes -end - (** [Dac_codec] is a module for encoding a payload as a whole and returnining the calculated root hash. *) module type Dac_codec = sig - (* Hash type used to represent pages *) - type hash - (* Page store type *) type page_store @@ -75,16 +64,23 @@ module type Dac_codec = sig [page_store]. The serialization scheme is some hash-based encoding scheme such that a root hash is produced that represents the payload. *) - val serialize_payload : page_store:page_store -> bytes -> hash tzresult Lwt.t + val serialize_payload : + Dac_plugin.t -> + page_store:page_store -> + bytes -> + Dac_plugin.hash tzresult Lwt.t - (** [deserialize_payload page_store hash] deserializes a payload from [hash] + (** [deserialize_payload dac_plugin page_store hash] deserializes a payload from [hash] using some hash-based encoding scheme. Any payload serialized by [serialize_payload] can de deserialized from its root hash by [deserialize_payload], that is, these functions are inverses of each other. *) val deserialize_payload : - page_store:page_store -> hash -> bytes tzresult Lwt.t + Dac_plugin.t -> + page_store:page_store -> + Dac_plugin.hash -> + bytes tzresult Lwt.t end (** [Buffered_dac_codec] partially constructs a Dac external message payload by @@ -97,34 +93,36 @@ module type Buffered_dac_codec = sig (* Buffer type *) type t - (* Hash type used to represent pages *) - type hash - (* Page store type *) type page_store (* Returns an empty buffer *) val empty : unit -> t - (** [add page_store buffer message] adds a [message] to [buffer]. The + (** [add dac_plugin page_store buffer message] adds a [message] to [buffer]. The [buffer] is serialized to [page_store] when it is full. Serialization logic is dependent on the encoding scheme. *) - val add : page_store:page_store -> t -> bytes -> unit tzresult Lwt.t + val add : + Dac_plugin.t -> page_store:page_store -> t -> bytes -> unit tzresult Lwt.t - (** [finalize page_store buffer] serializes the [buffer] to [page_store] and + (** [finalize dac_plugin page_store buffer] serializes the [buffer] to [page_store] and returns a root hash that represents the final payload. The serialization logic is dependent on the encoding scheme. [buffer] is emptied after this call. *) - val finalize : page_store:page_store -> t -> hash tzresult Lwt.t + val finalize : + Dac_plugin.t -> page_store:page_store -> t -> Dac_plugin.hash tzresult Lwt.t - (** [deserialize_payload page_store hash] deserializes a payload from [hash] + (** [deserialize_payload dac_plugin page_store hash] deserializes a payload from [hash] using some hash-based encoding scheme. Any payload serialized by [add] + [finalize] can be deserialized by this function. *) val deserialize_payload : - page_store:page_store -> hash -> bytes tzresult Lwt.t + Dac_plugin.t -> + page_store:page_store -> + Dac_plugin.hash -> + bytes tzresult Lwt.t end (** Encoding of DAC payload as a Merkle tree with an arbitrary branching @@ -163,23 +161,16 @@ module Merkle_tree : sig Remove `page_store = string` when Hash_chain is removed Context https://gitlab.com/tezos/tezos/-/merge_requests/7465#note_1247831273 *) - module V0 : - Dac_codec - with type page_store = Page_store.Filesystem.t - and type hash = Protocol.Sc_rollup_reveal_hash.t + module V0 : Dac_codec with type page_store = Page_store.Filesystem.t (**/**) module Internal_for_tests : sig - module Make_buffered - (H : Hashing_scheme) - (S : Page_store.S with type hash := H.t) - (V : VERSION) - (C : CONFIG) : - Buffered_dac_codec with type hash = H.t and type page_store = S.t + module Make_buffered (S : Page_store.S) (V : VERSION) (C : CONFIG) : + Buffered_dac_codec with type page_store = S.t module Make (B : Buffered_dac_codec) : - Dac_codec with type hash := B.hash and type page_store := B.page_store + Dac_codec with type page_store := B.page_store end end @@ -188,17 +179,13 @@ end *) module Hash_chain : sig module V0 : sig - type h = Protocol.Sc_rollup_reveal_hash.t - val serialize_payload : - for_each_page:(h * bytes -> unit tzresult Lwt.t) -> + Dac_plugin.t -> + for_each_page:(Dac_plugin.hash * bytes -> unit tzresult Lwt.t) -> bytes -> - h tzresult Lwt.t - - val make_hash_chain : bytes -> ((h * bytes) list, 'a) result - - val to_hex : h -> string + Dac_plugin.hash tzresult Lwt.t - val hash : bytes -> h + val make_hash_chain : + Dac_plugin.t -> bytes -> ((Dac_plugin.hash * bytes) list, 'a) result end end diff --git a/src/lib_dac_node/signature_manager.ml b/src/lib_dac_node/signature_manager.ml new file mode 100644 index 0000000000000000000000000000000000000000..6ee2b9795f2a36b20b3150d6fc3b1626ba32b9a0 --- /dev/null +++ b/src/lib_dac_node/signature_manager.ml @@ -0,0 +1,166 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022-2023 Trili Tech *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +type error += + | Cannot_convert_root_page_hash_to_bytes of string + | Cannot_compute_aggregate_signature of string + | Public_key_for_witness_not_available of int * string + +let () = + register_error_kind + `Permanent + ~id:"cannot_extract_root_page_hash_to_sign" + ~title:"Cannot convert root hash page to byte sequence" + ~description:"Cannot convert root hash page to byte sequence" + ~pp:(fun ppf b58_hash -> + Format.fprintf + ppf + "Cannot convert root hash page to byte sequence: %s" + b58_hash) + Data_encoding.(obj1 (req "hash" (string' Plain))) + (function + | Cannot_convert_root_page_hash_to_bytes b58_hash -> Some b58_hash + | _ -> None) + (fun b58_hash -> Cannot_convert_root_page_hash_to_bytes b58_hash) ; + register_error_kind + `Permanent + ~id:"cannot_compute_root_hash_aggregate_signature" + ~title:"Cannot compute aggregate signature of root page hash" + ~description:"Cannot compute aggregate signature of root page hash" + ~pp:(fun ppf b58_hash -> + Format.fprintf + ppf + "Cannot compute aggregate signature of root page hash: %s" + b58_hash) + Data_encoding.(obj1 (req "hash" (string' Plain))) + (function + | Cannot_compute_aggregate_signature b58_hash -> Some b58_hash | _ -> None) + (fun b58_hash -> Cannot_compute_aggregate_signature b58_hash) ; + register_error_kind + `Permanent + ~id:"public_key_of_witness_not_available" + ~title: + "Public key of witness dac member not available for verifying signature" + ~description: + "Public key of witness dac member not available for verifying signature" + ~pp:(fun ppf (witness_index, b58_hash) -> + Format.fprintf + ppf + "Public key of dac member %d not available for verifying signature of \ + root page hash %s" + witness_index + b58_hash) + Data_encoding.( + obj2 (req "witness_index" int31) (req "hash" (string' Plain))) + (function + | Public_key_for_witness_not_available (index, hash) -> Some (index, hash) + | _ -> None) + (fun (index, hash) -> Public_key_for_witness_not_available (index, hash)) + +let bind_es (f : 'a -> 'b option tzresult Lwt.t) v_opt = + let open Lwt_result_syntax in + match v_opt with None -> return None | Some v -> f v + +let rev_collect_indexed_signatures cctxt dac_sk_uris bytes_to_sign = + let open Lwt_result_syntax in + List.rev_mapi_es + (fun index sk_uri_opt -> + (* TODO: https://gitlab.com/tezos/tezos/-/issues/4306 + Implement Option.bind_es and revisit this. *) + bind_es + (fun sk_uri -> + let*! signature_res = + Tezos_client_base.Client_keys.aggregate_sign + cctxt + sk_uri + bytes_to_sign + in + let signature_opt = Result.to_option signature_res in + return + @@ Option.map (fun signature -> (index, signature)) signature_opt) + sk_uri_opt) + dac_sk_uris + +let compute_signatures_with_witnesses rev_indexed_signatures = + let open Lwt_result_syntax in + List.fold_left_es + (fun (signatures, witnesses) signature_opt -> + match signature_opt with + | None -> return (signatures, witnesses) + | Some (index, signature) -> + let bit = Z.shift_left Z.one index in + let witnesses = Z.logor witnesses bit in + return (signature :: signatures, witnesses)) + ([], Z.zero) + rev_indexed_signatures + +let sign_root_hash ((module P) : Dac_plugin.t) cctxt dac_sk_uris root_hash = + let open Lwt_result_syntax in + let bytes_to_sign = Data_encoding.Binary.to_bytes_opt P.encoding root_hash in + let root_hash = P.to_hex root_hash in + match bytes_to_sign with + | None -> tzfail @@ Cannot_convert_root_page_hash_to_bytes root_hash + | Some bytes_to_sign -> ( + let* rev_indexed_signatures = + rev_collect_indexed_signatures cctxt dac_sk_uris bytes_to_sign + in + let* signatures, witnesses = + compute_signatures_with_witnesses rev_indexed_signatures + in + let final_signature = + Tezos_crypto.Aggregate_signature.aggregate_signature_opt signatures + in + match final_signature with + | None -> tzfail @@ Cannot_compute_aggregate_signature root_hash + | Some signature -> return @@ (signature, witnesses)) + +let verify ((module P) : Dac_plugin.t) ~public_keys_opt root_page_hash signature + witnesses = + let open Lwt_result_syntax in + let hash_as_bytes = + Data_encoding.Binary.to_bytes_opt P.encoding root_page_hash + in + match hash_as_bytes with + | None -> + tzfail @@ Cannot_convert_root_page_hash_to_bytes (P.to_hex root_page_hash) + | Some bytes -> + let* pk_msg_list = + public_keys_opt + |> List.mapi (fun i public_key_opt -> (i, public_key_opt)) + |> List.filter_map_es (fun (i, public_key_opt) -> + let is_witness = Z.testbit witnesses i in + match public_key_opt with + | None -> + if is_witness then + tzfail + @@ Public_key_for_witness_not_available + (i, P.to_hex root_page_hash) + else return None + | Some public_key -> + if is_witness then return @@ Some (public_key, None, bytes) + else return None) + in + return + @@ Tezos_crypto.Aggregate_signature.aggregate_check pk_msg_list signature diff --git a/src/bin_dac_node/RPC_server.ml b/src/lib_dac_node/signature_manager.mli similarity index 50% rename from src/bin_dac_node/RPC_server.ml rename to src/lib_dac_node/signature_manager.mli index 9d24ae730be4446e838d5943b6c855ac978225da..5f2259b9d3a5efa3e27093412303c623601fbad2 100644 --- a/src/bin_dac_node/RPC_server.ml +++ b/src/lib_dac_node/signature_manager.mli @@ -1,8 +1,7 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2022 Trili Tech, *) -(* Copyright (c) 2022 Nomadic Labs, *) +(* Copyright (c) 2023 Trili Tech *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -24,54 +23,30 @@ (* *) (*****************************************************************************) -open Tezos_rpc_http -open Tezos_rpc_http_server +(* Module for managing the verification of aggregate signatures *) +type error += + | Cannot_convert_root_page_hash_to_bytes of string + | Cannot_compute_aggregate_signature of string + | Public_key_for_witness_not_available of int * string -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4750 - Move this to RPC_server.Legacy once all operating modes are supported. *) -let start_legacy ~rpc_address ~rpc_port ~reveal_data_dir ~threshold cctxt ctxt - dac_pks_opt dac_sk_uris = - let open Lwt_syntax in - let dir = - Tezos_rpc.Directory.register_dynamic_directory - Tezos_rpc.Directory.empty - Tezos_rpc.Path.open_root - (fun () -> - match Node_context.get_status ctxt with - | Ready {dac_plugin = (module Dac_plugin); _} -> - Lwt.return - (Dac_plugin.RPC.rpc_services - ~reveal_data_dir - cctxt - dac_pks_opt - dac_sk_uris - threshold) - | Starting -> Lwt.return Tezos_rpc.Directory.empty) - in - let rpc_address = P2p_addr.of_string_exn rpc_address in - let host = Ipaddr.V6.to_string rpc_address in - let node = `TCP (`Port rpc_port) in - let acl = RPC_server.Acl.default rpc_address in - let server = - RPC_server.init_server dir ~acl ~media_types:Media_type.all_media_types - in - Lwt.catch - (fun () -> - let* () = - RPC_server.launch - ~host - server - ~callback:(RPC_server.resto_callback server) - node - in - return_ok server) - fail_with_exn +(* [sign_root_hash dac_pliugin cctx dac_sk_uris_opt root_hash] is legacy function that + returns an aggregate signature over [root_hash] and a bitmap of witnesses where + empty elements of [dac_sk_uris_opt] are 0 and non-empty elements are 1. *) +val sign_root_hash : + Dac_plugin.t -> + #Client_context.wallet -> + Client_keys.aggregate_sk_uri option trace -> + Dac_plugin.hash -> + (Tezos_crypto.Aggregate_signature.signature * Z.t, tztrace) result Lwt.t -let shutdown = RPC_server.shutdown - -let install_finalizer rpc_server = - let open Lwt_syntax in - Lwt_exit.register_clean_up_callback ~loc:__LOC__ @@ fun exit_status -> - let* () = shutdown rpc_server in - let* () = Event.(emit shutdown_node exit_status) in - Tezos_base_unix.Internal_event_unix.close () +(** [verify dac_plugin public_keys_opt root_hash aggregate_signature witnesses] verifies + the [aggergate_signature] signed by the witnessed dac members. The witnessed + dac members is given by applying the [witnesses] bitmap against [public_keys_opt] + *) +val verify : + Dac_plugin.t -> + public_keys_opt:Tezos_crypto.Aggregate_signature.public_key option trace -> + Dac_plugin.hash -> + Tezos_crypto.Aggregate_signature.signature -> + Z.t -> + (bool, tztrace) result Lwt.t diff --git a/src/proto_alpha/lib_dac/RPC.ml b/src/proto_alpha/lib_dac/RPC.ml deleted file mode 100644 index d652ac7a6fb006da2868c95781bf32606bfdbb08..0000000000000000000000000000000000000000 --- a/src/proto_alpha/lib_dac/RPC.ml +++ /dev/null @@ -1,214 +0,0 @@ -(*****************************************************************************) -(* *) -(* Open Source License *) -(* Copyright (c) 2022 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. *) -(* *) -(*****************************************************************************) - -open Environment -open Error_monad - -type error += - | Cannot_construct_external_message - | Cannot_deserialize_external_message - -let () = - register_error_kind - `Permanent - ~id:"dac_cannot_construct_external_message" - ~title:"External rollup message could not be constructed" - ~description:"External rollup message could not be constructed" - ~pp:(fun ppf () -> - Format.fprintf ppf "External rollup message could not be constructed") - Data_encoding.unit - (function Cannot_construct_external_message -> Some () | _ -> None) - (fun () -> Cannot_construct_external_message) ; - register_error_kind - `Permanent - ~id:"dac_cannot_deserialize_rollup_external_message" - ~title:"External rollup message could not be deserialized" - ~description:"External rollup message could not be deserialized" - ~pp:(fun ppf () -> - Format.fprintf ppf "External rollup message could not be deserialized") - Data_encoding.unit - (function Cannot_deserialize_external_message -> Some () | _ -> None) - (fun () -> Cannot_deserialize_external_message) - -module Registration = struct - let register0_noctxt ~chunked s f dir = - RPC_directory.register ~chunked dir s (fun _rpc_ctxt q i -> f q i) - - (* A variant of [register0_noctxt] accepting one argument in the path *) - let register1_noctxt ~chunked s f dir = - RPC_directory.register ~chunked dir s (fun (_rpc_ctxt, p) q i -> f p q i) -end - -module DAC = struct - let store_preimage_request_encoding = - Data_encoding.( - obj2 - (req "payload" Data_encoding.(bytes Hex)) - (req "pagination_scheme" Dac_pages_encoding.pagination_scheme_encoding)) - - (* A variant of [Sc_rollup_reveal_hash.encoding] that prefers hex - encoding over b58check encoding for JSON. *) - let root_hash_encoding = - let binary = Protocol.Sc_rollup_reveal_hash.encoding in - Data_encoding.( - splitted - ~binary - ~json: - (conv_with_guard - Protocol.Sc_rollup_reveal_hash.to_hex - (fun str -> - Result.of_option - ~error:"Not a valid hash" - (Protocol.Sc_rollup_reveal_hash.of_hex str)) - (string Plain))) - - let store_preimage_response_encoding = - Data_encoding.( - obj2 - (req "root_hash" root_hash_encoding) - (req "external_message" (bytes Hex))) - - let external_message_query = - let open RPC_query in - query (fun hex_string -> hex_string) - |+ opt_field "external_message" RPC_arg.string (fun s -> s) - |> seal - - module S = struct - let dac_store_preimage = - RPC_service.put_service - ~description:"Split DAC reveal data" - ~query:RPC_query.empty - ~input:store_preimage_request_encoding - ~output:store_preimage_response_encoding - RPC_path.(open_root / "store_preimage") - - (* DAC/FIXME: https://gitlab.com/tezos/tezos/-/issues/4263 - remove this endpoint once end-to-end tests are in place. *) - let verify_external_message_signature = - RPC_service.get_service - ~description:"Verify signature of an external message to inject in L1" - ~query:external_message_query - ~output:Data_encoding.bool - RPC_path.(open_root / "verify_signature") - - let retrieve_preimage = - RPC_service.get_service - ~description: - "Retrieves a page by its page hash and returns its contents" - ~query:RPC_query.empty - ~output:(Data_encoding.bytes Hex) - RPC_path.( - open_root / "preimage" /: Protocol.Sc_rollup_reveal_hash.rpc_arg) - end - - let handle_serialize_dac_store_preimage cctxt dac_sk_uris reveal_data_dir - (data, pagination_scheme) = - let open Lwt_result_syntax in - let open Dac_pages_encoding in - let page_store = Page_store.Filesystem.init reveal_data_dir in - let* root_hash = - match pagination_scheme with - | Merkle_tree_V0 -> Merkle_tree.V0.serialize_payload ~page_store data - | Hash_chain_V0 -> - Hash_chain.V0.serialize_payload - ~for_each_page:(fun (hash, content) -> - Page_store.Filesystem.save page_store ~hash ~content) - data - in - let* signature, witnesses = - Dac_manager.Reveal_hash.Signatures.sign_root_hash - cctxt - dac_sk_uris - root_hash - in - let*? external_message = - match - Dac_manager.Reveal_hash.External_message.make - root_hash - signature - witnesses - with - | Ok external_message -> Ok external_message - | Error _ -> Error_monad.error Cannot_construct_external_message - in - return (root_hash, external_message) - - let handle_verify_external_message_signature public_keys_opt - encoded_l1_message = - let open Lwt_result_syntax in - let open Dac_manager.Reveal_hash 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.of_bytes as_bytes - in - match external_message with - | None -> tzfail @@ Cannot_deserialize_external_message - | Some (External_message.Dac_message {root_hash; signature; witnesses}) -> - Signatures.verify ~public_keys_opt root_hash signature witnesses - - let handle_retrieve_preimage reveal_data_dir hash = - let page_store = Page_store.Filesystem.init reveal_data_dir in - Page_store.Filesystem.load page_store ~hash - - let register_serialize_dac_store_preimage cctxt dac_sk_uris reveal_data_dir = - Registration.register0_noctxt - ~chunked:false - S.dac_store_preimage - (fun () input -> - handle_serialize_dac_store_preimage - cctxt - dac_sk_uris - reveal_data_dir - input) - - let register_verify_external_message_signature public_keys_opt = - Registration.register0_noctxt - ~chunked:false - S.verify_external_message_signature - (fun external_message () -> - handle_verify_external_message_signature - public_keys_opt - external_message) - - let register_retrieve_preimage reveal_data_dir = - Registration.register1_noctxt - ~chunked:false - S.retrieve_preimage - (fun hash () () -> handle_retrieve_preimage reveal_data_dir hash) - - let register reveal_data_dir cctxt dac_public_keys_opt dac_sk_uris = - (RPC_directory.empty : unit RPC_directory.t) - |> register_serialize_dac_store_preimage cctxt dac_sk_uris reveal_data_dir - |> register_verify_external_message_signature dac_public_keys_opt - |> register_retrieve_preimage reveal_data_dir -end - -let rpc_services ~reveal_data_dir cctxt dac_public_keys_opt dac_sk_uris - _threshold = - DAC.register reveal_data_dir cctxt dac_public_keys_opt dac_sk_uris diff --git a/src/proto_alpha/lib_dac/dac_plugin_registration.ml b/src/proto_alpha/lib_dac/dac_plugin_registration.ml index afe80bd58bc8732cdd4d60f197adcdc6cab9f870..8cc826d9da187ee9a87dc56428281e3f66db9b14 100644 --- a/src/proto_alpha/lib_dac/dac_plugin_registration.ml +++ b/src/proto_alpha/lib_dac/dac_plugin_registration.ml @@ -25,34 +25,76 @@ (*****************************************************************************) module Make (Mapper : sig - val of_bytes : bytes -> Dac_plugin.Dac_hash.t + val of_bytes : bytes -> Dac_plugin.hash end) : Dac_plugin.T = struct - module Dac_hash = struct - let to_bytes = Dac_plugin.Dac_hash.to_bytes - - let to_reveal_hash dac_hash = - dac_hash |> to_bytes - |> Data_encoding.Binary.of_bytes_exn - Protocol.Sc_rollup_reveal_hash.encoding - - let of_reveal_hash reveal_hash = - reveal_hash - |> Data_encoding.Binary.to_bytes_exn - Protocol.Sc_rollup_reveal_hash.encoding - |> Mapper.of_bytes - - let encoding = - Data_encoding.conv - to_reveal_hash - of_reveal_hash - Protocol.Sc_rollup_reveal_hash.encoding - end + let to_bytes = Dac_plugin.hash_to_bytes + + let to_reveal_hash dac_hash = + dac_hash |> to_bytes + |> Data_encoding.Binary.of_bytes_exn Protocol.Sc_rollup_reveal_hash.encoding + + let of_reveal_hash reveal_hash = + reveal_hash + |> Data_encoding.Binary.to_bytes_exn Protocol.Sc_rollup_reveal_hash.encoding + |> Mapper.of_bytes + + let encoding = + Data_encoding.conv + to_reveal_hash + of_reveal_hash + Protocol.Sc_rollup_reveal_hash.encoding + + let dac_hash_to_proto_supported_hashes = function + | Dac_plugin.Blake2B -> Protocol.Sc_rollup_reveal_hash.Blake2B + + let proto_to_dac_hash_supported_hashes = function + | Protocol.Sc_rollup_reveal_hash.Blake2B -> Dac_plugin.Blake2B + + let hash_string ~(scheme : Dac_plugin.supported_hashes) ?key strings = + Protocol.Sc_rollup_reveal_hash.hash_string + ~scheme:(dac_hash_to_proto_supported_hashes scheme) + ?key + strings + |> of_reveal_hash + + let hash_bytes ~(scheme : Dac_plugin.supported_hashes) ?key bytes = + Protocol.Sc_rollup_reveal_hash.hash_bytes + ~scheme:(dac_hash_to_proto_supported_hashes scheme) + ?key + bytes + |> of_reveal_hash + + let scheme_of_hash hash = + to_reveal_hash hash |> Protocol.Sc_rollup_reveal_hash.scheme_of_hash + |> proto_to_dac_hash_supported_hashes + + let of_hex hex = + Protocol.Sc_rollup_reveal_hash.of_hex hex |> Option.map of_reveal_hash + + let to_hex hash = to_reveal_hash hash |> Protocol.Sc_rollup_reveal_hash.to_hex + + let size ~scheme = + Protocol.Sc_rollup_reveal_hash.size + ~scheme:(dac_hash_to_proto_supported_hashes scheme) + + let hash_rpc_arg = + let construct = to_hex in + let destruct hash = + match of_hex hash with + | None -> Error "Cannot parse reveal hash" + | Some reveal_hash -> Ok reveal_hash + in + Tezos_rpc.Arg.make + ~descr:"A reveal hash" + ~name:"reveal_hash" + ~destruct + ~construct + () module Proto = Registerer.Registered - module RPC = RPC end -let make_plugin : (bytes -> Dac_plugin.Dac_hash.t) -> (module Dac_plugin.T) = +let make_plugin : (bytes -> Dac_plugin.hash) -> (module Dac_plugin.T) = fun of_bytes -> let module Plugin = Make (struct let of_bytes = of_bytes diff --git a/src/proto_alpha/lib_dac/dac_signature_manager.ml b/src/proto_alpha/lib_dac/dac_signature_manager.ml deleted file mode 100644 index f0009dc71e87c09af6dbf3b44a183a0931d908ad..0000000000000000000000000000000000000000 --- a/src/proto_alpha/lib_dac/dac_signature_manager.ml +++ /dev/null @@ -1,180 +0,0 @@ -(*****************************************************************************) -(* *) -(* Open Source License *) -(* Copyright (c) 2022 Trili Tech *) -(* *) -(* Permission is hereby granted, free of charge, to any person obtaining a *) -(* copy of this software and associated documentation files (the "Software"),*) -(* to deal in the Software without restriction, including without limitation *) -(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) -(* and/or sell copies of the Software, and to permit persons to whom the *) -(* Software is furnished to do so, subject to the following conditions: *) -(* *) -(* The above copyright notice and this permission notice shall be included *) -(* in all copies or substantial portions of the Software. *) -(* *) -(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) -(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) -(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) -(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) -(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) -(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) -(* DEALINGS IN THE SOFTWARE. *) -(* *) -(*****************************************************************************) - -open Protocol -open Environment -open Error_monad - -type error += - | Cannot_convert_root_page_hash_to_bytes of string - | Cannot_compute_aggregate_signature of string - | Public_key_for_witness_not_available of int * string - -let () = - register_error_kind - `Permanent - ~id:"cannot_extract_root_page_hash_to_sign" - ~title:"Cannot convert root hash page to byte sequence" - ~description:"Cannot convert root hash page to byte sequence" - ~pp:(fun ppf b58_hash -> - Format.fprintf - ppf - "Cannot convert root hash page to byte sequence: %s" - b58_hash) - Data_encoding.(obj1 (req "hash" (string Plain))) - (function - | Cannot_convert_root_page_hash_to_bytes b58_hash -> Some b58_hash - | _ -> None) - (fun b58_hash -> Cannot_convert_root_page_hash_to_bytes b58_hash) ; - register_error_kind - `Permanent - ~id:"cannot_compute_root_hash_aggregate_signature" - ~title:"Cannot compute aggregate signature of root page hash" - ~description:"Cannot compute aggregate signature of root page hash" - ~pp:(fun ppf b58_hash -> - Format.fprintf - ppf - "Cannot compute aggregate signature of root page hash: %s" - b58_hash) - Data_encoding.(obj1 (req "hash" (string Plain))) - (function - | Cannot_compute_aggregate_signature b58_hash -> Some b58_hash | _ -> None) - (fun b58_hash -> Cannot_compute_aggregate_signature b58_hash) ; - register_error_kind - `Permanent - ~id:"public_key_of_witness_not_available" - ~title: - "Public key of witness dac member not available for verifying signature" - ~description: - "Public key of witness dac member not available for verifying signature" - ~pp:(fun ppf (witness_index, b58_hash) -> - Format.fprintf - ppf - "Public key of dac member %d not available for verifying signature of \ - root page hash %s" - witness_index - b58_hash) - Data_encoding.(obj2 (req "witness_index" int31) (req "hash" (string Plain))) - (function - | Public_key_for_witness_not_available (index, hash) -> Some (index, hash) - | _ -> None) - (fun (index, hash) -> Public_key_for_witness_not_available (index, hash)) - -module type REVEAL_HASH = module type of Sc_rollup_reveal_hash - -module Make (Hashing_scheme : REVEAL_HASH) = struct - let bind_es (f : 'a -> 'b option tzresult Lwt.t) v_opt = - let open Lwt_result_syntax in - match v_opt with None -> return None | Some v -> f v - - let rev_collect_indexed_signatures cctxt dac_sk_uris bytes_to_sign = - let open Lwt_result_syntax in - List.rev_mapi_es - (fun index sk_uri_opt -> - (* TODO: https://gitlab.com/tezos/tezos/-/issues/4306 - Implement Option.bind_es and revisit this. *) - bind_es - (fun sk_uri -> - let*! signature_res = - Tezos_client_base.Client_keys.aggregate_sign - cctxt - sk_uri - bytes_to_sign - in - let signature_opt = Result.to_option signature_res in - return - @@ Option.map (fun signature -> (index, signature)) signature_opt) - sk_uri_opt) - dac_sk_uris - - let compute_signatures_with_witnesses rev_indexed_signatures = - let open Lwt_result_syntax in - List.fold_left_es - (fun (signatures, witnesses) signature_opt -> - match signature_opt with - | None -> return (signatures, witnesses) - | Some (index, signature) -> - let*? bitmap = Bitset.add witnesses index in - return (signature :: signatures, bitmap)) - ([], Bitset.empty) - rev_indexed_signatures - - let sign_root_hash cctxt dac_sk_uris root_hash = - let open Lwt_result_syntax in - let bytes_to_sign = - Data_encoding.Binary.to_bytes_opt Hashing_scheme.encoding root_hash - in - let root_hash = Hashing_scheme.to_hex root_hash in - match bytes_to_sign with - | None -> tzfail @@ Cannot_convert_root_page_hash_to_bytes root_hash - | Some bytes_to_sign -> ( - let* rev_indexed_signatures = - rev_collect_indexed_signatures cctxt dac_sk_uris bytes_to_sign - in - let* signatures, witnesses = - compute_signatures_with_witnesses rev_indexed_signatures - in - let final_signature = - Tezos_crypto.Aggregate_signature.aggregate_signature_opt signatures - in - match final_signature with - | None -> tzfail @@ Cannot_compute_aggregate_signature root_hash - | Some signature -> return @@ (signature, witnesses)) - - let verify ~public_keys_opt root_page_hash signature witnesses = - let open Lwt_result_syntax in - let hash_as_bytes = - Data_encoding.Binary.to_bytes_opt Hashing_scheme.encoding root_page_hash - in - match hash_as_bytes with - | None -> - tzfail - @@ Cannot_convert_root_page_hash_to_bytes - (Hashing_scheme.to_hex root_page_hash) - | Some bytes -> - let* pk_msg_list = - public_keys_opt - |> List.mapi (fun i public_key_opt -> (i, public_key_opt)) - |> List.filter_map_es (fun (i, public_key_opt) -> - let*? is_witness = Bitset.mem witnesses i in - - match public_key_opt with - | None -> - if is_witness then - tzfail - @@ Public_key_for_witness_not_available - (i, Hashing_scheme.to_hex root_page_hash) - else return None - | Some public_key -> - if is_witness then return @@ Some (public_key, None, bytes) - else return None) - in - return - @@ Tezos_crypto.Aggregate_signature.aggregate_check - pk_msg_list - signature -end - -module Reveal_hash = Make (Sc_rollup_reveal_hash) diff --git a/src/proto_alpha/lib_dac/dune b/src/proto_alpha/lib_dac/dune index e3384eaa058de67c8b533b874940ef3fa79d9f85..d95b4442321617ec350fe591280630f714689126 100644 --- a/src/proto_alpha/lib_dac/dune +++ b/src/proto_alpha/lib_dac/dune @@ -12,7 +12,6 @@ tezos_dac_node_lib tezos-client-alpha tezos-embedded-protocol-alpha - tezos-layer2-utils-alpha tezos-protocol-alpha) (inline_tests (flags -verbose) (modes native)) (preprocess (pps ppx_expect)) @@ -26,5 +25,4 @@ -open Tezos_dac_node_lib -open Tezos_client_alpha -open Tezos_embedded_protocol_alpha - -open Tezos_layer2_utils_alpha -open Tezos_protocol_alpha)) diff --git a/src/proto_alpha/lib_dac/test/test_dac_pages_encoding.ml b/src/proto_alpha/lib_dac/test/test_dac_pages_encoding.ml index c592bf260db47b7d58e15b3f37549a9207db2910..dd0fbc5c0bc2bcd559fe8231125304ae7f79c226 100644 --- a/src/proto_alpha/lib_dac/test/test_dac_pages_encoding.ml +++ b/src/proto_alpha/lib_dac/test/test_dac_pages_encoding.ml @@ -1,7 +1,7 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2022 Trili Tech *) +(* Copyright (c) 2022-2023 Trili Tech *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -34,14 +34,26 @@ (** DAC/FIXME: https://gitlab.com/tezos/tezos/-/issues/4021 Add tests to check actual (sequences of) bytes in serialized pages. *) -open Protocol - let lift f = Lwt.map Environment.wrap_tzresult f (* Tests are run against a mock storage backend where a Hash-indexed/Bytes-valued Map is used to simulate adding and retrieving files to a directory. *) -module Hashes_map = Sc_rollup_reveal_hash.Map + +(** TODO: https://gitlab.com/tezos/tezos/-/issues/4855 + Move tests to libdac_node/test +*) +let dac_plugin = Stdlib.Option.get (Dac_plugin.get Protocol.hash) + +module Hashes_map = Map.Make (struct + type t = Dac_plugin.hash + + let compare h1 h2 = + let (module Dac_plugin) = dac_plugin in + let s1 = Dac_plugin.to_hex h1 in + let s2 = Dac_plugin.to_hex h2 in + String.compare s1 s2 +end) type hashes_map = bytes Hashes_map.t @@ -185,21 +197,19 @@ e color cui tu fai cotanto mestiĀ». Allor si mosse, e io li tenni dietro.|} module Hashes_map_backend = struct - open Environment.Error_monad - type t = bytes Hashes_map.t ref type configuration = unit - type hash = Sc_rollup_reveal_hash.t + type hash = Dac_plugin.hash let init () = ref Hashes_map.empty type error += - | Page_already_saved of Sc_rollup_reveal_hash.t - | Page_is_missing of Sc_rollup_reveal_hash.t + | Page_already_saved of Dac_plugin.hash + | Page_is_missing of Dac_plugin.hash - let save t ~hash ~content = + let save (_plugin : Dac_plugin.t) t ~hash ~content = let open Lwt_result_syntax in match Hashes_map.find hash !t with | None -> @@ -209,7 +219,7 @@ module Hashes_map_backend = struct if Bytes.equal old_bytes content then return_unit else tzfail @@ Page_already_saved hash - let load t ~hash = + let load (_plugin : Dac_plugin.t) t ~hash = let open Lwt_result_syntax in let bytes = Hashes_map.find hash !t in match bytes with @@ -219,35 +229,31 @@ module Hashes_map_backend = struct let number_of_pages t = List.length @@ Hashes_map.bindings !t end -let assert_equal_bytes ~loc msg = - Assert.equal ~loc Bytes.equal msg String.pp_bytes_hex +let assert_equal_bytes ~loc msg a b = + Assert.equal ~loc Bytes.equal msg String.pp_bytes_hex a b let assert_fails_with ~loc k expected_err = let open Lwt_result_syntax in let*! res = k in - let res = Environment.wrap_tzresult res in - Assert.error ~loc res (( = ) (Environment.wrap_tzerror expected_err)) + Assert.error ~loc res (( = ) expected_err) module Merkle_tree = struct - open Dac_pages_encoding.Merkle_tree - - module Make_V0_for_test - (C : Dac_pages_encoding.CONFIG) - (S : Page_store.S with type hash := Sc_rollup_reveal_hash.t) = - Internal_for_tests.Make - (Internal_for_tests.Make_buffered - (struct - include Sc_rollup_reveal_hash - - let scheme = Sc_rollup_reveal_hash.Blake2B - end) - (S) - (struct - let content_version = 0 - - let hashes_version = 0 - end) - (C)) + open Pages_encoding.Merkle_tree + + module Make_V0_for_test (C : Pages_encoding.CONFIG) (S : Page_store.S) = + struct + module Buffered = + Internal_for_tests.Make_buffered + (S) + (struct + let content_version = 0 + + let hashes_version = 0 + end) + (C) + + include Internal_for_tests.Make (Buffered) + end module V0 = struct let test_serialization_fails_with ~loc ~max_page_size ~payload ~error = @@ -258,7 +264,9 @@ module Merkle_tree = struct end) (Hashes_map_backend) in let page_store = Hashes_map_backend.init () in - let serialize_payload = serialize_payload ~page_store payload in + let serialize_payload = + serialize_payload dac_plugin ~page_store payload + in assert_fails_with ~loc serialize_payload error let test_serialization_roundtrip ?expect_num_of_pages ~loc ~max_page_size @@ -271,8 +279,10 @@ module Merkle_tree = struct (Hashes_map_backend) in let page_store = Hashes_map_backend.init () in let open Lwt_result_syntax in - let* hash = lift @@ serialize_payload ~page_store payload in - let* retrieved_payload = lift @@ deserialize_payload hash ~page_store in + let* hash = serialize_payload dac_plugin ~page_store payload in + let* retrieved_payload = + deserialize_payload dac_plugin ~page_store hash + in let* () = match expect_num_of_pages with | Some expected -> @@ -302,7 +312,7 @@ module Merkle_tree = struct ~loc:__LOC__ ~max_page_size:50 ~payload - ~error:Dac_pages_encoding.Merkle_tree_branching_factor_not_high_enough + ~error:Pages_encoding.Merkle_tree_branching_factor_not_high_enough let serialize_empty_payload_fails () = (* Limit the number of hashes stored per page to 2. Because hashes @@ -314,7 +324,7 @@ module Merkle_tree = struct ~loc:__LOC__ ~max_page_size:80 ~payload:Bytes.empty - ~error:Dac_pages_encoding.Payload_cannot_be_empty + ~error:Pages_encoding.Payload_cannot_be_empty let one_page_roundtrip () = (* Limit the number of hashes stored per page to 2. Because hashes @@ -453,16 +463,16 @@ module Hash_chain = struct String.sub str (min (String.length str) n) (max 0 (String.length str - n)) module V0 = struct - module Pagination_scheme = Dac_pages_encoding.Hash_chain.V0 + module Pagination_scheme = Pages_encoding.Hash_chain.V0 let deserialize_page page : - [`Node of Sc_rollup_reveal_hash.t * string | `Leaf of string] = + [`Node of Dac_plugin.hash * string | `Leaf of string] = if String.length page > 3996 then let content = String.sub page 0 3996 in + let (module Plugin) = dac_plugin in let hash = Stdlib.Option.get - @@ Sc_rollup_reveal_hash.of_hex - (take_after page (3996 + String.length " hash:")) + @@ Plugin.of_hex (take_after page (3996 + String.length " hash:")) in `Node (hash, content) else `Leaf page @@ -482,51 +492,47 @@ module Hash_chain = struct let test_make_chain_hash_one_page () = let open Lwt_result_syntax in let payload = Bytes.of_string "simple payload" in - let*? pages = Pagination_scheme.make_hash_chain payload in + let*? pages = Pagination_scheme.make_hash_chain dac_plugin payload in let* () = Assert.equal_int ~loc:__LOC__ 1 (List.length pages) in let actual_hash, content = Stdlib.List.hd pages in let* () = assert_equal_bytes ~loc:__LOC__ "Contents not equal" payload content in + let (module Plugin) = dac_plugin in let expected_hash = - Pagination_scheme.to_hex @@ Pagination_scheme.hash content + Plugin.to_hex @@ Plugin.hash_bytes ~scheme:Blake2B [content] in - Assert.equal_string - ~loc:__LOC__ - expected_hash - (Pagination_scheme.to_hex actual_hash) + Assert.equal_string ~loc:__LOC__ expected_hash (Plugin.to_hex actual_hash) let test_make_chain_hash_long () = let open Lwt_result_syntax in let payload = Bytes.of_string long_payload in - let*? pages = Pagination_scheme.make_hash_chain payload in + let*? pages = Pagination_scheme.make_hash_chain dac_plugin payload in let* () = Assert.equal_int ~loc:__LOC__ 2 (List.length pages) in let head_succ = Stdlib.List.hd pages |> snd |> fun byt -> take_after (String.of_bytes byt) (3996 + String.length " hash:") in + let (module Plugin) = dac_plugin in let next_hash, content = Stdlib.List.nth pages 1 in let* () = - Assert.equal_string - ~loc:__LOC__ - (Pagination_scheme.to_hex next_hash) - head_succ + Assert.equal_string ~loc:__LOC__ (Plugin.to_hex next_hash) head_succ in Assert.equal_string ~loc:__LOC__ - (Pagination_scheme.to_hex next_hash) - (Pagination_scheme.to_hex @@ Pagination_scheme.hash content) + (Plugin.to_hex next_hash) + (Plugin.to_hex @@ Plugin.hash_bytes ~scheme:Blake2B [content]) let test_serialize () = let open Lwt_result_syntax in let payload = Bytes.of_string long_payload in let page_store = Hashes_map_backend.init () in let* root_hash = - lwt_map_error Environment.wrap_tztrace - @@ Pagination_scheme.serialize_payload - ~for_each_page:(fun (hash, content) -> - Hashes_map_backend.save page_store ~hash ~content) - payload + Pagination_scheme.serialize_payload + dac_plugin + ~for_each_page:(fun (hash, content) -> + Hashes_map_backend.save dac_plugin page_store ~hash ~content) + payload in let* () = Assert.equal_int @@ -534,12 +540,10 @@ module Hash_chain = struct (Hashes_map_backend.number_of_pages page_store) 2 in - let* content = - lwt_map_error Environment.wrap_tztrace - @@ retrieve_content - ~get_page:(Hashes_map_backend.load page_store) - root_hash + let get_page ~hash = + Hashes_map_backend.load dac_plugin page_store ~hash in + let* content = retrieve_content ~get_page root_hash in Assert.equal_string ~loc:__LOC__ long_payload content let test_serialize_empty_payload_fails () = @@ -548,13 +552,14 @@ module Hash_chain = struct let result = Pagination_scheme.serialize_payload ~for_each_page:(fun (hash, content) -> - Hashes_map_backend.save page_store ~hash ~content) + Hashes_map_backend.save dac_plugin page_store ~hash ~content) + dac_plugin payload in assert_fails_with ~loc:__LOC__ result - Dac_pages_encoding.Payload_cannot_be_empty + Pages_encoding.Payload_cannot_be_empty end end diff --git a/src/proto_alpha/lib_dac/test/test_dac_plugin_registration.ml b/src/proto_alpha/lib_dac/test/test_dac_plugin_registration.ml index 76a57e3c7765c0ac5aa2efbeb826d4575dd148f3..9d76e46a749dac9ae0bce5218bd077cd86d13877 100644 --- a/src/proto_alpha/lib_dac/test/test_dac_plugin_registration.ml +++ b/src/proto_alpha/lib_dac/test/test_dac_plugin_registration.ml @@ -34,6 +34,10 @@ module Protocol_reveal_hash = Protocol.Sc_rollup_reveal_hash +let dac_plugin = Stdlib.Option.get (Dac_plugin.get Protocol.hash) + +module P = (val dac_plugin) + (* Hash copied from https://gitlab.com/tezos/tezos/-/blob/master/tezt/tests/dac.ml#L331 *) let reveal_hash = @@ -44,17 +48,8 @@ let reveal_hash = let assert_equal_bytes ~loc msg = Assert.equal ~loc Bytes.equal msg String.pp_bytes_hex -let make_plugin_and_save_hash (proto : (module Dac_plugin.T) option ref) - make_plugin : (bytes -> Dac_plugin.Dac_hash.t) -> (module Dac_plugin.T) = - fun f -> - let dac_plugin = make_plugin f in - proto := Option.some dac_plugin ; - dac_plugin - -let test_dac_hash_bin_encoding_roundtrips_with_reveal_hash () = +let test_dac_hash_bin_encoding_roundtrip_with_reveal_hash () = let open Lwt_result_syntax in - let dac_plugin = Stdlib.Option.get (Dac_plugin.get Protocol.hash) in - let module Plugin = (val dac_plugin) in let to_bytes e a = Stdlib.Result.get_ok @@ Data_encoding.Binary.to_bytes e a in @@ -62,8 +57,8 @@ let test_dac_hash_bin_encoding_roundtrips_with_reveal_hash () = Stdlib.Result.get_ok @@ Data_encoding.Binary.of_bytes e a in let reveal_hash_bytes = to_bytes Protocol_reveal_hash.encoding reveal_hash in - let dac_hash = from_bytes Plugin.Dac_hash.encoding reveal_hash_bytes in - let dac_hash_bytes = to_bytes Plugin.Dac_hash.encoding dac_hash in + let dac_hash = from_bytes P.encoding reveal_hash_bytes in + let dac_hash_bytes = to_bytes P.encoding dac_hash in let reveal_hash_decoded = from_bytes Protocol_reveal_hash.encoding dac_hash_bytes in @@ -82,10 +77,64 @@ let test_dac_hash_bin_encoding_roundtrips_with_reveal_hash () = reveal_hash reveal_hash_decoded +let test_dac_hash_hex_roundtrip_with_reveal_hash () = + let reveal_hash_hex = Protocol_reveal_hash.to_hex reveal_hash in + let dac_hash = Stdlib.Option.get @@ P.of_hex reveal_hash_hex in + let dac_hash_hex = P.to_hex dac_hash in + Assert.equal_string ~loc:__LOC__ reveal_hash_hex dac_hash_hex + +let test_dac_hash_hash_bytes_with_reveal_hash () = + let payload = Bytes.of_string "Hello world" in + let dac_hash = P.hash_bytes ~scheme:Blake2B [payload] in + let dac_hash = + Stdlib.Result.get_ok @@ Data_encoding.Binary.to_bytes P.encoding dac_hash + in + let reveal_hash = Protocol_reveal_hash.hash_bytes ~scheme:Blake2B [payload] in + let reveal_hash = + Stdlib.Result.get_ok + @@ Data_encoding.Binary.to_bytes Protocol_reveal_hash.encoding reveal_hash + in + assert_equal_bytes + ~loc:__LOC__ + "Encoded bytes are not equal" + reveal_hash + dac_hash + +let test_dac_hash_hash_string_with_reveal_hash () = + let payload = "Hello world" in + let dac_hash = P.hash_string ~scheme:Blake2B [payload] in + let dac_hash = + Stdlib.Result.get_ok @@ Data_encoding.Binary.to_bytes P.encoding dac_hash + in + let reveal_hash = + Protocol_reveal_hash.hash_string ~scheme:Blake2B [payload] + in + let reveal_hash = + Stdlib.Result.get_ok + @@ Data_encoding.Binary.to_bytes Protocol_reveal_hash.encoding reveal_hash + in + assert_equal_bytes + ~loc:__LOC__ + "Encoded bytes are not equal" + reveal_hash + dac_hash + let tests = [ Tztest.tztest "Binary encoding roundtrip test between Dac hash and reveal hash" `Quick - test_dac_hash_bin_encoding_roundtrips_with_reveal_hash; + test_dac_hash_bin_encoding_roundtrip_with_reveal_hash; + Tztest.tztest + "Hex encoding roundtrip test between Dac hash and reveal hash" + `Quick + test_dac_hash_hex_roundtrip_with_reveal_hash; + Tztest.tztest + "Hash bytes should be equal between Dac hash and reveal hash" + `Quick + test_dac_hash_hash_bytes_with_reveal_hash; + Tztest.tztest + "Hash string should be equal between Dac hash and reveal hash" + `Quick + test_dac_hash_hash_string_with_reveal_hash; ]