diff --git a/src/lib_dac/RPC_services.ml b/src/lib_dac/RPC_services.ml index 47a912134410a1d125b06f525486bd512e5658c1..7fe6eb452b3057bb61f0b1d325eb9e840f5cb301 100644 --- a/src/lib_dac/RPC_services.ml +++ b/src/lib_dac/RPC_services.ml @@ -136,3 +136,14 @@ let get_health_ready = ~query:Tezos_rpc.Query.empty ~output:Data_encoding.bool Tezos_rpc.Path.(open_root / "health" / "ready") + +module V1 = struct + let v1_prefix = Api_version.v1_prefix + + let get_pages = + 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.(v1_prefix / "pages" /: Dac_plugin.raw_hash_rpc_arg) +end diff --git a/src/lib_dac/RPC_services.mli b/src/lib_dac/RPC_services.mli index f11e7be6a766ae65d48e5261eb185c3a31f4c65d..e10d84d47f1616eecef7cd97e9e937797983540b 100644 --- a/src/lib_dac/RPC_services.mli +++ b/src/lib_dac/RPC_services.mli @@ -23,9 +23,6 @@ (* *) (*****************************************************************************) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/5579 - Add support for first publicly released DAC API [V1]. *) - (** [V0] is experimental DAC API. [V0] is deprecated, however for the time being the API will be binding. It will be used by 1M/tps demo. The plan is to remove it once we get rid of the @@ -117,3 +114,19 @@ val get_health_live : and fail with [tzfail Dac_node_not_ready] otherwise. *) val get_health_ready : ([`GET], unit, unit, unit, unit, bool) Tezos_rpc.Service.service + +(** [V1] is a second major DAC API release. + [V1] API is work in progress. Do not use! *) +module V1 : sig + (** "GET v1/pages" requests the preimage of hash, consisting of a + single page, from cctxt. When the request succeeds, the raw page will be + returned as a sequence of bytes. *) + val get_pages : + ( [`GET], + unit, + unit * Dac_plugin.raw_hash, + unit, + unit, + Bytes.t ) + Tezos_rpc.Service.service +end diff --git a/src/lib_dac/api_version.ml b/src/lib_dac/api_version.ml index b8f0d77de4d127b0d76e8a7798f4189ea5cb5e04..e8a539ed247612fa4b6c7ec19fe80979abf5284a 100644 --- a/src/lib_dac/api_version.ml +++ b/src/lib_dac/api_version.ml @@ -24,8 +24,12 @@ (* *) (*****************************************************************************) -type t = V0 +type t = V0 | V1 -let to_rpc_path_prefix = function V0 -> Tezos_rpc.Path.(open_root / "v0") +let to_rpc_path_prefix = function + | V0 -> Tezos_rpc.Path.(open_root / "v0") + | V1 -> Tezos_rpc.Path.(open_root / "v1") let v0_prefix = to_rpc_path_prefix V0 + +let v1_prefix = to_rpc_path_prefix V1 diff --git a/src/lib_dac/api_version.mli b/src/lib_dac/api_version.mli index 597d9f739ef2c5409db11767c58c2894ad96f702..f8ec595dd82bc0e0d2e5f5eeb49f8a8447d97cf4 100644 --- a/src/lib_dac/api_version.mli +++ b/src/lib_dac/api_version.mli @@ -27,7 +27,14 @@ (** [Api_version] module is used for versioning DAC API. *) (** [t] type is used to version DAC API. *) -type t = V0 (** [V0] is experimental DAC API. Use at your own risk! *) +type t = + | V0 (** [V0] is experimental DAC API. Use at your own risk! *) + | V1 + (** [V1] is a second major DAC API release. + [V1] API is work in progress. Do not use! *) (** [v0_prefix] is [V0] API RPC prefix. *) val v0_prefix : (unit, unit) Tezos_rpc.Path.t + +(** [v1_prefix] is [V1] API RPC prefix. *) +val v1_prefix : (unit, unit) Tezos_rpc.Path.t diff --git a/src/lib_dac_node/RPC_server.ml b/src/lib_dac_node/RPC_server.ml index 3626fad3379cb61b6875b73d9b0e85ff08c4eadb..10023c2be93ca86d82e2f8be860dfd343c24f18d 100644 --- a/src/lib_dac_node/RPC_server.ml +++ b/src/lib_dac_node/RPC_server.ml @@ -144,6 +144,7 @@ let handle_get_verify_signature dac_plugin public_keys_opt encoded_l1_message = signature witnesses +(** [handle_get_preimage] is shared by both [V0] and [V1] API. *) let handle_get_preimage dac_plugin page_store raw_hash = let open Lwt_result_syntax in let*? hash = Dac_plugin.raw_to_hash dac_plugin raw_hash in @@ -245,6 +246,12 @@ let register_get_missing_page dac_plugin page_store cctxt = (fun root_hash () () -> handle_get_missing_page cctxt page_store dac_plugin root_hash) +let register_get_pages dac_plugin page_store = + add_service + Tezos_rpc.Directory.register1 + RPC_services.V1.get_pages + (fun hash () () -> handle_get_preimage dac_plugin page_store hash) + module Coordinator = struct let handle_post_preimage dac_plugin page_store hash_streamer payload = let open Lwt_result_syntax in @@ -375,11 +382,14 @@ module Coordinator = struct rw_store page_store |> register_get_certificate rw_store dac_plugin + |> register_get_pages dac_plugin page_store end module Committee_member = struct let dynamic_rpc_dir dac_plugin page_store = - Tezos_rpc.Directory.empty |> register_get_preimage dac_plugin page_store + Tezos_rpc.Directory.empty + |> register_get_preimage dac_plugin page_store + |> register_get_pages dac_plugin page_store end module Observer = struct @@ -387,6 +397,7 @@ module Observer = struct Tezos_rpc.Directory.empty |> register_get_preimage dac_plugin page_store |> register_get_missing_page dac_plugin page_store coordinator_cctxt + |> register_get_pages dac_plugin page_store end module Legacy = struct diff --git a/tezt/lib_tezos/dac_rpc.ml b/tezt/lib_tezos/dac_rpc.ml index 37a4e4905db31b4135e1463ae8a8374e8fb3eac5..cc747722c8eaff8066f9004a1d1d489c38d65e66 100644 --- a/tezt/lib_tezos/dac_rpc.ml +++ b/tezt/lib_tezos/dac_rpc.ml @@ -110,3 +110,10 @@ end let get_health_live = make GET ["health"; "live"] JSON.as_bool let get_health_ready = make GET ["health"; "ready"] JSON.as_bool + +module V1 = struct + let api_prefix = "v1" + + let get_pages page_hash = + make GET [api_prefix; "pages"; page_hash] JSON.as_string +end diff --git a/tezt/lib_tezos/dac_rpc.mli b/tezt/lib_tezos/dac_rpc.mli index b6b1e77f5ce2faa212c327a3e56631729b0617dd..003a714e7ac12c1548d75428cee5299932232712 100644 --- a/tezt/lib_tezos/dac_rpc.mli +++ b/tezt/lib_tezos/dac_rpc.mli @@ -85,3 +85,12 @@ val get_health_live : (Dac_node.t, bool) RPC_core.t [Node_context.get_status cctxt] is [Ready] and fail with [tzfail Dac_node_not_ready] otherwise. *) val get_health_ready : (Dac_node.t, bool) RPC_core.t + +(** [V1] is a second major DAC API release which is currently work in progress. *) +module V1 : sig + (** [get_pages hash] requests the preimage of hash, consisting of a + single page, from cctxt. When the request succeeds, the raw page will be + returned as a sequence of bytes. This is achieved by calling + "GET v1/pages". *) + val get_pages : string -> (Dac_node.t, string) RPC_core.t +end diff --git a/tezt/tests/dac.ml b/tezt/tests/dac.ml index 82de8e1c2529e230845d7b25f6595542ab815e03..fbbe642ee5667fd81019f19092d44f3cb09eadf5 100644 --- a/tezt/tests/dac.ml +++ b/tezt/tests/dac.ml @@ -306,6 +306,8 @@ let sample_payload example_filename = let root_hash = JSON.(json |-> "root_hash" |> as_string) in (payload, root_hash) +let decode_hex_string_to_bytes s = Hex.to_string (`Hex s) + (** This modules encapsulate tests for DAC nodes when running in legacy node. It includes tests where we have two dac nodes running in the legacy mode interacting with each other. As such one node normally tries @@ -2615,6 +2617,36 @@ let register_with_unsupported_protocol ~protocols = Observer.test_dac_not_ready_without_protocol protocols ; Member.test_dac_not_ready_without_protocol protocols +(** [V1_API] is a test suite for [V1] API. *) +module V1_API = struct + (** Test "v1/page" endpoint. *) + let test_get_pages Scenarios.{coordinator_node; _} = + (* 1. Post [payload] to [Coordinator] and receive [root_hash]. + 2. Asumming that [payload] fits into one [Contents] page + retrieving it via [GET v1/pages/[root_hash]] should result + in initial payload. *) + let payload = "test" in + (* Assert that [expected] payload fits onto one [Contents] page. + Since [init_hex_root_hash] calls "POST v0/preimage" that uses + [Merkle_tree.V0] serialization, [max_page_size] is 4096 bytes, + out of which 5 bytes are for preamble, meaning inital payload + can be at max 4091 bytes in size for the above to hold. *) + let () = assert (Bytes.length (String.to_bytes payload) < 4091) in + let* root_hash = + (* TODO https://gitlab.com/tezos/tezos/-/issues/5671 + Once we have "PUT v1/preimage" we should use a call to [V1] api here + instead. *) + RPC.call coordinator_node (Dac_rpc.V0.Coordinator.post_preimage ~payload) + in + let* raw = RPC.call coordinator_node (Dac_rpc.V1.get_pages @@ root_hash) in + let remove_preamble s = + let preamle_size = 5 in + String.(sub s preamle_size (length s - preamle_size)) + in + let actual = remove_preamble (decode_hex_string_to_bytes raw) in + return @@ check_preimage payload actual +end + let register ~protocols = (* Tests with layer1 and dac nodes *) Legacy.test_dac_node_startup protocols ; @@ -2749,4 +2781,12 @@ let register ~protocols = (Full_infrastructure.test_client ~send_payload_from_file:true) protocols ; Tx_kernel_e2e.test_tx_kernel_e2e_with_dac_observer_synced_with_dac protocols ; - Tx_kernel_e2e.test_tx_kernel_e2e_with_dac_observer_missing_pages protocols + Tx_kernel_e2e.test_tx_kernel_e2e_with_dac_observer_missing_pages protocols ; + scenario_with_full_dac_infrastructure + ~__FILE__ + ~observers:0 + ~committee_size:0 + ~tags:["dac"; "dac_node"] + "test v1/get_pages" + V1_API.test_get_pages + protocols