From 3f388873fc69d1085c6a0278a7b3af346c81fe87 Mon Sep 17 00:00:00 2001 From: Andrea Cerone Date: Tue, 22 Nov 2022 12:26:21 +0000 Subject: [PATCH 1/7] Dac/Plugin: add signature manager module --- .../lib_dal/dac_signature_manager.ml | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/proto_alpha/lib_dal/dac_signature_manager.ml diff --git a/src/proto_alpha/lib_dal/dac_signature_manager.ml b/src/proto_alpha/lib_dal/dac_signature_manager.ml new file mode 100644 index 000000000000..f38c3d991483 --- /dev/null +++ b/src/proto_alpha/lib_dal/dac_signature_manager.ml @@ -0,0 +1,147 @@ +(*****************************************************************************) +(* *) +(* 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)) + (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)) + (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)) + (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: + 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 b58_root_hash = Hashing_scheme.to_b58check root_hash in + match bytes_to_sign with + | None -> tzfail @@ Cannot_convert_root_page_hash_to_bytes b58_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 b58_root_hash + | Some signature -> return @@ (signature, witnesses)) +end + +module Reveal_hash = Make (Sc_rollup_reveal_hash) -- GitLab From eb077f88075d8876109017a3882a029b3fc10681 Mon Sep 17 00:00:00 2001 From: Andrea Cerone Date: Tue, 22 Nov 2022 12:26:52 +0000 Subject: [PATCH 2/7] Dac/Plugin: add external message manager --- .../lib_dal/dac_external_message_manager.ml | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/proto_alpha/lib_dal/dac_external_message_manager.ml diff --git a/src/proto_alpha/lib_dal/dac_external_message_manager.ml b/src/proto_alpha/lib_dal/dac_external_message_manager.ml new file mode 100644 index 000000000000..2076f79b31a2 --- /dev/null +++ b/src/proto_alpha/lib_dal/dac_external_message_manager.ml @@ -0,0 +1,114 @@ +(*****************************************************************************) +(* *) +(* 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.Error_monad + +type error += Could_not_serialize_rollup_external_message of string + +let () = + register_error_kind + `Permanent + ~id:"dac_could_not_serialize_rollup_external_message" + ~title:"Could not serialize rollup external message" + ~description: + "Serialization of rollup external message containing Dac root page hash \ + failed" + ~pp:(fun ppf b58_hash -> + Format.fprintf + ppf + "Serialization of rollup external message containing Dac root page \ + hash %sfailed" + b58_hash) + Data_encoding.(obj1 (req "hash" string)) + (function + | Could_not_serialize_rollup_external_message b58_hash -> Some b58_hash + | _ -> None) + (fun b58_hash -> Could_not_serialize_rollup_external_message b58_hash) + +module type REVEAL_HASH = module type of Sc_rollup_reveal_hash + +module Make + (Hashing_scheme : REVEAL_HASH) (Encoding_metadata : sig + val tag : int + + 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 = + Data_encoding.( + conv + (function + | Dac_message {root_hash; signature; witnesses} -> + (root_hash, signature, witnesses)) + (fun (root_hash, signature, witnesses) -> + Dac_message {root_hash; signature; witnesses}) + (obj3 + (req "root_hash" Hashing_scheme.encoding) + (req "signature" Tezos_crypto.Aggregate_signature.encoding) + (req "witnesses" Bitset.encoding))) + + let dac_message_encoding = + Data_encoding.( + union + ~tag_size:`Uint8 + [ + case + ~title:("dac_message_" ^ Encoding_metadata.title) + (Tag Encoding_metadata.tag) + untagged_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 + match res with + | Ok bytes -> Ok bytes + | Error _ -> + error + @@ Could_not_serialize_rollup_external_message + (Hashing_scheme.to_b58check root_hash) + + let of_bytes encoded_message = + Data_encoding.Binary.of_bytes_opt dac_message_encoding encoded_message +end + +module Reveal_hash = + Make + (Sc_rollup_reveal_hash) + (struct + let tag = 42 + + let title = "reveal_hash_v0" + end) -- GitLab From 809408cbea19fa71e307295c80e5a2af6d72d2c0 Mon Sep 17 00:00:00 2001 From: Andrea Cerone Date: Tue, 22 Nov 2022 12:27:32 +0000 Subject: [PATCH 3/7] Dac/Plugin: group dac managers in single module --- src/proto_alpha/lib_dal/dac_manager.ml | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/proto_alpha/lib_dal/dac_manager.ml diff --git a/src/proto_alpha/lib_dal/dac_manager.ml b/src/proto_alpha/lib_dal/dac_manager.ml new file mode 100644 index 000000000000..e020a2af5b9e --- /dev/null +++ b/src/proto_alpha/lib_dal/dac_manager.ml @@ -0,0 +1,31 @@ +(*****************************************************************************) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +module Reveal_hash = struct + module Hashing_scheme = Dac_pages_encoding.Merkle_tree.V0 + module Storage = Dac_preimage_data_manager.Reveal_hash + module Signatures = Dac_signature_manager.Reveal_hash + module External_message = Dac_external_message_manager.Reveal_hash +end -- GitLab From 1eb80423fe2dbf699503bf05071b5341812673d4 Mon Sep 17 00:00:00 2001 From: Andrea Cerone Date: Tue, 22 Nov 2022 12:31:36 +0000 Subject: [PATCH 4/7] Dac/Plugin: compute external message to inject to L1 --- src/bin_dal_node/RPC_server.ml | 13 ++- src/bin_dal_node/daemon.ml | 15 +++- src/lib_dal_node/dal_plugin.ml | 7 +- src/lib_dal_node/dal_plugin.mli | 7 +- src/proto_alpha/lib_dal/RPC.ml | 83 +++++++++++++++---- src/proto_alpha/lib_dal/dac_manager.ml | 1 - .../lib_dal/dac_signature_manager.ml | 10 +-- 7 files changed, 107 insertions(+), 29 deletions(-) diff --git a/src/bin_dal_node/RPC_server.ml b/src/bin_dal_node/RPC_server.ml index 685da56c3f74..efcdcbcbb0fd 100644 --- a/src/bin_dal_node/RPC_server.ml +++ b/src/bin_dal_node/RPC_server.ml @@ -88,9 +88,10 @@ let register ctxt = register_new ctxt (register_legacy ctxt) let merge dir plugin_dir = Tezos_rpc.Directory.merge dir plugin_dir -let start configuration ctxt = +let start configuration cctxt ctxt dac_pks_opt dac_sk_uris = let open Lwt_syntax in - let Configuration.{rpc_addr; rpc_port; dac = {reveal_data_dir; _}; _} = + let Configuration. + {rpc_addr; rpc_port; dac = {reveal_data_dir; threshold; _}; _} = configuration in let dir = register ctxt in @@ -99,7 +100,13 @@ let start configuration ctxt = Tezos_rpc.Directory.register_dynamic_directory dir plugin_prefix (fun () -> match Node_context.get_status ctxt with | Ready {plugin = (module Plugin); _} -> - Lwt.return (Plugin.RPC.rpc_services ~reveal_data_dir) + Lwt.return + (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_addr = P2p_addr.of_string_exn rpc_addr in diff --git a/src/bin_dal_node/daemon.ml b/src/bin_dal_node/daemon.ml index 42c97b576815..b2770495212d 100644 --- a/src/bin_dal_node/daemon.ml +++ b/src/bin_dal_node/daemon.ml @@ -226,10 +226,21 @@ let run ~data_dir cctxt = let* () = Dac_manager.Storage.ensure_reveal_data_dir_exists config.dac.reveal_data_dir in - let* _dac_list = Dac_manager.Keys.get_keys cctxt config in + let* dac_accounts = Dac_manager.Keys.get_keys cctxt config in + let dac_pks_opt, dac_sk_uris = + dac_accounts + |> List.map (fun account_opt -> + match account_opt with + | None -> (None, None) + | Some (_pkh, pk_opt, sk_uri) -> (pk_opt, Some sk_uri)) + |> List.split + in let*! store = Store.init config in let ctxt = Node_context.init config store in - let* rpc_server = RPC_server.(start config ctxt) in + + let* rpc_server = + RPC_server.(start config cctxt ctxt dac_pks_opt dac_sk_uris) + in let _ = RPC_server.install_finalizer rpc_server in let*! () = Event.(emit rpc_server_is_ready (config.rpc_addr, config.rpc_port)) diff --git a/src/lib_dal_node/dal_plugin.ml b/src/lib_dal_node/dal_plugin.ml index 4efd23217224..ea0c34d7be42 100644 --- a/src/lib_dal_node/dal_plugin.ml +++ b/src/lib_dal_node/dal_plugin.ml @@ -39,7 +39,12 @@ module type T = sig module RPC : sig val rpc_services : - reveal_data_dir:string -> unit Tezos_rpc.Directory.directory + 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 end diff --git a/src/lib_dal_node/dal_plugin.mli b/src/lib_dal_node/dal_plugin.mli index 94b985f5269f..4f7df8a094d8 100644 --- a/src/lib_dal_node/dal_plugin.mli +++ b/src/lib_dal_node/dal_plugin.mli @@ -39,7 +39,12 @@ module type T = sig module RPC : sig val rpc_services : - reveal_data_dir:string -> unit Tezos_rpc.Directory.directory + 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 end diff --git a/src/proto_alpha/lib_dal/RPC.ml b/src/proto_alpha/lib_dal/RPC.ml index 3f3684d6b197..473071b25727 100644 --- a/src/proto_alpha/lib_dal/RPC.ml +++ b/src/proto_alpha/lib_dal/RPC.ml @@ -24,6 +24,21 @@ (*****************************************************************************) open Environment +open Error_monad + +type error += Cannot_construct_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) module Registration = struct let register0_noctxt ~chunked s f dir = @@ -39,7 +54,11 @@ module DAC = struct (req "payload" Data_encoding.(bytes Hex)) (req "pagination_scheme" Dac_pages_encoding.pagination_scheme_encoding)) - let store_preimage_response_encoding = Protocol.Sc_rollup_reveal_hash.encoding + let store_preimage_response_encoding = + Data_encoding.( + obj2 + (req "root_hash" Protocol.Sc_rollup_reveal_hash.encoding) + (req "external_message" (bytes Hex))) module S = struct let dac_store_preimage = @@ -51,30 +70,62 @@ module DAC = struct RPC_path.(open_root / "dac" / "store_preimage") end - let handle_serialize_dac_store_preimage reveal_data_dir input = + 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 for_each_page (hash, page_contents) = - Hash_storage.save_bytes reveal_data_dir hash page_contents + Dac_manager.Reveal_hash.Storage.save_bytes + reveal_data_dir + hash + page_contents + in + let* root_hash = + match pagination_scheme with + | Merkle_tree_V0 -> + let size = + Protocol.Alpha_context.Constants.sc_rollup_message_size_limit + in + Merkle_tree.V0.serialize_payload + ~max_page_size:size + data + ~for_each_page + | Hash_chain_V0 -> Hash_chain.V0.serialize_payload ~for_each_page 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 - let data, pagination_scheme = input in - match pagination_scheme with - | Merkle_tree_V0 -> - let size = - Protocol.Alpha_context.Constants.sc_rollup_message_size_limit - in - Merkle_tree.V0.serialize_payload ~max_page_size:size data ~for_each_page - | Hash_chain_V0 -> Hash_chain.V0.serialize_payload ~for_each_page data + return (root_hash, external_message) - let register_serialize_dac_store_preimage reveal_data_dir = + 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 reveal_data_dir input) + handle_serialize_dac_store_preimage + cctxt + dac_sk_uris + reveal_data_dir + input) - let register reveal_data_dir = + 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 reveal_data_dir + |> register_serialize_dac_store_preimage cctxt dac_sk_uris reveal_data_dir end -let rpc_services ~reveal_data_dir = DAC.register reveal_data_dir +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_dal/dac_manager.ml b/src/proto_alpha/lib_dal/dac_manager.ml index e020a2af5b9e..cbf068e1ffdd 100644 --- a/src/proto_alpha/lib_dal/dac_manager.ml +++ b/src/proto_alpha/lib_dal/dac_manager.ml @@ -24,7 +24,6 @@ (*****************************************************************************) module Reveal_hash = struct - module Hashing_scheme = Dac_pages_encoding.Merkle_tree.V0 module Storage = Dac_preimage_data_manager.Reveal_hash module Signatures = Dac_signature_manager.Reveal_hash module External_message = Dac_external_message_manager.Reveal_hash diff --git a/src/proto_alpha/lib_dal/dac_signature_manager.ml b/src/proto_alpha/lib_dal/dac_signature_manager.ml index f38c3d991483..72bdf6159c8f 100644 --- a/src/proto_alpha/lib_dal/dac_signature_manager.ml +++ b/src/proto_alpha/lib_dal/dac_signature_manager.ml @@ -43,7 +43,7 @@ let () = ppf "Cannot convert root hash page to byte sequence: %s" b58_hash) - Data_encoding.(obj1 (req "hash" string)) + Data_encoding.(obj1 (req "hash" (string Plain))) (function | Cannot_convert_root_page_hash_to_bytes b58_hash -> Some b58_hash | _ -> None) @@ -58,7 +58,7 @@ let () = ppf "Cannot compute aggregate signature of root page hash: %s" b58_hash) - Data_encoding.(obj1 (req "hash" string)) + 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) ; @@ -76,7 +76,7 @@ let () = root page hash %s" witness_index b58_hash) - Data_encoding.(obj2 (req "witness_index" int31) (req "hash" string)) + Data_encoding.(obj2 (req "witness_index" int31) (req "hash" (string Plain))) (function | Public_key_for_witness_not_available (index, hash) -> Some (index, hash) | _ -> None) @@ -93,8 +93,8 @@ module Make (Hashing_scheme : REVEAL_HASH) = struct let open Lwt_result_syntax in List.rev_mapi_es (fun index sk_uri_opt -> - (* TODO: - Implement Option.bind_es and revisit this*) + (* TODO: https://gitlab.com/tezos/tezos/-/issues/4306 + Implement Option.bind_es and revisit this. *) bind_es (fun sk_uri -> let*! signature_res = -- GitLab From 55058e6a8f32b999ec172a689624715d9e1f07f0 Mon Sep 17 00:00:00 2001 From: Andrea Cerone Date: Tue, 22 Nov 2022 12:58:48 +0000 Subject: [PATCH 5/7] Dac/Plugin: add verify signature function --- .../lib_dal/dac_signature_manager.ml | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/proto_alpha/lib_dal/dac_signature_manager.ml b/src/proto_alpha/lib_dal/dac_signature_manager.ml index 72bdf6159c8f..394a7b786463 100644 --- a/src/proto_alpha/lib_dal/dac_signature_manager.ml +++ b/src/proto_alpha/lib_dal/dac_signature_manager.ml @@ -142,6 +142,39 @@ module Make (Hashing_scheme : REVEAL_HASH) = struct match final_signature with | None -> tzfail @@ Cannot_compute_aggregate_signature b58_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_b58check 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_b58check 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) -- GitLab From c4daf9f87efdbb0aa34a0af8accb315283ed654a Mon Sep 17 00:00:00 2001 From: Andrea Cerone Date: Tue, 22 Nov 2022 12:59:36 +0000 Subject: [PATCH 6/7] Dac/Plugin: verify signature endpoint --- src/proto_alpha/lib_dal/RPC.ml | 58 ++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/proto_alpha/lib_dal/RPC.ml b/src/proto_alpha/lib_dal/RPC.ml index 473071b25727..30caa6e1eb58 100644 --- a/src/proto_alpha/lib_dal/RPC.ml +++ b/src/proto_alpha/lib_dal/RPC.ml @@ -26,7 +26,9 @@ open Environment open Error_monad -type error += Cannot_construct_external_message +type error += + | Cannot_construct_external_message + | Cannot_deserialize_external_message let () = register_error_kind @@ -38,7 +40,17 @@ let () = 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) + (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 = @@ -60,6 +72,12 @@ module DAC = struct (req "root_hash" Protocol.Sc_rollup_reveal_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 @@ -68,6 +86,15 @@ module DAC = struct ~input:store_preimage_request_encoding ~output:store_preimage_response_encoding RPC_path.(open_root / "dac" / "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 / "dac" / "verify_signature") end let handle_serialize_dac_store_preimage cctxt dac_sk_uris reveal_data_dir @@ -110,6 +137,21 @@ module DAC = struct 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 = 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 register_serialize_dac_store_preimage cctxt dac_sk_uris reveal_data_dir = Registration.register0_noctxt ~chunked:false @@ -121,9 +163,19 @@ module DAC = struct reveal_data_dir input) - let register reveal_data_dir cctxt _dac_public_keys_opt dac_sk_uris = + 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 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 end let rpc_services ~reveal_data_dir cctxt dac_public_keys_opt dac_sk_uris -- GitLab From 2fbeccb64cbfbb8e1c88d99a247431d1d2e8d435 Mon Sep 17 00:00:00 2001 From: Andrea Cerone Date: Tue, 22 Nov 2022 12:32:03 +0000 Subject: [PATCH 7/7] Dac/Tezt: tezts for verifying external message signature --- tezt/lib_tezos/rollup.ml | 15 ++++++++++++++- tezt/lib_tezos/rollup.mli | 17 +++++++++++++---- tezt/tests/dal.ml | 29 +++++++++++++++++++++++++---- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/tezt/lib_tezos/rollup.ml b/tezt/lib_tezos/rollup.ml index 8b92e11487ae..924cfd1f6391 100644 --- a/tezt/lib_tezos/rollup.ml +++ b/tezt/lib_tezos/rollup.ml @@ -619,7 +619,20 @@ module Dal = struct pagination_scheme) in let data = JSON.unannotate preimage in - make ~data PUT ["plugin"; "dac"; "store_preimage"] JSON.as_string + make ~data PUT ["plugin"; "dac"; "store_preimage"] @@ fun json -> + JSON. + ( json |-> "root_hash" |> as_string, + json |-> "external_message" |> get_bytes_from_json_string_node ) + + let dac_verify_signature external_msg = + let query_string = + [ + ( "external_message", + match Hex.of_string external_msg with `Hex s -> s ); + ] + in + make ~query_string GET ["plugin"; "dac"; "verify_signature"] + @@ JSON.as_bool end module RPC = struct diff --git a/tezt/lib_tezos/rollup.mli b/tezt/lib_tezos/rollup.mli index 04a484c1172d..3802420f8807 100644 --- a/tezt/lib_tezos/rollup.mli +++ b/tezt/lib_tezos/rollup.mli @@ -270,13 +270,22 @@ module Dal : sig val shards : slot_header:string -> int list -> (Dal_node.t, string list) RPC_core.t - (** [dac_store_preimage payload pagination_scheme] posts a [payload] to - dac/store_preimage using a given [pagination_scheme]. Returns the - computed root hash. *) + (** [dac_store_preimage ~payload ~pagination_scheme] posts a [payload] to + dac/store_preimage using a given [pagination_scheme]. It returnst the + base58 encoded root page hash, and the raw bytes that can + be used as contents of a rollup message to trigger the request of + the payload in a Wasm rollup. *) val dac_store_preimage : payload:string -> pagination_scheme:string -> - (Dal_node.t, string) RPC_core.t + (Dal_node.t, string * string) RPC_core.t + + (** [dac_verify_signature data] requests the dal node to verify + the signature of the external message [data] via + the plugin/dac/verify_signature endpoint. The DAC committee + of the dal node must be the same that was used to produce the + [data]. *) + val dac_verify_signature : string -> (Dal_node.t, bool) RPC_core.t end module RPC : sig diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index 262616a7c7a7..0e5583a2dc3e 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -1360,9 +1360,19 @@ let check_preimage expected_preimage actual_preimage = "Preimage does not match expected value (Current: %L <> Expected: %R)") let test_dal_node_handles_dac_store_preimage_merkle_V0 _protocol dal_node - sc_rollup_node _sc_rollup_address _node _client pvm_name = + sc_rollup_node _sc_rollup_address _node client pvm_name = + (* Terminate the dal node before setting dac parameters. *) + let* () = Dal_node.terminate dal_node in + let* dac_member = Client.bls_gen_keys ~alias:"dac_member" client in + let* dac_member_info = Client.bls_show_address ~alias:dac_member client in + let dac_member_address = dac_member_info.aggregate_public_key_hash in + let* () = Dal_node.Dac.set_parameters ~threshold:1 dal_node in + let* () = + Dal_node.Dac.add_committee_member ~address:dac_member_address dal_node + in + let* () = Dal_node.run dal_node in let payload = "test" in - let* actual_rh = + let* actual_rh, l1_operation = RPC.call dal_node (Rollup.Dal.RPC.dac_store_preimage @@ -1387,12 +1397,19 @@ let test_dal_node_handles_dac_store_preimage_merkle_V0 _protocol dal_node String.sub recovered_payload 5 (String.length recovered_payload - 5) in check_preimage payload recovered_preimage ; + let* is_signature_valid = + RPC.call dal_node (Rollup.Dal.RPC.dac_verify_signature l1_operation) + in + Check.( + (is_signature_valid = true) + bool + ~error_msg:"Signature of external message is not valid") ; unit let test_dal_node_handles_dac_store_preimage_hash_chain_V0 _protocol dal_node sc_rollup_node _sc_rollup_address _node _client pvm_name = let payload = "test" in - let* actual_rh = + let* actual_rh, _l1_operation = RPC.call dal_node (Rollup.Dal.RPC.dac_store_preimage @@ -1444,7 +1461,7 @@ let test_rollup_arith_uses_reveals _protocol dal_node sc_rollup_node Buffer.add_string buf "0 " ; aux buf nadd in - let* actual_rh = + let* actual_rh, _l1_operation = RPC.call dal_node (Rollup.Dal.RPC.dac_store_preimage @@ -1622,18 +1639,22 @@ let register ~protocols = (rollup_node_stores_dal_slots ~expand_test:rollup_node_interprets_dal_pages) protocols ; scenario_with_all_nodes + ~tags:["dac"; "dal_node"] "dac_reveals_data_merkle_tree_v0" test_dal_node_handles_dac_store_preimage_merkle_V0 protocols ; scenario_with_all_nodes + ~tags:["dac"; "dal_node"] "dac_reveals_data_hash_chain_v0" test_dal_node_handles_dac_store_preimage_hash_chain_V0 protocols ; scenario_with_all_nodes + ~tags:["dac"; "dal_node"] "dac_rollup_arith_uses_reveals" test_rollup_arith_uses_reveals protocols ; scenario_with_all_nodes + ~tags:["dac"; "dal_node"] "dac_rollup_arith_wrong_hash" test_reveals_fails_on_wrong_hash protocols -- GitLab