diff --git a/src/bin_dal_node/RPC_server.ml b/src/bin_dal_node/RPC_server.ml index d59f54e709998164f7db4352acb5dfa6b68622cd..546f048fdbaef3a53e450511b911758de0361986 100644 --- a/src/bin_dal_node/RPC_server.ml +++ b/src/bin_dal_node/RPC_server.ml @@ -35,6 +35,42 @@ let call_handler2 ctxt handler = let store = Node_context.get_store ctxt in handler store ready_ctxt +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." + ~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)) ; + 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 = Errors.to_option_tzresult @@ -59,6 +95,28 @@ 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 = + let open Result_syntax in + match Cryptobox.polynomial_from_slot cryptobox slot with + | Error (`Slot_wrong_size msg) -> + (* Storage consistency ensures we can always compute the + 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] 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; _} -> let open Lwt_result_syntax in @@ -70,19 +128,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 @@ -96,6 +144,39 @@ 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 + 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 + Ok (Bytes.of_string (slot ^ padding)) + in + let* commitment = + Slot_manager.add_commitment store slot cryptobox |> Errors.to_tzresult + in + let*? commitment_proof = commitment_proof_from_slot cryptobox slot 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 diff --git a/src/lib_dal_node_services/services.ml b/src/lib_dal_node_services/services.ml index cfff7f466cb5c287a0a6c8f5a0cc1249c1d14058..dc119b96b5ce76d51cc24e3eae50e312a0485702 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 afd297e7396ccec2c4f76994e12fb59d7c5fde45..ec920030b14a7f8e9630507e19e19a77f6d6d77d 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 60cde0082e5ade09cf3619e7be17652cdeef16ae..4abbad87a6753ab8051188faecf8b81cdc558e41 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 b44f00d471d194134e1209de26e67780528269d9..5699a060f13072d1a8d824b76f886be66f768fe2 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