From 0c05d0324b4eaac1d6ba4ba7c4aefa991481f572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Fri, 8 Dec 2023 12:20:40 +0100 Subject: [PATCH 1/5] Dal/Node: Add a service for posting a new slot --- src/lib_dal_node_services/services.ml | 22 ++++++++++++++++++++++ src/lib_dal_node_services/services.mli | 13 +++++++++++++ src/lib_dal_node_services/types.ml | 18 ++++++++++++++++++ src/lib_dal_node_services/types.mli | 2 ++ 4 files changed, 55 insertions(+) diff --git a/src/lib_dal_node_services/services.ml b/src/lib_dal_node_services/services.ml index cfff7f466cb5..dc119b96b5ce 100644 --- a/src/lib_dal_node_services/services.ml +++ b/src/lib_dal_node_services/services.ml @@ -54,6 +54,28 @@ let post_commitment : ~output:Cryptobox.Commitment.encoding Tezos_rpc.Path.(open_root / "commitments") +let post_slot : + < meth : [`POST] + ; input : Cryptobox.slot + ; output : Cryptobox.commitment * Cryptobox.commitment_proof + ; prefix : unit + ; params : unit + ; query : < padding : char > > + service = + Tezos_rpc.Service.post_service + ~description: + "Post a slot to the DAL node, computes its commitment and commitment \ + proof, then computes the correspoding shards with their proof. The \ + result of this RPC can be directly used to publish a slot header." + ~query:Types.slot_query + ~input:slot_encoding + ~output: + Data_encoding.( + obj2 + (req "commitment" Cryptobox.Commitment.encoding) + (req "commitment_proof" Cryptobox.Commitment_proof.encoding)) + Tezos_rpc.Path.(open_root / "slot") + let patch_commitment : < meth : [`PATCH] ; input : slot_id diff --git a/src/lib_dal_node_services/services.mli b/src/lib_dal_node_services/services.mli index afd297e7396c..ec920030b14a 100644 --- a/src/lib_dal_node_services/services.mli +++ b/src/lib_dal_node_services/services.mli @@ -54,6 +54,19 @@ val post_commitment : ; query : unit > service +(** This RPC should be used by a slot producer. It allows to produce a + commitment, a commitment proof and the shards from a slot. A + padding is added if the slot is not of the expected size + ([slot_size] from the Cryptobox). *) +val post_slot : + < meth : [`POST] + ; input : Cryptobox.slot + ; output : Cryptobox.commitment * Cryptobox.commitment_proof + ; prefix : unit + ; params : unit + ; query : < padding : char > > + service + (** Associate a commitment to a level and a slot index. See {!val: Slot_manager.associate_slot_id_with_commitment} for more details. *) val patch_commitment : diff --git a/src/lib_dal_node_services/types.ml b/src/lib_dal_node_services/types.ml index 60cde0082e5a..4abbad87a675 100644 --- a/src/lib_dal_node_services/types.ml +++ b/src/lib_dal_node_services/types.ml @@ -454,6 +454,24 @@ let header_status_arg = let construct = Data_encoding.Binary.to_string_exn header_status_encoding in Tezos_rpc.Arg.make ~name:"header_status" ~destruct ~construct () +let char = + Resto.Arg.make + ~name:"char" + ~destruct:(fun str -> + if String.length str = 1 then Ok (String.get str 0) + else Error "A single character string is expected") + ~construct:(fun c -> String.make 1 c) + () + +let slot_query = + let open Tezos_rpc.Query in + query (fun padding -> + object + method padding = padding + end) + |+ field "padding" char '\x00' (fun obj -> obj#padding) + |> seal + let wait_query = let open Tezos_rpc.Query in query (fun wait -> diff --git a/src/lib_dal_node_services/types.mli b/src/lib_dal_node_services/types.mli index b44f00d471d1..5699a060f130 100644 --- a/src/lib_dal_node_services/types.mli +++ b/src/lib_dal_node_services/types.mli @@ -256,6 +256,8 @@ type with_proof = {with_proof : bool} val slot_id_query : (level option * shard_index option) Resto.Query.t +val slot_query : < padding : char > Resto.Query.t + val wait_query : < wait : bool > Resto.Query.t val connected_query : < connected : bool > Resto.Query.t -- GitLab From e46b5f7121046de65d702a64c185ce6d73533cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Fri, 8 Dec 2023 14:17:28 +0100 Subject: [PATCH 2/5] DAL/Node: Implement `post_slot` RPC --- src/bin_dal_node/RPC_server.ml | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/bin_dal_node/RPC_server.ml b/src/bin_dal_node/RPC_server.ml index d59f54e70999..e584bdc47d23 100644 --- a/src/bin_dal_node/RPC_server.ml +++ b/src/bin_dal_node/RPC_server.ml @@ -96,6 +96,51 @@ module Slots_handlers = struct ~with_proof |> Errors.to_option_tzresult) + let post_slot ctxt () query slot = + call_handler2 + ctxt + (fun store {cryptobox; shards_proofs_precomputation; proto_parameters; _} + -> + let open Lwt_result_syntax in + let slot_size = proto_parameters.cryptobox_parameters.slot_size in + let slot_length = String.length slot in + let*? slot = + if slot_length > slot_size then (* Raise an error *) + assert false + else if slot_length = slot_size then Ok (Bytes.of_string slot) + else + let padding = String.make (slot_size - slot_length) query#padding in + Ok (Bytes.of_string (slot ^ padding)) + in + let* commitment = + Slot_manager.add_commitment store slot cryptobox |> Errors.to_tzresult + in + let*? commitment_proof = + match Cryptobox.polynomial_from_slot cryptobox slot with + | Error _ -> + (* Storage consistency ensures we can always compute the + polynomial from the slot. *) + assert false + | Ok polynomial -> ( + match Cryptobox.prove_commitment cryptobox polynomial with + (* [polynomial] was produced with the parameters from + [cryptobox], thus we can always compute the proof from + [polynomial]. *) + | Error _ -> assert false + | Ok proof -> Ok proof) + in + (* Cannot return None *) + let* (_ : unit option) = + Slot_manager.add_commitment_shards + ~shards_proofs_precomputation + store + cryptobox + commitment + ~with_proof:true + |> Errors.to_option_tzresult + in + return (commitment, commitment_proof)) + let get_commitment_by_published_level_and_index ctxt level slot_index () () = call_handler1 ctxt (fun store -> Slot_manager.get_commitment_by_published_level_and_index -- GitLab From 22b13402d3164fc9002d22753bd17e89400e89fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Tue, 12 Dec 2023 09:18:42 +0100 Subject: [PATCH 3/5] DAL/Node: Factorize computation of commitment proof --- src/bin_dal_node/RPC_server.ml | 47 +++++++++++++++------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/bin_dal_node/RPC_server.ml b/src/bin_dal_node/RPC_server.ml index e584bdc47d23..d098911f2491 100644 --- a/src/bin_dal_node/RPC_server.ml +++ b/src/bin_dal_node/RPC_server.ml @@ -59,6 +59,22 @@ module Slots_handlers = struct Slot_manager.get_commitment_slot store cryptobox commitment |> to_option_tzresult) + (* This function assumes the slot is valid since we already have + computed a commitment for it. *) + let commitment_proof_from_slot cryptobox slot = + match Cryptobox.polynomial_from_slot cryptobox slot with + | Error _ -> + (* Storage consistency ensures we can always compute the + polynomial from the slot. *) + assert false + | Ok polynomial -> ( + match Cryptobox.prove_commitment cryptobox polynomial with + (* [polynomial] was produced with the parameters from + [cryptobox], thus we can always compute the proof from + [polynomial]. *) + | Error _ -> assert false + | Ok proof -> proof) + let get_commitment_proof ctxt commitment () () = call_handler2 ctxt (fun store {cryptobox; _} -> let open Lwt_result_syntax in @@ -70,19 +86,9 @@ module Slots_handlers = struct in match slot with | None -> return_none - | Some slot -> ( - match Cryptobox.polynomial_from_slot cryptobox slot with - | Error _ -> - (* Storage consistency ensures we can always compute the - polynomial from the slot. *) - assert false - | Ok polynomial -> ( - match Cryptobox.prove_commitment cryptobox polynomial with - (* [polynomial] was produced with the parameters from - [cryptobox], thus we can always compute the proof from - [polynomial]. *) - | Error _ -> assert false - | Ok proof -> return_some proof))) + | Some slot -> + let proof = commitment_proof_from_slot cryptobox slot in + return_some proof) let put_commitment_shards ctxt commitment () Types.{with_proof} = call_handler2 @@ -115,20 +121,7 @@ module Slots_handlers = struct let* commitment = Slot_manager.add_commitment store slot cryptobox |> Errors.to_tzresult in - let*? commitment_proof = - match Cryptobox.polynomial_from_slot cryptobox slot with - | Error _ -> - (* Storage consistency ensures we can always compute the - polynomial from the slot. *) - assert false - | Ok polynomial -> ( - match Cryptobox.prove_commitment cryptobox polynomial with - (* [polynomial] was produced with the parameters from - [cryptobox], thus we can always compute the proof from - [polynomial]. *) - | Error _ -> assert false - | Ok proof -> Ok proof) - in + let commitment_proof = commitment_proof_from_slot cryptobox slot in (* Cannot return None *) let* (_ : unit option) = Slot_manager.add_commitment_shards -- GitLab From 79b5a87bb93bbfbbf1aa04f80658efc13c67016b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Tue, 12 Dec 2023 09:35:37 +0100 Subject: [PATCH 4/5] DAL/Node: Better error messages while computing commitment proof --- src/bin_dal_node/RPC_server.ml | 40 +++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/bin_dal_node/RPC_server.ml b/src/bin_dal_node/RPC_server.ml index d098911f2491..7f3f5b402668 100644 --- a/src/bin_dal_node/RPC_server.ml +++ b/src/bin_dal_node/RPC_server.ml @@ -35,6 +35,24 @@ let call_handler2 ctxt handler = let store = Node_context.get_store ctxt in handler store ready_ctxt +type error += Cryptobox_error of string * string + +let () = + register_error_kind + `Permanent + ~id:"cryptobox_error" + ~title:"cryptobox error" + ~description:"A wrapper around an error raised by the cryptobox of the DAL" + ~pp:(fun fmt (f, msg) -> + Format.fprintf + fmt + "The DAL cryptobox function '%s' failed with:@.'%s'" + f + msg) + Data_encoding.(obj2 (req "function_name" string) (req "explanation" string)) + (function Cryptobox_error (f, msg) -> Some (f, msg) | _ -> None) + (fun (f, msg) -> Cryptobox_error (f, msg)) + module Slots_handlers = struct let to_option_tzresult r = Errors.to_option_tzresult @@ -62,18 +80,24 @@ module Slots_handlers = struct (* This function assumes the slot is valid since we already have computed a commitment for it. *) let commitment_proof_from_slot cryptobox slot = + let open Result_syntax in match Cryptobox.polynomial_from_slot cryptobox slot with - | Error _ -> + | Error (`Slot_wrong_size msg) -> (* Storage consistency ensures we can always compute the - polynomial from the slot. *) - assert false + polynomial from the slot. But let's returne an errror to be defensive. *) + tzfail (Cryptobox_error ("polynomial_from_slot", msg)) | Ok polynomial -> ( match Cryptobox.prove_commitment cryptobox polynomial with (* [polynomial] was produced with the parameters from [cryptobox], thus we can always compute the proof from - [polynomial]. *) - | Error _ -> assert false - | Ok proof -> proof) + [polynomial] except if an error happens with the loading of the SRS. *) + | Error (`Invalid_degree_strictly_less_than_expected _) -> + tzfail + (Cryptobox_error + ( "prove_commitment", + "Unexpected error. Maybe an issue with the SRS from the DAL \ + node." )) + | Ok proof -> return proof) let get_commitment_proof ctxt commitment () () = call_handler2 ctxt (fun store {cryptobox; _} -> @@ -87,7 +111,7 @@ module Slots_handlers = struct match slot with | None -> return_none | Some slot -> - let proof = commitment_proof_from_slot cryptobox slot in + let*? proof = commitment_proof_from_slot cryptobox slot in return_some proof) let put_commitment_shards ctxt commitment () Types.{with_proof} = @@ -121,7 +145,7 @@ module Slots_handlers = struct let* commitment = Slot_manager.add_commitment store slot cryptobox |> Errors.to_tzresult in - let commitment_proof = commitment_proof_from_slot cryptobox slot in + let*? commitment_proof = commitment_proof_from_slot cryptobox slot in (* Cannot return None *) let* (_ : unit option) = Slot_manager.add_commitment_shards -- GitLab From a33c468749ab4c6aca394cc686627c39bbf3197e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Tue, 12 Dec 2023 09:44:14 +0100 Subject: [PATCH 5/5] DAL/Node: Raise a proper error when slot size is too large --- src/bin_dal_node/RPC_server.ml | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/bin_dal_node/RPC_server.ml b/src/bin_dal_node/RPC_server.ml index 7f3f5b402668..546f048fdbae 100644 --- a/src/bin_dal_node/RPC_server.ml +++ b/src/bin_dal_node/RPC_server.ml @@ -35,14 +35,16 @@ let call_handler2 ctxt handler = let store = Node_context.get_store ctxt in handler store ready_ctxt -type error += Cryptobox_error of string * string +type error += + | Cryptobox_error of string * string + | Post_slot_too_large of {expected : int; got : int} let () = register_error_kind `Permanent ~id:"cryptobox_error" ~title:"cryptobox error" - ~description:"A wrapper around an error raised by the cryptobox of the DAL" + ~description:"A wrapper around an error raised by the cryptobox of the DAL." ~pp:(fun fmt (f, msg) -> Format.fprintf fmt @@ -51,7 +53,23 @@ let () = msg) Data_encoding.(obj2 (req "function_name" string) (req "explanation" string)) (function Cryptobox_error (f, msg) -> Some (f, msg) | _ -> None) - (fun (f, msg) -> Cryptobox_error (f, msg)) + (fun (f, msg) -> Cryptobox_error (f, msg)) ; + register_error_kind + `Permanent + ~id:"post_slot_too_large" + ~title:"Post slot too large" + ~description: + "The length of posted data exceeds the expected size of DAL slots." + ~pp:(fun fmt (expected, got) -> + Format.fprintf + fmt + "The RPC expects a slot_size of at most '%d'. Got: '%d' expected got" + expected + got) + Data_encoding.(obj2 (req "expected" int31) (req "got" int31)) + (function + | Post_slot_too_large {expected; got} -> Some (expected, got) | _ -> None) + (fun (expected, got) -> Post_slot_too_large {expected; got}) module Slots_handlers = struct let to_option_tzresult r = @@ -135,8 +153,9 @@ module Slots_handlers = struct let slot_size = proto_parameters.cryptobox_parameters.slot_size in let slot_length = String.length slot in let*? slot = - if slot_length > slot_size then (* Raise an error *) - assert false + if slot_length > slot_size then + Result_syntax.tzfail + (Post_slot_too_large {expected = slot_size; got = slot_length}) else if slot_length = slot_size then Ok (Bytes.of_string slot) else let padding = String.make (slot_size - slot_length) query#padding in -- GitLab