diff --git a/manifest/main.ml b/manifest/main.ml index 5c9c038e36cf28b95c0bd58ad3935f7bc775a8f4..9d8dae9a462ad35bc97323aecf48194c5e2a0869 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -5233,6 +5233,57 @@ module Protocol = Protocol ~inline_tests:ppx_expect ~linkall:true in + let dac = + (* [~link_all:true] is necessary to ensure that the dac plugin + registration happens when running the dal node. Removing this + option would cause DAL related tezts to fail because the DAC + plugin cannot be resolved. *) + only_if (active && N.(number >= 017)) @@ fun () -> + public_lib + (sf "tezos-dac-%s" name_dash) + ~path:(path // "lib_dac_plugin") + ~synopsis: + "Tezos/Protocol: protocol specific library for the Data availability \ + Committee" + ~deps: + [ + octez_base |> open_ ~m:"TzPervasives" + |> open_ ~m:"TzPervasives.Error_monad.Legacy_monad_globals"; + octez_protocol_compiler_registerer |> open_; + octez_stdlib_unix |> open_; + octez_dac_lib |> open_; + octez_dac_client_lib |> open_; + client |> if_some |> open_; + embedded |> open_; + main |> open_; + ] + ~inline_tests:ppx_expect + ~linkall:true + in + let _dac_tests = + only_if (active && N.(number >= 017)) @@ fun () -> + tezt + [ + "test_dac_pages_encoding"; + "test_dac_plugin_registration"; + "test_helpers"; + ] + ~path:(path // "lib_dac_plugin/test") + ~with_macos_security_framework:true + ~opam:(sf "tezos-dac-%s" name_dash) + ~deps: + [ + octez_base |> open_ ~m:"TzPervasives" + |> open_ ~m:"TzPervasives.Error_monad.Legacy_monad_globals"; + dac |> if_some |> open_; + main |> open_; + octez_base_test_helpers |> open_; + test_helpers |> if_some |> open_; + octez_dac_lib |> open_; + octez_dac_node_lib |> open_; + alcotezt; + ] + in let octez_sc_rollup_layer2 = only_if N.(number >= 016) @@ fun () -> public_lib @@ -5293,6 +5344,7 @@ module Protocol = Protocol octez_crypto_dal |> if_ N.(number >= 016) |> open_; prometheus_app |> if_ N.(number >= 016); octez_node_config |> if_ N.(number >= 016); + dac |> if_some |> open_; ] in let octez_sc_rollup_client = @@ -5478,56 +5530,6 @@ module Protocol = Protocol alcotezt; ] in - let dac = - (* [~link_all:true] is necessary to ensure that the dac plugin - registration happens when running the dal node. Removing this - option would cause DAL related tezts to fail because the DAC - plugin cannot be resolved. *) - only_if (active && N.(number >= 017)) @@ fun () -> - public_lib - (sf "tezos-dac-%s" name_dash) - ~path:(path // "lib_dac_plugin") - ~synopsis: - "Tezos/Protocol: protocol specific library for the Data availability \ - Committee" - ~deps: - [ - octez_base |> open_ ~m:"TzPervasives" - |> open_ ~m:"TzPervasives.Error_monad.Legacy_monad_globals"; - octez_protocol_compiler_registerer |> open_; - octez_stdlib_unix |> open_; - octez_dac_lib |> open_; - client |> if_some |> open_; - embedded |> open_; - main |> open_; - ] - ~inline_tests:ppx_expect - ~linkall:true - in - let _dac_tests = - only_if (active && N.(number >= 017)) @@ fun () -> - tezt - [ - "test_dac_pages_encoding"; - "test_dac_plugin_registration"; - "test_helpers"; - ] - ~path:(path // "lib_dac_plugin/test") - ~with_macos_security_framework:true - ~opam:(sf "tezos-dac-%s" name_dash) - ~deps: - [ - octez_base |> open_ ~m:"TzPervasives" - |> open_ ~m:"TzPervasives.Error_monad.Legacy_monad_globals"; - dac |> if_some |> open_; - main |> open_; - octez_base_test_helpers |> open_; - test_helpers |> if_some |> open_; - octez_dac_lib |> open_; - octez_dac_node_lib |> open_; - alcotezt; - ] - in let benchmark_type_inference = only_if active @@ fun () -> public_lib diff --git a/opam/octez-smart-rollup-node-alpha.opam b/opam/octez-smart-rollup-node-alpha.opam index 3cb31b9221adac2135631fee9be0f83a570e79e0..89f60843426fd49ec8154ceef0d46962fbef4f50 100644 --- a/opam/octez-smart-rollup-node-alpha.opam +++ b/opam/octez-smart-rollup-node-alpha.opam @@ -42,6 +42,7 @@ depends: [ "tezos-crypto-dal" "prometheus-app" { >= "1.2" } "octez-node-config" + "tezos-dac-alpha" "tezos-clic" "tezos-client-commands" ] diff --git a/opam/tezos-dac-alpha.opam b/opam/tezos-dac-alpha.opam index c620542e0a5c05922539b0f43d43946199b55c26..4d08e27b05194c9d28d62c146aebc24da72add70 100644 --- a/opam/tezos-dac-alpha.opam +++ b/opam/tezos-dac-alpha.opam @@ -15,6 +15,7 @@ depends: [ "octez-protocol-compiler" "tezos-stdlib-unix" "tezos-dac-lib" + "tezos-dac-client-lib" "tezos-client-alpha" "tezos-embedded-protocol-alpha" "tezos-protocol-alpha" diff --git a/src/lib_dac/RPC_services.ml b/src/lib_dac/RPC_services.ml index b01cc85473520cffccc01b1f6742c8f7cd2e7a8e..218ed47c77f907963a42bbb8b0881d47ef2577cf 100644 --- a/src/lib_dac/RPC_services.ml +++ b/src/lib_dac/RPC_services.ml @@ -106,3 +106,21 @@ let coordinator_post_preimage ((module P) : Dac_plugin.t) = ~input:Data_encoding.bytes ~output:P.encoding Tezos_rpc.Path.(open_root / "preimage") + +let get_missing_page ((module P) : Dac_plugin.t) = + Tezos_rpc.Service.get_service + ~description: + "Fetch a given page by forwarding the request to a Coordinator's GET \ + /preimage. The page is then saved to the node's page store before being \ + returned in the response. The endpoint should only be exposed in \ + Observer mode." + ~query:Tezos_rpc.Query.empty + ~output:Data_encoding.bytes + Tezos_rpc.Path.(open_root / "missing_page" /: P.hash_rpc_arg) + +let ping : ([`GET], unit, unit, unit, unit, string) Tezos_rpc.Service.service = + Tezos_rpc.Service.get_service + ~description:"Returns pong" + ~query:Tezos_rpc.Query.empty + ~output:Data_encoding.string + Tezos_rpc.Path.(open_root / "ping") diff --git a/src/lib_dac_client/dac_node_client.ml b/src/lib_dac_client/dac_node_client.ml index 5813202fbf9472589b41a8d5a4828dc5bebc7ff1..8a35e0cbeb37cbe6fae3f9cedc14a62915a17f2a 100644 --- a/src/lib_dac_client/dac_node_client.ml +++ b/src/lib_dac_client/dac_node_client.ml @@ -56,3 +56,5 @@ let streamed_call (cctxt : #cctxt) = cctxt#call_streamed_service *) let get_preimage (plugin : Dac_plugin.t) (cctxt : #cctxt) page_hash = call cctxt (RPC_services.retrieve_preimage plugin) ((), page_hash) () () + +let ping (cctxt : #cctxt) = call cctxt RPC_services.ping () () () diff --git a/src/lib_dac_client/dac_node_client.mli b/src/lib_dac_client/dac_node_client.mli index e608a4899e65ed1206c9bcaba033d0478535280c..c2c00d89be937cf71f3e926d9e01608b55401149 100644 --- a/src/lib_dac_client/dac_node_client.mli +++ b/src/lib_dac_client/dac_node_client.mli @@ -64,3 +64,6 @@ val streamed_call : returned as a sequence of bytes. *) val get_preimage : Dac_plugin.t -> #cctxt -> Dac_plugin.hash -> bytes tzresult Lwt.t + +(** [ping cctxt] returns pong. *) +val ping : #cctxt -> string tzresult Lwt.t diff --git a/src/lib_dac_node/RPC_server.ml b/src/lib_dac_node/RPC_server.ml index 7005a2040478de2ff1422e57968cc70a7ffb065a..bff9a8578ec4d0919a98caa4ac20d2e361d14ff6 100644 --- a/src/lib_dac_node/RPC_server.ml +++ b/src/lib_dac_node/RPC_server.ml @@ -154,6 +154,19 @@ let handle_get_certificate ctx root_hash = Certificate_repr.{aggregate_signature; witnesses; root_hash}) value_opt +let handle_get_missing_page ctx dac_plugin root_hash = + let open Lwt_result_syntax in + let page_store = Node_context.get_page_store ctx in + let*? cctxt = Node_context.get_coordinator_client ctx in + let remote_store = Page_store.Remote.(init {cctxt; page_store}) in + let* preimage = + (* TODO: https://gitlab.com/tezos/tezos/-/issues/5142 + Retrieve missing page from dac committee via "flooding". *) + Page_store.Remote.load dac_plugin remote_store root_hash + in + let*! () = Event.(emit (fetched_missing_page dac_plugin) root_hash) in + return preimage + let register_serialize_dac_store_preimage ctx cctxt dac_sk_uris page_store hash_streamer directory = directory @@ -220,6 +233,20 @@ let register_get_certificate ctx dac_plugin = (RPC_services.get_certificate dac_plugin) (fun root_hash () () -> handle_get_certificate ctx root_hash) +let register_get_missing_page ctx dac_plugin = + match (Node_context.get_config ctx).mode with + | Legacy _ | Observer _ -> + add_service + Tezos_rpc.Directory.register1 + (RPC_services.get_missing_page dac_plugin) + (fun root_hash () () -> + handle_get_missing_page ctx dac_plugin root_hash) + | Coordinator _ | Committee_member _ -> Fun.id + +let register_ping = + add_service Tezos_rpc.Directory.register0 RPC_services.ping (fun () () -> + Lwt_result_syntax.return "pong") + let register dac_plugin node_context cctxt dac_public_keys_opt dac_sk_uris hash_streamer = let page_store = Node_context.get_page_store node_context in @@ -239,6 +266,7 @@ let register dac_plugin node_context cctxt dac_public_keys_opt dac_sk_uris "/preimage" endpoint should be moved out of the [start_legacy]. *) |> register_coordinator_preimage_endpoint dac_plugin hash_streamer page_store |> register_get_certificate node_context dac_plugin + |> register_get_missing_page node_context dac_plugin (* TODO: https://gitlab.com/tezos/tezos/-/issues/4750 Move this to RPC_server.Legacy once all operating modes are supported. *) diff --git a/src/lib_dac_node/event.ml b/src/lib_dac_node/event.ml index 109c26c07c62e4ac3464f7f9882d830c3156acfe..24ad5b7a3b29eccdf354d18b4499cae7419fc3d2 100644 --- a/src/lib_dac_node/event.ml +++ b/src/lib_dac_node/event.ml @@ -239,3 +239,11 @@ let emit_received_root_hash_processed ((module P) : Dac_plugin.t) hash = let emit_processing_root_hash_failed ((module P) : Dac_plugin.t) hash errors = emit processing_root_hash_failed (P.to_hex hash, errors) + +let fetched_missing_page ((module P) : Dac_plugin.t) = + declare_1 + ~section + ~name:"missing_page_fetched" + ~msg:"Successfully fetched missing page for hash: {hash}" + ~level:Notice + ("hash", P.encoding) diff --git a/src/lib_dac_node/node_context.ml b/src/lib_dac_node/node_context.ml index a2d5be51a1351824385af32057ea38fb9b2ba530..442ac935870a596f8aa700b35dd54ff915390d79 100644 --- a/src/lib_dac_node/node_context.ml +++ b/src/lib_dac_node/node_context.ml @@ -87,6 +87,7 @@ let set_ready ctxt dac_plugin = type error += | Node_not_ready | Invalid_operation_for_mode of {mode : string; operation : string} + | Coordinator_client_not_defined_in_config let () = register_error_kind @@ -119,7 +120,20 @@ let () = (function | Invalid_operation_for_mode {mode; operation} -> Some (mode, operation) | _ -> None) - (fun (mode, operation) -> Invalid_operation_for_mode {mode; operation}) + (fun (mode, operation) -> Invalid_operation_for_mode {mode; operation}) ; + register_error_kind + `Permanent + ~id:"dac_coordinator_client_not_defined_in_config" + ~title:"Coordinator client was not defined in config." + ~description: + "Coordinator client configuration was expected but not defined." + ~pp:(fun ppf () -> + Format.fprintf + ppf + "Coordinator client configuration was expected but not defined.") + Data_encoding.unit + (function Coordinator_client_not_defined_in_config -> Some () | _ -> None) + (fun () -> Coordinator_client_not_defined_in_config) let get_ready ctxt = let open Result_syntax in @@ -162,3 +176,8 @@ let get_committee_members ctxt = tzfail @@ Invalid_operation_for_mode {mode = "dac_member"; operation = "get_committee_members"} + +let get_coordinator_client ctxt = + match ctxt.coordinator_opt with + | Some cctxt -> Ok cctxt + | None -> Result_syntax.tzfail Coordinator_client_not_defined_in_config diff --git a/src/lib_dac_node/node_context.mli b/src/lib_dac_node/node_context.mli index c8ae5cb3cff880c126fc8713559cdb3b26ad00ba..e0e2076d652e22ae429bda5fab15940dc8575712 100644 --- a/src/lib_dac_node/node_context.mli +++ b/src/lib_dac_node/node_context.mli @@ -95,3 +95,7 @@ val get_node_store : t -> 'a Store_sigs.mode -> 'a Store.Irmin_store.t [Configuration.Coordinator.dac_members_addresses] *) val get_committee_members : t -> Tezos_crypto.Aggregate_signature.public_key_hash list tzresult + +(** [get_coordinator_client ctx] returns the Coordinator client if it + is available. *) +val get_coordinator_client : t -> Dac_node_client.cctxt tzresult diff --git a/src/proto_alpha/lib_dac_plugin/dac_observer_client.ml b/src/proto_alpha/lib_dac_plugin/dac_observer_client.ml new file mode 100644 index 0000000000000000000000000000000000000000..fb64cf61379887e89a03c9f580e63c3729a14b84 --- /dev/null +++ b/src/proto_alpha/lib_dac_plugin/dac_observer_client.ml @@ -0,0 +1,138 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2023 TriliTech, *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +module Reveal_hash = Protocol.Sc_rollup_reveal_hash +module Dac_client = Dac_node_client + +module Configuration = struct + type t = {observer_endpoint : Uri.t} +end + +type t = { + config : Configuration.t; + observer_cctx : Dac_client.unix_cctxt; + dac_plugin : Dac_plugin.t; +} + +type error += + | Cannot_initlized_dac_plugin + | Timeout of Z.t + | Cannot_request_preimage_from_observer + +let () = + register_error_kind + ~id:"dac_observer_client.cannot_initialize_dac_plugin" + ~title:"Could not initialize DAC plugin" + ~description:"Could not initialize DAC plugin." + ~pp:(fun ppf () -> Format.fprintf ppf "Could not initialize DAC plugin") + `Permanent + Data_encoding.unit + (function Cannot_initlized_dac_plugin -> Some () | _ -> None) + (fun _ -> Cannot_initlized_dac_plugin) ; + register_error_kind + ~id:"dac_observer_client.timeout" + ~title:"Timeout while querying Dac Observer Node" + ~description:"Timeout while querying Dac Observer Node." + ~pp:(fun ppf seconds -> + Format.fprintf + ppf + "Timeout after %as when querying Dac Observer Node" + Z.pp_print + seconds) + `Permanent + Data_encoding.z + (function Timeout seconds -> Some seconds | _ -> None) + (fun seconds -> Timeout seconds) ; + register_error_kind + ~id:"dac_observer_client.cannot_request_preimage_from_observer" + ~title:"Cannot request preimage from observer" + ~description:"Cannot request preimage from observer." + ~pp:(fun ppf () -> + Format.fprintf ppf "Cannot request preimage from observer.") + `Permanent + Data_encoding.unit + (function Cannot_request_preimage_from_observer -> Some () | _ -> None) + (fun () -> Cannot_request_preimage_from_observer) + +let proto_hash_to_dac_hash ((module P) : Dac_plugin.t) proto_hash = + proto_hash + |> Data_encoding.Binary.to_bytes_exn Protocol.Sc_rollup_reveal_hash.encoding + |> Data_encoding.Binary.of_bytes_exn P.encoding + +module Client = struct + let make_unix endpoint = + let rpc_config = + {Tezos_rpc_http_client_unix.RPC_client_unix.default_config with endpoint} + in + new Dac_node_client.unix_cctxt ~rpc_config + + let ping cctx : string tzresult Lwt.t = Dac_client.ping cctx + + let fetch_missing_page_call (plugin : Dac_plugin.t) + (cctxt : #Dac_client.cctxt) page_hash = + Dac_client.call + cctxt + (RPC_services.get_missing_page plugin) + ((), page_hash) + () + () + + let fetch_missing_page dac_client hash = + let open Lwt_result_syntax in + let dac_plugin = dac_client.dac_plugin in + let dac_hash = proto_hash_to_dac_hash dac_plugin hash in + let+ preimage = + fetch_missing_page_call dac_plugin dac_client.observer_cctx dac_hash + in + String.of_bytes preimage +end + +let timeout_default = Z.of_int 60 + +let init ~observer_endpoint () = + let open Lwt_result_syntax in + let* dac_plugin = + match Dac_plugin.get Protocol.hash with + | Some dac_plugin -> return dac_plugin + | None -> + let () = Dac_plugin.register Dac_plugin_registration.make_plugin in + Option.fold_f + ~none:(fun () -> tzfail @@ Cannot_initlized_dac_plugin) + ~some:(fun p -> return p) + (Dac_plugin.get Protocol.hash) + in + let observer_cctx = Client.make_unix observer_endpoint in + let+ _ping = Client.ping observer_cctx in + {config = {observer_endpoint}; observer_cctx; dac_plugin} + +let fetch_preimage ?(timeout_s = timeout_default) dac_client hash = + let open Lwt_result_syntax in + let run () = + Lwt_unix.with_timeout (Z.to_float timeout_s) (fun () -> + Client.fetch_missing_page dac_client hash) + in + Lwt.catch run (function + | Lwt_unix.Timeout -> tzfail @@ Timeout timeout_s + | _ -> tzfail Cannot_request_preimage_from_observer) diff --git a/src/proto_alpha/lib_dac_plugin/dac_observer_client.mli b/src/proto_alpha/lib_dac_plugin/dac_observer_client.mli new file mode 100644 index 0000000000000000000000000000000000000000..27be20f1e69cca7bdf8196efe83c47e956e312ac --- /dev/null +++ b/src/proto_alpha/lib_dac_plugin/dac_observer_client.mli @@ -0,0 +1,57 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2023 TriliTech, *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(* Client for interacting with a Dac Node in Observer mode. *) + +module Reveal_hash = Tezos_protocol_alpha.Protocol.Sc_rollup_reveal_hash +module Dac_client = Dac_node_client + +type error += + | Cannot_initlized_dac_plugin + | Timeout of Z.t + | Cannot_request_preimage_from_observer + +module Configuration : sig + type t +end + +type t + +(** [init observer_endpoint] initializes a [Dac_observer_client.t] with + base endpoint point to [observer_endpoint]. +*) +val init : + observer_endpoint:Uri.t -> + unit -> + (t, error trace) result Lwt.t +(** [fetch_preimage ?timeout_s dac_observer_client hash] requests the preimage of [hash] + from a Dac Observer Node. As a side effect, the Observer node saves the preimage to + disk before returning. +*) +val fetch_preimage : + ?timeout_s:Z.t -> + t -> + Tezos_protocol_alpha.Protocol.Sc_rollup_reveal_hash.t -> + string tzresult Lwt.t diff --git a/src/proto_alpha/lib_dac_plugin/dune b/src/proto_alpha/lib_dac_plugin/dune index dee875addab82210b75fca5f2baeb450eefd5954..ce6fc8440ebbf6b16aa14ec3b2ba8c0551e73093 100644 --- a/src/proto_alpha/lib_dac_plugin/dune +++ b/src/proto_alpha/lib_dac_plugin/dune @@ -10,6 +10,7 @@ octez-protocol-compiler.registerer tezos-stdlib-unix tezos-dac-lib + tezos-dac-client-lib tezos-client-alpha tezos-embedded-protocol-alpha tezos-protocol-alpha) @@ -23,6 +24,7 @@ -open Tezos_protocol_registerer -open Tezos_stdlib_unix -open Tezos_dac_lib + -open Tezos_dac_client_lib -open Tezos_client_alpha -open Tezos_embedded_protocol_alpha -open Tezos_protocol_alpha)) diff --git a/src/proto_alpha/lib_sc_rollup_node/dune b/src/proto_alpha/lib_sc_rollup_node/dune index cf52914bd2cd88d74139eb1cdc7a0c9d4d99998d..8487f124c451866f1b731830018d2ee451cb1883 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dune +++ b/src/proto_alpha/lib_sc_rollup_node/dune @@ -40,7 +40,8 @@ tezos-scoru-wasm-fast tezos-crypto-dal prometheus-app - octez-node-config) + octez-node-config + tezos-dac-alpha) (flags (:standard) -open Tezos_base @@ -62,4 +63,5 @@ -open Tezos_layer2_store -open Octez_crawler -open Octez_injector - -open Tezos_crypto_dal)) + -open Tezos_crypto_dal + -open Tezos_dac_alpha)) diff --git a/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml index eed5cb02fefef7d172c651f452ebdda9ee3144d8..b31457de4831dabff775ad9fa165674d31a91257 100644 --- a/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml @@ -88,7 +88,7 @@ module Make (PVM : Pvm.S) = struct in match found_in_map with | Some data -> return data - | None -> Reveals.get ~data_dir ~pvm_kind:PVM.kind ~hash + | None -> Reveals.get ~data_dir ~pvm_kind:PVM.kind hash let continue_with_fuel consumption initial_fuel state f = let open Delayed_write_monad.Lwt_result_syntax in diff --git a/src/proto_alpha/lib_sc_rollup_node/refutation_game.ml b/src/proto_alpha/lib_sc_rollup_node/refutation_game.ml index fcc0cb9f79535c43efa8004a357cd0965ac5010d..465b3e5ba928d8145255c3b98b2bd9725295e5f4 100644 --- a/src/proto_alpha/lib_sc_rollup_node/refutation_game.ml +++ b/src/proto_alpha/lib_sc_rollup_node/refutation_game.ml @@ -217,7 +217,7 @@ module Make (Interpreter : Interpreter.S) : let reveal hash = let open Lwt_syntax in let* res = - Reveals.get ~data_dir:node_ctxt.data_dir ~pvm_kind:PVM.kind ~hash + Reveals.get ~data_dir:node_ctxt.data_dir ~pvm_kind:PVM.kind hash in match res with Ok data -> return @@ Some data | Error _ -> return None diff --git a/src/proto_alpha/lib_sc_rollup_node/reveals.ml b/src/proto_alpha/lib_sc_rollup_node/reveals.ml index e61edb2d4003aec99f3b1d2e6abd311bc210fceb..297955f5185e7a3f43684928f69f0ad91d38ee5e 100644 --- a/src/proto_alpha/lib_sc_rollup_node/reveals.ml +++ b/src/proto_alpha/lib_sc_rollup_node/reveals.ml @@ -87,17 +87,23 @@ let file_contents filename = Lwt.catch (fun () -> let*! contents = Lwt_utils_unix.read_file filename in - return contents) - (fun _ -> tzfail @@ Could_not_open_preimage_file filename) + return @@ Some contents) + (fun _ -> return_none) let path data_dir pvm_name hash = let hash = Protocol.Sc_rollup_reveal_hash.to_hex hash in Filename.(concat (concat data_dir pvm_name) hash) -let get ~data_dir ~pvm_kind ~hash = +let get ?dac_client_opt ~data_dir ~pvm_kind hash = let open Lwt_result_syntax in let filename = path data_dir (Sc_rollup.Kind.to_string pvm_kind) hash in - let* contents = file_contents filename in + let* contents_opt = file_contents filename in + let* contents = + match (contents_opt, dac_client_opt) with + | Some contents, _ -> return contents + | None, Some client -> Dac_observer_client.fetch_preimage client hash + | None, None -> tzfail @@ Could_not_open_preimage_file filename + in let*? () = let contents_hash = Reveal_hash.hash_string ~scheme:Reveal_hash.Blake2B [contents] diff --git a/src/proto_alpha/lib_sc_rollup_node/reveals.mli b/src/proto_alpha/lib_sc_rollup_node/reveals.mli index 56950f44c8b61cb8d2a56895a93e4c4be7735ad9..3914c56d1831360dba856b3388da8413e5ce8202 100644 --- a/src/proto_alpha/lib_sc_rollup_node/reveals.mli +++ b/src/proto_alpha/lib_sc_rollup_node/reveals.mli @@ -67,7 +67,8 @@ type source = 4kB) to be revealed.} } *) val get : + ?dac_client_opt:Dac_observer_client.t -> data_dir:string -> pvm_kind:Protocol.Alpha_context.Sc_rollup.Kind.t -> - hash:Protocol.Sc_rollup_reveal_hash.t -> + Protocol.Sc_rollup_reveal_hash.t -> string tzresult Lwt.t diff --git a/tezt/lib_tezos/rollup.ml b/tezt/lib_tezos/rollup.ml index 17e41231a8746936d86cf4db4c19d8f35e87d605..092bb08cad06d89f31ceec8535dfb39987f7adfe 100644 --- a/tezt/lib_tezos/rollup.ml +++ b/tezt/lib_tezos/rollup.ml @@ -956,5 +956,8 @@ module Dac = struct ( json |-> "witnesses" |> as_int, json |-> "aggregate_signature" |> as_string, json |-> "root_hash" |> as_string ) + + let get_missing_page ~hex_root_hash = + make GET ["missing_page"; Hex.show hex_root_hash] JSON.as_string end end diff --git a/tezt/lib_tezos/rollup.mli b/tezt/lib_tezos/rollup.mli index 34119328904e835fccd10d5c3b746ffda92bb292..5f376609ae773eb8f0033e7743073359ad530e90 100644 --- a/tezt/lib_tezos/rollup.mli +++ b/tezt/lib_tezos/rollup.mli @@ -452,5 +452,10 @@ module Dac : sig the subscribed dac members and obervers. *) val coordinator_store_preimage : payload:string -> (Dac_node.t, string) RPC_core.t + + (** [get_missing_page ~hex_root_hash] calls GET missing_page/[page_hash] + endpoint. *) + val get_missing_page : + hex_root_hash:Hex.t -> (Dac_node.t, string) RPC_core.t end end diff --git a/tezt/tests/dac.ml b/tezt/tests/dac.ml index eab2b993f7d403b4f6f5647627e213aa3a769920..495dfa3ef77f69513239e29009a7a734b7a75065 100644 --- a/tezt/tests/dac.ml +++ b/tezt/tests/dac.ml @@ -53,10 +53,10 @@ let regression_test ~__FILE__ ?(tags = []) title f = let tags = "dac" :: tags in Protocol.register_regression_test ~__FILE__ ~title ~tags f -let assert_lwt_failure ?__LOC__ msg unit_lwt = +let assert_lwt_failure ?__LOC__ msg lwt_under_inspection = let* passed = Lwt.catch - (fun () -> Lwt.map (fun () -> false) unit_lwt) + (fun () -> Lwt.map (fun _a -> false) lwt_under_inspection) (fun _exn -> return true) in if passed then unit else Test.fail ?__LOC__ msg @@ -1135,6 +1135,55 @@ let test_get_certificate _protocol _tezos_node _tz_client coordinator _threshold assert_verify_aggregate_signature [member] hex_root_hash certificate ; unit +(* 1. Observer should fetch missing page from Coordinator when GET /missing_page/{hash} + is called. + 2. As a side effect, Observer should save fetched page into its page store before + returning it in the response. This can be observer by checking the result of + retrieving preimage before and after the GET /missing_page/{hash} call.*) +let test_observer_get_missing_page _protocol node client coordinator threshold + _committee_members = + let root_hash = + "00649d431e829f4adc68edecb8d8d8071154b57086cc124b465f6f6600a4bc91c7" + in + let root_hash_stream_promise = + wait_for_root_hash_pushed_to_data_streamer coordinator root_hash + in + let* hex_root_hash = + init_hex_root_hash ~payload:"test payload abc 123" coordinator + in + assert (root_hash = Hex.show hex_root_hash) ; + let* () = root_hash_stream_promise in + let observer = + Dac_node.create_legacy ~threshold ~committee_members:[] ~node ~client () + in + let* _ = Dac_node.init_config observer in + let () = Legacy.set_coordinator observer coordinator in + let* () = Dac_node.run observer in + let* () = + assert_lwt_failure + ~__LOC__ + "Expected retrieve_preimage" + (RPC.call + observer + (Rollup.Dac.RPC.dac_retrieve_preimage (Hex.show hex_root_hash))) + in + let* missing_page = + RPC.call observer (Rollup.Dac.RPC.get_missing_page ~hex_root_hash) + in + let* coordinator_page = + RPC.call + coordinator + (Rollup.Dac.RPC.dac_retrieve_preimage (Hex.show hex_root_hash)) + in + check_preimage coordinator_page missing_page ; + let* observer_preimage = + RPC.call + observer + (Rollup.Dac.RPC.dac_retrieve_preimage (Hex.show hex_root_hash)) + in + check_preimage coordinator_page observer_preimage ; + unit + let register ~protocols = (* Tests with layer1 and dac nodes *) test_dac_node_startup protocols ; @@ -1209,4 +1258,11 @@ let register ~protocols = ~tags:["dac"; "dac_node"] "dac_store_member_signature" Signature_manager.Coordinator.test_handle_store_signature + protocols ; + scenario_with_layer1_and_legacy_dac_nodes + ~threshold:0 + ~committee_members:1 + ~tags:["dac"; "dac_node"] + "dac_observer_get_missing_page" + test_observer_get_missing_page protocols