From 8b9d5b42086319f180c009146547d3609ff98799 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Thu, 17 Nov 2022 14:07:08 +0000 Subject: [PATCH 1/2] Dal node: add hash chain pagination scheme, update store_preimage RPC Dal/Sc_rollup node: move rollup reveals_data tezt to dal tezt Sc_rollup node: remove import command Dal node: fix up commit --- .../main_sc_rollup_node_alpha.ml | 37 +- src/proto_alpha/bin_sc_rollup_node/reveals.ml | 181 +------- .../bin_sc_rollup_node/reveals.mli | 21 - src/proto_alpha/lib_dal/RPC.ml | 22 +- src/proto_alpha/lib_dal/dac_pages_encoding.ml | 89 ++++ .../lib_dal/test/test_dac_pages_encoding.ml | 431 ++++++++++++------ tezt/lib_tezos/rollup.ml | 7 +- tezt/lib_tezos/rollup.mli | 5 +- tezt/lib_tezos/sc_rollup_node.ml | 16 - tezt/lib_tezos/sc_rollup_node.mli | 7 - tezt/tests/dal.ml | 173 ++++++- ...h L1 (dac_reveals_data_hash_chain_v0).out} | 4 +- ...h L1 (dac_reveals_data_merkle_tree_v0).out | 34 ++ ...ith L1 (dac_rollup_arith_uses_reveals).out | 77 ++++ ... with L1 (dac_rollup_arith_wrong_hash).out | 74 +++ ...ith L1 (rollup_node_applies_dal_pages).out | 4 +- ... with L1 (rollup_node_downloads_slots).out | 4 +- tezt/tests/sc_rollup.ml | 59 --- 18 files changed, 746 insertions(+), 499 deletions(-) rename tezt/tests/expected/dal.ml/{Alpha- Testing DAL rollup and node with L1 (dac_handles_reveal_data).out => Alpha- Testing DAL rollup and node with L1 (dac_reveals_data_hash_chain_v0).out} (93%) create mode 100644 tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_reveals_data_merkle_tree_v0).out create mode 100644 tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_rollup_arith_uses_reveals).out create mode 100644 tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_rollup_arith_wrong_hash).out diff --git a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml index e09a73676768..86cbb3a6a06c 100644 --- a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml +++ b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml @@ -197,14 +197,6 @@ let reconnection_delay_arg = (Tezos_clic.parameter (fun _ p -> try return (float_of_string p) with _ -> failwith "Cannot read float")) -let filename_arg = - Tezos_clic.default_arg - ~long:"filename" - ~placeholder:"filename" - ~doc:"The path to the file to import." - ~default:"import.in" - Client_proto_args.string_parameter - let injector_retention_period_arg = let default = Configuration.default_injector_retention_period |> string_of_int @@ -227,20 +219,6 @@ let injector_retention_period_arg = Configuration.max_injector_retention_period ; p) -let pvm_name_arg = - Tezos_clic.default_arg - ~long:"pvm-name" - ~placeholder:"pvm_name" - ~doc:"The name of the PVM." - ~default:"arith" - Client_proto_args.string_parameter - -let pvm_kind_arg = - Tezos_clic.map_arg pvm_name_arg ~f:(fun _ name -> - match Protocol.Alpha_context.Sc_rollup.Kind.of_name name with - | None -> failwith "Invalid PVM name %s" name - | Some kind -> return kind) - let group = { Tezos_clic.name = "sc_rollup.node"; @@ -372,23 +350,10 @@ let run_command = in Daemon.run ~data_dir configuration cctxt) -let import_command = - let open Tezos_clic in - command - ~group - ~desc:"Import data to be used in reveal ticks." - (args3 data_dir_arg filename_arg pvm_kind_arg) - (prefixes ["import"] @@ stop) - (fun (data_dir, filename, pvm_kind) cctxt -> - let open Lwt_result_syntax in - let*? hash = Reveals.import ~data_dir pvm_kind ~filename in - let*! () = cctxt#message "%a" Protocol.Sc_rollup_reveal_hash.pp hash in - return_unit) - let sc_rollup_commands () = List.map (Tezos_clic.map_command (new Protocol_client_context.wrap_full)) - [config_init_command; run_command; import_command] + [config_init_command; run_command] let select_commands _ _ = return (sc_rollup_commands () @ Client_helpers_commands.commands ()) diff --git a/src/proto_alpha/bin_sc_rollup_node/reveals.ml b/src/proto_alpha/bin_sc_rollup_node/reveals.ml index 436d42bb9c44..e7df54e15e99 100644 --- a/src/proto_alpha/bin_sc_rollup_node/reveals.ml +++ b/src/proto_alpha/bin_sc_rollup_node/reveals.ml @@ -28,8 +28,6 @@ module Reveal_hash = Protocol.Sc_rollup_reveal_hash type error += | Wrong_hash of {found : Reveal_hash.t; expected : Reveal_hash.t} | Could_not_open_preimage_file of String.t - | Empty_reveal_data - | PVM_not_supported of Protocol.Alpha_context.Sc_rollup.Kind.t let () = register_error_kind @@ -65,31 +63,7 @@ let () = Data_encoding.(obj1 (req "hash" string)) (function | Could_not_open_preimage_file filename -> Some filename | _ -> None) - (fun filename -> Could_not_open_preimage_file filename) ; - register_error_kind - `Permanent - ~id:"sc_rollup_node_empty_reveal_data" - ~title:"Empty revelation data" - ~description:"Empty revelation data." - ~pp:(fun ppf () -> - Format.pp_print_string ppf "Tried to import or use empty revelation data.") - Data_encoding.unit - (function Empty_reveal_data -> Some () | _ -> None) - (fun () -> Empty_reveal_data) ; - register_error_kind - `Permanent - ~id:"sc_rollup_node_reveal_data_not_supported" - ~title:"Revelation data not supported for PVM" - ~description:"Revelation data not supported for PVM." - ~pp:(fun ppf kind -> - Format.fprintf - ppf - "Revelation data not supported for PVM %s" - (Protocol.Alpha_context.Sc_rollup.Kind.name_of kind)) - Data_encoding.( - obj1 (req "pvm_kind" Protocol.Alpha_context.Sc_rollup.Kind.encoding)) - (function PVM_not_supported k -> Some k | _ -> None) - (fun k -> PVM_not_supported k) + (fun filename -> Could_not_open_preimage_file filename) type source = String of string | File of string @@ -101,22 +75,10 @@ let file_contents filename = return contents) (fun _ -> tzfail @@ Could_not_open_preimage_file filename) -let save_string filename s = - let cout = open_out filename in - output_string cout s ; - close_out cout - let path data_dir pvm_name hash = let hash = Format.asprintf "%a" Reveal_hash.pp hash in Filename.(concat (concat data_dir pvm_name) hash) -let ensure_dir_exists data_dir pvm_name = - let path = Filename.concat data_dir pvm_name in - if Sys.(file_exists path) then ( - if not (Sys.is_directory path) then - Stdlib.failwith (path ^ " should be a directory.")) - else Sys.mkdir path 0o700 - let get ~data_dir ~pvm_name ~hash = let open Lwt_result_syntax in let filename = path data_dir pvm_name hash in @@ -130,144 +92,3 @@ let get ~data_dir ~pvm_name ~hash = (Wrong_hash {found = contents_hash; expected = hash}) in return contents - -module Arith = struct - let pvm_name = - Protocol.Alpha_context.Sc_rollup.ArithPVM.Protocol_implementation.name - - type input = - | String of {s : string; mutable pos : int; len : int} - | Input of in_channel - - let rev_chunks_of_input input = - (* FIXME: https://gitlab.com/tezos/tezos/-/issues/3853 - Can be made more efficient. *) - let get_char () = - match input with - | Input cin -> ( try Some (input_char cin) with End_of_file -> None) - | String ({s; pos; len} as sin) -> - if pos >= len then None - else ( - sin.pos <- pos + 1 ; - Some s.[pos]) - in - let buf = Buffer.create 31 in - let tokens = - (* let cin = open_in filename in *) - let rec aux tokens = - match get_char () with - | None -> - List.rev - @@ - if Buffer.length buf > 0 then - let token = Buffer.contents buf in - token :: tokens - else tokens - | Some ' ' -> - let token = Buffer.contents buf in - Buffer.clear buf ; - aux (token :: tokens) - | Some c -> - Buffer.add_char buf c ; - aux tokens - in - let tokens = aux [] in - tokens - in - let limit = - (4 * 1024) - 100 (* We reserve 100 bytes for the continuation hash. *) - in - Buffer.clear buf ; - let make_chunk () = - let chunk = Buffer.contents buf in - Buffer.clear buf ; - chunk - in - let chunks, _ = - List.fold_left - (fun (chunks, size) token -> - let len = String.length token in - if size + len > limit then ( - let chunk = make_chunk () in - Buffer.add_string buf token ; - (chunk :: chunks, len)) - else - let len = - if Buffer.length buf > 0 then ( - Buffer.add_char buf ' ' ; - len + 1) - else len - in - Buffer.add_string buf token ; - (chunks, size + len)) - ([], 0) - tokens - in - let chunks = - if Buffer.length buf > 0 then make_chunk () :: chunks else chunks - in - chunks - - let link_rev_chunks rev_chunks = - let rec aux successor_hash linked_chunks = function - | [] -> linked_chunks - | chunk :: rev_chunks -> - let cell = - match successor_hash with - | None -> chunk - | Some h -> Format.asprintf "%s hash:%a" chunk Reveal_hash.pp h - in - let hash = Reveal_hash.hash_string ~scheme:Blake2B [cell] in - aux (Some hash) ((hash, cell) :: linked_chunks) rev_chunks - in - aux None [] rev_chunks - - let input_of_source = function - | File filename -> Input (open_in filename) - | String s -> String {s; pos = 0; len = String.length s} - - let close_input = function String _ -> () | Input cin -> close_in cin - - let chunkify source = - let input = input_of_source source in - let rev_chunks = rev_chunks_of_input input in - let linked_hashed_chunks = link_rev_chunks rev_chunks in - close_input input ; - linked_hashed_chunks - - let first_hash = function - | (hash, _) :: _ -> ok hash - | [] -> error Empty_reveal_data - - let import data_dir source = - ensure_dir_exists data_dir pvm_name ; - let linked_hashed_chunks = chunkify source in - List.iter - (fun (hash, data) -> save_string (path data_dir pvm_name hash) data) - linked_hashed_chunks ; - first_hash linked_hashed_chunks - - let chunkify source = - let open Result_syntax in - let linked_hashed_chunks = chunkify source in - let chunks_map = - linked_hashed_chunks |> List.to_seq - |> Protocol.Sc_rollup_reveal_hash.Map.of_seq - in - let+ hash = first_hash linked_hashed_chunks in - (chunks_map, hash) -end - -let import ~data_dir (pvm_kind : Protocol.Alpha_context.Sc_rollup.Kind.t) - ~filename = - match pvm_kind with - | Example_arith -> Arith.import data_dir (File filename) - | Wasm_2_0_0 -> - (* TODO: https://gitlab.com/tezos/tezos/-/issues/4067 - Add support for multiple revelation data serialization schemes *) - error (PVM_not_supported pvm_kind) - -let chunkify (pvm_kind : Protocol.Alpha_context.Sc_rollup.Kind.t) source = - match pvm_kind with - | Example_arith -> Arith.chunkify source - | Wasm_2_0_0 -> error (PVM_not_supported pvm_kind) diff --git a/src/proto_alpha/bin_sc_rollup_node/reveals.mli b/src/proto_alpha/bin_sc_rollup_node/reveals.mli index 19debadd0b1e..bb6e7e82deb5 100644 --- a/src/proto_alpha/bin_sc_rollup_node/reveals.mli +++ b/src/proto_alpha/bin_sc_rollup_node/reveals.mli @@ -48,8 +48,6 @@ sources of reveal data so that the rollup node can automatically download data in advance. *) -open Protocol.Alpha_context - (** Source of data *) type source = | String of string (** A string containing the whole data *) @@ -71,22 +69,3 @@ val get : pvm_name:string -> hash:Protocol.Sc_rollup_reveal_hash.t -> string tzresult Lwt.t - -(** [import ~data_dir pvm_kind ~filename] turns the content of ~filename into a - chunk of pages of (at most) 4KB and stores them on disk in [data_dir]. It - returns the hash of the first chunk. *) -val import : - data_dir:string -> - Sc_rollup.Kind.t -> - filename:string -> - Protocol.Sc_rollup_reveal_hash.t tzresult - -(** [chunkify pvm_kind source] turns the content of ~filename into a chunk of - pages of (at most) 4KB. It returns the map of chunks and the hash of the - first chunk. *) -val chunkify : - Sc_rollup.Kind.t -> - source -> - (string Protocol.Sc_rollup_reveal_hash.Map.t - * Protocol.Sc_rollup_reveal_hash.t) - tzresult diff --git a/src/proto_alpha/lib_dal/RPC.ml b/src/proto_alpha/lib_dal/RPC.ml index 64ed6eee8ade..5b2a557abc8e 100644 --- a/src/proto_alpha/lib_dal/RPC.ml +++ b/src/proto_alpha/lib_dal/RPC.ml @@ -31,25 +31,37 @@ module Registration = struct end module DAC = struct - module Hashing_scheme = Dac_pages_encoding.Merkle_tree.V0 module Hash_storage = Dac_preimage_data_manager.Reveal_hash + let store_preimage_request_encoding = + Data_encoding.( + obj2 + (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 + module S = struct let dac_store_preimage = RPC_service.put_service ~description:"Split DAC reveal data" ~query:RPC_query.empty - ~input:Data_encoding.(bytes Hex) - ~output:Hashing_scheme.hash_encoding + ~input:store_preimage_request_encoding + ~output:store_preimage_response_encoding RPC_path.(open_root / "dac" / "store_preimage") end - let handle_serialize_dac_store_preimage reveal_data_dir data = + let handle_serialize_dac_store_preimage reveal_data_dir input = + let open Dac_pages_encoding in let for_each_page (hash, page_contents) = Hash_storage.save_bytes reveal_data_dir hash page_contents in let size = Protocol.Alpha_context.Constants.sc_rollup_message_size_limit in - Hashing_scheme.serialize_payload ~max_page_size:size data ~for_each_page + let data, pagination_scheme = input in + match pagination_scheme with + | Merkle_tree_V0 -> + 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 let register_serialize_dac_store_preimage reveal_data_dir = Registration.register0_noctxt diff --git a/src/proto_alpha/lib_dal/dac_pages_encoding.ml b/src/proto_alpha/lib_dal/dac_pages_encoding.ml index a225a5a4f2ca..8ca0faa24390 100644 --- a/src/proto_alpha/lib_dal/dac_pages_encoding.ml +++ b/src/proto_alpha/lib_dal/dac_pages_encoding.ml @@ -40,6 +40,12 @@ type error += | Non_positive_size_of_payload | Merkle_tree_branching_factor_not_high_enough +type pagination_scheme = Merkle_tree_V0 | Hash_chain_V0 + +let pagination_scheme_encoding = + Data_encoding.string_enum + [("Merkle_tree_V0", Merkle_tree_V0); ("Hash_chain_V0", Hash_chain_V0)] + let () = register_error_kind `Permanent @@ -377,3 +383,86 @@ module Merkle_tree = struct let hashes_version = 0 end)) end + +module Hash_chain = struct + module type PAGE_FMT = sig + type h + + val content_limit : int + + val hash_to_string : h -> string + + (** Serializes a successor hash and content into a single page (an element in + the hash link) *) + val serialize_content : h -> string -> string + end + + module Make (Hashing_scheme : sig + include Dac_preimage_data_manager.REVEAL_HASH + + val scheme : supported_hashes + end) + (P : PAGE_FMT with type h = Hashing_scheme.t) = + struct + let hash bytes = + Hashing_scheme.hash_bytes ~scheme:Hashing_scheme.scheme [bytes] + + let to_b58check = Hashing_scheme.to_b58check + + let link_chunks chunks : (Hashing_scheme.t * bytes) list = + let rec link_chunks_rev linked_pages rev_pages = + match rev_pages with + | [] -> linked_pages + | chunk :: rev_chunks -> + let page = + match linked_pages with + | [] -> chunk + | (succ_hash, _) :: _ -> P.serialize_content succ_hash chunk + in + let page = Bytes.of_string page in + let hash = hash page in + (link_chunks_rev [@tailcall]) + ((hash, page) :: linked_pages) + rev_chunks + in + let rev_chunks = List.rev chunks in + link_chunks_rev [] rev_chunks + + let make_hash_chain data = + let open Result_syntax in + let+ chunks = String.chunk_bytes P.content_limit data in + link_chunks chunks + + (** Main function for computing a hash chain from a byte sequence. Returns the + chain head hash.[for_each_page] may be supplied to run post processing + tasks on each page, for example, to persisit a serialized page to disk. + *) + let serialize_payload ~for_each_page payload = + let open Lwt_result_syntax in + let* () = + fail_unless (Bytes.length payload > 0) Payload_cannot_be_empty + in + let*? hash_chain = make_hash_chain payload in + let+ () = List.iter_es for_each_page hash_chain in + Stdlib.List.hd hash_chain |> fst + end + + module V0 = + Make + (struct + include Sc_rollup_reveal_hash + + let scheme = Sc_rollup_reveal_hash.Blake2B + end) + (struct + type h = Sc_rollup_reveal_hash.t + + let content_limit = + (4 * 1024) - 100 (* We reserve 100 bytes for the continuation hash. *) + + let hash_to_string = Sc_rollup_reveal_hash.to_b58check + + let serialize_content succ_hash content = + Format.asprintf "%s hash:%s" content (hash_to_string succ_hash) + end) +end diff --git a/src/proto_alpha/lib_dal/test/test_dac_pages_encoding.ml b/src/proto_alpha/lib_dal/test/test_dac_pages_encoding.ml index 5c50ab259ca1..60e3999dcd72 100644 --- a/src/proto_alpha/lib_dal/test/test_dac_pages_encoding.ml +++ b/src/proto_alpha/lib_dal/test/test_dac_pages_encoding.ml @@ -45,7 +45,159 @@ module Hashes_map = Sc_rollup_reveal_hash.Map type hashes_map = bytes Hashes_map.t -module Make_Merkle_tree_V0_backend () = struct +let long_payload = + (* Inferno, Canto I (Dante Alighieri). Size in bytes: 5226. *) + {|Nel mezzo del cammin di nostra vita +mi ritrovai per una selva oscura +ché la diritta via era smarrita. +Ahi quanto a dir qual era è cosa dura +esta selva selvaggia e aspra e forte +che nel pensier rinova la paura! +Tant’è amara che poco è più morte; +ma per trattar del ben ch’i’ vi trovai, +dirò de l’altre cose ch’i’ v’ho scorte. +Io non so ben ridir com’i’ v’intrai, +tant’era pien di sonno a quel punto +che la verace via abbandonai. +Ma poi ch’i’ fui al piè d’un colle giunto, +là dove terminava quella valle +che m’avea di paura il cor compunto, +guardai in alto, e vidi le sue spalle +vestite già de’ raggi del pianeta +che mena dritto altrui per ogne calle. +Allor fu la paura un poco queta +che nel lago del cor m’era durata +la notte ch’i’ passai con tanta pieta. +E come quei che con lena affannata +uscito fuor del pelago a la riva +si volge a l’acqua perigliosa e guata, +così l’animo mio, ch’ancor fuggiva, +si volse a retro a rimirar lo passo +che non lasciò già mai persona viva. +Poi ch’èi posato un poco il corpo lasso, +ripresi via per la piaggia diserta, +sì che ’l piè fermo sempre era ’l più basso. +Ed ecco, quasi al cominciar de l’erta, +una lonza leggera e presta molto, +che di pel macolato era coverta; +e non mi si partia dinanzi al volto, +anzi ’mpediva tanto il mio cammino, +ch’i’ fui per ritornar più volte vòlto. +Temp’era dal principio del mattino, +e ’l sol montava ’n sù con quelle stelle +ch’eran con lui quando l’amor divino +mosse di prima quelle cose belle; +sì ch’a bene sperar m’era cagione +di quella fiera a la gaetta pelle +l’ora del tempo e la dolce stagione; +ma non sì che paura non mi desse +la vista che m’apparve d’un leone. +Questi parea che contra me venisse +con la test’alta e con rabbiosa fame, +sì che parea che l’aere ne tremesse. +Ed una lupa, che di tutte brame +sembiava carca ne la sua magrezza, +e molte genti fé già viver grame, +questa mi porse tanto di gravezza +con la paura ch’uscia di sua vista, +ch’io perdei la speranza de l’altezza. +E qual è quei che volontieri acquista, +e giugne ’l tempo che perder lo face, +che ’n tutt’i suoi pensier piange e s’attrista; +tal mi fece la bestia sanza pace, +che, venendomi ’ncontro, a poco a poco +mi ripigneva là dove ’l sol tace. +Mentre ch’i’ rovinava in basso loco, +dinanzi a li occhi mi si fu offerto +chi per lungo silenzio parea fioco. +Quando vidi costui nel gran diserto, +«Miserere di me», gridai a lui, +«qual che tu sii, od ombra od omo certo!». +Rispuosemi: «Non omo, omo già fui, +e li parenti miei furon lombardi, +mantoani per patria ambedui. +Nacqui sub Iulio, ancor che fosse tardi, +e vissi a Roma sotto ’l buono Augusto +nel tempo de li dèi falsi e bugiardi. +Poeta fui, e cantai di quel giusto +figliuol d’Anchise che venne di Troia, +poi che ’l superbo Ilión fu combusto. +Ma tu perché ritorni a tanta noia? +perché non sali il dilettoso monte +ch’è principio e cagion di tutta gioia?». +«Or se’ tu quel Virgilio e quella fonte +che spandi di parlar sì largo fiume?», +rispuos’io lui con vergognosa fronte. +«O de li altri poeti onore e lume +vagliami ’l lungo studio e ’l grande amore +che m’ha fatto cercar lo tuo volume. +Tu se’ lo mio maestro e ’l mio autore; +tu se’ solo colui da cu’ io tolsi +lo bello stilo che m’ha fatto onore. +Vedi la bestia per cu’ io mi volsi: +aiutami da lei, famoso saggio, +ch’ella mi fa tremar le vene e i polsi». +«A te convien tenere altro viaggio», +rispuose poi che lagrimar mi vide, +«se vuo’ campar d’esto loco selvaggio: +ché questa bestia, per la qual tu gride, +non lascia altrui passar per la sua via, +ma tanto lo ’mpedisce che l’uccide; +e ha natura sì malvagia e ria, +che mai non empie la bramosa voglia, +e dopo ’l pasto ha più fame che pria. +Molti son li animali a cui s’ammoglia, +e più saranno ancora, infin che ’l veltro +verrà, che la farà morir con doglia. +Questi non ciberà terra né peltro, +ma sapienza, amore e virtute, +e sua nazion sarà tra feltro e feltro. +Di quella umile Italia fia salute +per cui morì la vergine Cammilla, +Eurialo e Turno e Niso di ferute. +Questi la caccerà per ogne villa, +fin che l’avrà rimessa ne lo ’nferno, +là onde ’nvidia prima dipartilla. +Ond’io per lo tuo me’ penso e discerno +che tu mi segui, e io sarò tua guida, +e trarrotti di qui per loco etterno, +ove udirai le disperate strida, +vedrai li antichi spiriti dolenti, +ch’a la seconda morte ciascun grida; +e vederai color che son contenti +nel foco, perché speran di venire +quando che sia a le beate genti. +A le quai poi se tu vorrai salire, +anima fia a ciò più di me degna: +con lei ti lascerò nel mio partire; +ché quello imperador che là sù regna, +perch’i’ fu’ ribellante a la sua legge, +non vuol che ’n sua città per me si vegna. +In tutte parti impera e quivi regge; +quivi è la sua città e l’alto seggio: +oh felice colui cu’ ivi elegge!». +E io a lui: «Poeta, io ti richeggio +per quello Dio che tu non conoscesti, +acciò ch’io fugga questo male e peggio, +che tu mi meni là dov’or dicesti, +sì ch’io veggia la porta di san Pietro +e color cui tu fai cotanto mesti». +Allor si mosse, e io li tenni dietro.|} + +module type BACKEND = sig + type h + + val save_page : h * bytes -> (unit, error trace) result Lwt.t + + val load_page : h -> (bytes, error trace) result Lwt.t + + val number_of_pages : unit -> int +end + +module type Hashes_Map_backend = functor () -> + BACKEND with type h = Hashes_map.key + +module Hashes_Map_backend () = struct open Environment.Error_monad type error += @@ -96,7 +248,7 @@ module Merkle_tree = struct *) let serialize_one_hash_per_page_fails () = - let module Backend = Make_Merkle_tree_V0_backend () in + let module Backend = Hashes_Map_backend () in let payload = List.repeat 195 (Bytes.of_string "a") |> Bytes.concat Bytes.empty in @@ -109,7 +261,7 @@ module Merkle_tree = struct Dac_pages_encoding.Merkle_tree_branching_factor_not_high_enough let serialize_empty_payload_fails () = - let module Backend = Make_Merkle_tree_V0_backend () in + let module Backend = Hashes_Map_backend () in (* Limit the number of hashes stored per page to 2. Because hashes have a fixed size of 32 bytes, and 5 bytes are used for the preamble, we need 32 * 2 + 5 = 69 bytes to store two hashes in a page. We round @@ -127,7 +279,7 @@ module Merkle_tree = struct let one_page_roundtrip () = let open Lwt_result_syntax in - let module Backend = Make_Merkle_tree_V0_backend () in + let module Backend = Hashes_Map_backend () in (* Limit the number of hashes stored per page to 2. Because hashes have a fixed size of 32 bytes, and 5 bytes are used for the preamble, we need 32 * 2 + 5 = 69 bytes to store two hashes in a page. We round @@ -165,7 +317,7 @@ module Merkle_tree = struct ceil(2/2) = 1 page is used for storing the 2 hashes of the previous pages. *) let open Lwt_result_syntax in - let module Backend = Make_Merkle_tree_V0_backend () in + let module Backend = Hashes_Map_backend () in (* Limit the number of hashes stored per page to 2. Because hashes have a fixed size of 32 bytes, and 5 bytes are used for the preamble, we need 32 * 2 + 5 = 69 bytes to store two hashes in a page. We round @@ -207,7 +359,7 @@ module Merkle_tree = struct tree. Finally, another page will be used for storing the Merkle tree root page, which contains the two hashes of the Merkle tree nodes above. In total, the serialization should be spread among 4 pages. *) - let module Backend = Make_Merkle_tree_V0_backend () in + let module Backend = Hashes_Map_backend () in let open Lwt_result_syntax in (* Limit the number of hashes stored per page to 2. Because hashes have a fixed size of 32 bytes, and 5 bytes are used for the preamble, @@ -254,7 +406,7 @@ module Merkle_tree = struct causing the serialization to fail. *) let open Lwt_result_syntax in - let module Backend = Make_Merkle_tree_V0_backend () in + let module Backend = Hashes_Map_backend () in let max_page_size = 98 in (* 279 bytes of payload *) let payload = @@ -286,153 +438,14 @@ module Merkle_tree = struct (* To ensure that the serialization and deserialization process work as expected, we test a roundtrip for a reasonably long text. We also increase the page size to allow for more than two hashes in a page. *) - let module Backend = Make_Merkle_tree_V0_backend () in + let module Backend = Hashes_Map_backend () in let open Lwt_result_syntax in (* The page size is set to 150. Of these, 5 bytes are used for the page preamble, and the reset will contain hashes which are 32 bytes long each. The number of hashes that can fit into a page is floor((150 - 5)/32) = 4. *) let max_page_size = 150 in - let payload = - (* Inferno, Canto I (Dante Alighieri). Size in bytes: 5226. *) - Bytes.of_string - "Nel mezzo del cammin di nostra vita\n\ - mi ritrovai per una selva oscura\n\ - ché la diritta via era smarrita.\n\ - Ahi quanto a dir qual era è cosa dura\n\ - esta selva selvaggia e aspra e forte\n\ - che nel pensier rinova la paura!\n\ - Tant’è amara che poco è più morte;\n\ - ma per trattar del ben ch’i’ vi trovai,\n\ - dirò de l’altre cose ch’i’ v’ho scorte.\n\ - Io non so ben ridir com’i’ v’intrai,\n\ - tant’era pien di sonno a quel punto\n\ - che la verace via abbandonai.\n\ - Ma poi ch’i’ fui al piè d’un colle giunto,\n\ - là dove terminava quella valle\n\ - che m’avea di paura il cor compunto,\n\ - guardai in alto, e vidi le sue spalle\n\ - vestite già de’ raggi del pianeta\n\ - che mena dritto altrui per ogne calle.\n\ - Allor fu la paura un poco queta\n\ - che nel lago del cor m’era durata\n\ - la notte ch’i’ passai con tanta pieta.\n\ - E come quei che con lena affannata\n\ - uscito fuor del pelago a la riva\n\ - si volge a l’acqua perigliosa e guata,\n\ - così l’animo mio, ch’ancor fuggiva,\n\ - si volse a retro a rimirar lo passo\n\ - che non lasciò già mai persona viva.\n\ - Poi ch’èi posato un poco il corpo lasso,\n\ - ripresi via per la piaggia diserta,\n\ - sì che ’l piè fermo sempre era ’l più basso.\n\ - Ed ecco, quasi al cominciar de l’erta,\n\ - una lonza leggera e presta molto,\n\ - che di pel macolato era coverta;\n\ - e non mi si partia dinanzi al volto,\n\ - anzi ’mpediva tanto il mio cammino,\n\ - ch’i’ fui per ritornar più volte vòlto.\n\ - Temp’era dal principio del mattino,\n\ - e ’l sol montava ’n sù con quelle stelle\n\ - ch’eran con lui quando l’amor divino\n\ - mosse di prima quelle cose belle;\n\ - sì ch’a bene sperar m’era cagione\n\ - di quella fiera a la gaetta pelle\n\ - l’ora del tempo e la dolce stagione;\n\ - ma non sì che paura non mi desse\n\ - la vista che m’apparve d’un leone.\n\ - Questi parea che contra me venisse\n\ - con la test’alta e con rabbiosa fame,\n\ - sì che parea che l’aere ne tremesse.\n\ - Ed una lupa, che di tutte brame\n\ - sembiava carca ne la sua magrezza,\n\ - e molte genti fé già viver grame,\n\ - questa mi porse tanto di gravezza\n\ - con la paura ch’uscia di sua vista,\n\ - ch’io perdei la speranza de l’altezza.\n\ - E qual è quei che volontieri acquista,\n\ - e giugne ’l tempo che perder lo face,\n\ - che ’n tutt’i suoi pensier piange e s’attrista;\n\ - tal mi fece la bestia sanza pace,\n\ - che, venendomi ’ncontro, a poco a poco\n\ - mi ripigneva là dove ’l sol tace.\n\ - Mentre ch’i’ rovinava in basso loco,\n\ - dinanzi a li occhi mi si fu offerto\n\ - chi per lungo silenzio parea fioco.\n\ - Quando vidi costui nel gran diserto,\n\ - «Miserere di me», gridai a lui,\n\ - «qual che tu sii, od ombra od omo certo!».\n\ - Rispuosemi: «Non omo, omo già fui,\n\ - e li parenti miei furon lombardi,\n\ - mantoani per patria ambedui.\n\ - Nacqui sub Iulio, ancor che fosse tardi,\n\ - e vissi a Roma sotto ’l buono Augusto\n\ - nel tempo de li dèi falsi e bugiardi.\n\ - Poeta fui, e cantai di quel giusto\n\ - figliuol d’Anchise che venne di Troia,\n\ - poi che ’l superbo Ilión fu combusto.\n\ - Ma tu perché ritorni a tanta noia?\n\ - perché non sali il dilettoso monte\n\ - ch’è principio e cagion di tutta gioia?».\n\ - «Or se’ tu quel Virgilio e quella fonte\n\ - che spandi di parlar sì largo fiume?»,\n\ - rispuos’io lui con vergognosa fronte.\n\ - «O de li altri poeti onore e lume\n\ - vagliami ’l lungo studio e ’l grande amore\n\ - che m’ha fatto cercar lo tuo volume.\n\ - Tu se’ lo mio maestro e ’l mio autore;\n\ - tu se’ solo colui da cu’ io tolsi\n\ - lo bello stilo che m’ha fatto onore.\n\ - Vedi la bestia per cu’ io mi volsi:\n\ - aiutami da lei, famoso saggio,\n\ - ch’ella mi fa tremar le vene e i polsi».\n\ - «A te convien tenere altro viaggio»,\n\ - rispuose poi che lagrimar mi vide,\n\ - «se vuo’ campar d’esto loco selvaggio:\n\ - ché questa bestia, per la qual tu gride,\n\ - non lascia altrui passar per la sua via,\n\ - ma tanto lo ’mpedisce che l’uccide;\n\ - e ha natura sì malvagia e ria,\n\ - che mai non empie la bramosa voglia,\n\ - e dopo ’l pasto ha più fame che pria.\n\ - Molti son li animali a cui s’ammoglia,\n\ - e più saranno ancora, infin che ’l veltro\n\ - verrà, che la farà morir con doglia.\n\ - Questi non ciberà terra né peltro,\n\ - ma sapienza, amore e virtute,\n\ - e sua nazion sarà tra feltro e feltro.\n\ - Di quella umile Italia fia salute\n\ - per cui morì la vergine Cammilla,\n\ - Eurialo e Turno e Niso di ferute.\n\ - Questi la caccerà per ogne villa,\n\ - fin che l’avrà rimessa ne lo ’nferno,\n\ - là onde ’nvidia prima dipartilla.\n\ - Ond’io per lo tuo me’ penso e discerno\n\ - che tu mi segui, e io sarò tua guida,\n\ - e trarrotti di qui per loco etterno,\n\ - ove udirai le disperate strida,\n\ - vedrai li antichi spiriti dolenti,\n\ - ch’a la seconda morte ciascun grida;\n\ - e vederai color che son contenti\n\ - nel foco, perché speran di venire\n\ - quando che sia a le beate genti.\n\ - A le quai poi se tu vorrai salire,\n\ - anima fia a ciò più di me degna:\n\ - con lei ti lascerò nel mio partire;\n\ - ché quello imperador che là sù regna,\n\ - perch’i’ fu’ ribellante a la sua legge,\n\ - non vuol che ’n sua città per me si vegna.\n\ - In tutte parti impera e quivi regge;\n\ - quivi è la sua città e l’alto seggio:\n\ - oh felice colui cu’ ivi elegge!».\n\ - E io a lui: «Poeta, io ti richeggio\n\ - per quello Dio che tu non conoscesti,\n\ - acciò ch’io fugga questo male e peggio,\n\ - che tu mi meni là dov’or dicesti,\n\ - sì ch’io veggia la porta di san Pietro\n\ - e color cui tu fai cotanto mesti».\n\ - Allor si mosse, e io li tenni dietro." - in + let payload = Bytes.of_string long_payload in let* hash = lift @@ serialize_payload @@ -452,6 +465,111 @@ module Merkle_tree = struct end end +module Hash_chain = struct + (* Return substring of [str] after the first [n] char. Returns the original string + if n <=0. Returns an empty string if the substring after the first [n] chars + is empty. *) + let take_after str n = + let n = max 0 n in + String.sub str (min (String.length str) n) (max 0 (String.length str - n)) + + module V0 = struct + module Pagination_scheme = Dac_pages_encoding.Hash_chain.V0 + + let deserialize_page page : + [`Node of Sc_rollup_reveal_hash.t * string | `Leaf of string] = + if String.length page > 3996 then + let content = String.sub page 0 3996 in + let hash = + Stdlib.Option.get + @@ Sc_rollup_reveal_hash.of_b58check_opt + (take_after page (3996 + String.length " hash:")) + in + `Node (hash, content) + else `Leaf page + + let rec retrieve_content ~get_page ?(result = "") page_hash = + let open Lwt_result_syntax in + let* page = get_page page_hash in + let* res = return @@ deserialize_page (Bytes.to_string page) in + match res with + | `Node (succ_hash, content) -> + (retrieve_content [@tailcall]) + ~get_page + ~result:(String.cat result content) + succ_hash + | `Leaf content -> return @@ String.cat result content + + let test_make_chain_hash_one_page () = + let open Lwt_result_syntax in + let payload = Bytes.of_string "simple payload" in + let*? pages = Pagination_scheme.make_hash_chain payload in + let* () = Assert.equal_int ~loc:__LOC__ 1 (List.length pages) in + let actual_hash, content = Stdlib.List.hd pages in + let* () = + assert_equal_bytes ~loc:__LOC__ "Contents not equal" payload content + in + let expected_hash = + Pagination_scheme.to_b58check @@ Pagination_scheme.hash content + in + Assert.equal_string + ~loc:__LOC__ + expected_hash + (Pagination_scheme.to_b58check actual_hash) + + let test_make_chain_hash_long () = + let open Lwt_result_syntax in + let payload = Bytes.of_string long_payload in + let*? pages = Pagination_scheme.make_hash_chain payload in + let* () = Assert.equal_int ~loc:__LOC__ 2 (List.length pages) in + let head_succ = + Stdlib.List.hd pages |> snd |> fun byt -> + take_after (String.of_bytes byt) (3996 + String.length " hash:") + in + let next_hash, content = Stdlib.List.nth pages 1 in + let* () = + Assert.equal_string + ~loc:__LOC__ + (Pagination_scheme.to_b58check next_hash) + head_succ + in + Assert.equal_string + ~loc:__LOC__ + (Pagination_scheme.to_b58check next_hash) + (Pagination_scheme.to_b58check @@ Pagination_scheme.hash content) + + let test_serialize () = + let module Backend = Hashes_Map_backend () in + let open Lwt_result_syntax in + let payload = Bytes.of_string long_payload in + let* root_hash = + lwt_map_error Environment.wrap_tztrace + @@ Pagination_scheme.serialize_payload + ~for_each_page:Backend.save_page + payload + in + let* () = Assert.equal_int ~loc:__LOC__ (Backend.number_of_pages ()) 2 in + let* content = + lwt_map_error Environment.wrap_tztrace + @@ retrieve_content ~get_page:Backend.load_page root_hash + in + Assert.equal_string ~loc:__LOC__ long_payload content + + let test_serialize_empty_payload_fails () = + let module Backend = Hashes_Map_backend () in + let payload = Bytes.of_string "" in + let result = + Pagination_scheme.serialize_payload + ~for_each_page:Backend.save_page + payload + in + assert_fails_with + ~loc:__LOC__ + result + Dac_pages_encoding.Payload_cannot_be_empty + end +end + let tests = [ Tztest.tztest @@ -486,4 +604,21 @@ let tests = "Hashes pages are not larger than expected" `Quick Merkle_tree.V0.multiple_pages_roundtrip_do_not_exceed_page_size; + Tztest.tztest + "Constructing hash chain (V0) for single page content is correct" + `Quick + Hash_chain.V0.test_make_chain_hash_one_page; + Tztest.tztest + "Constructing hash chain (V0) for multi page content is correct" + `Quick + Hash_chain.V0.test_make_chain_hash_long; + Tztest.tztest + "Serializing an empty payload returns an error (Hash chain)" + `Quick + Hash_chain.V0.test_serialize_empty_payload_fails; + Tztest.tztest + "Contents fitting in more pages can be retrieved after being saved - \ + repeated pages (Hash chain, V0)" + `Quick + Hash_chain.V0.test_serialize; ] diff --git a/tezt/lib_tezos/rollup.ml b/tezt/lib_tezos/rollup.ml index 438ca8a1473f..7b8fc0af54f0 100644 --- a/tezt/lib_tezos/rollup.ml +++ b/tezt/lib_tezos/rollup.ml @@ -609,11 +609,14 @@ module Dal = struct make ~data POST ["shards"; slot_header] (fun json -> JSON.(json |> as_list |> List.map encode)) - let dac_store_preimage preimage = + let dac_store_preimage preimage pagination_scheme = let preimage = JSON.parse ~origin:"dal_node_dac_store_preimage_rpc" - (encode_bytes_to_hex_string preimage) + (Format.sprintf + {|{"payload":%s,"pagination_scheme":"%s"}|} + (encode_bytes_to_hex_string preimage) + pagination_scheme) in let data = JSON.unannotate preimage in make ~data PUT ["plugin"; "dac"; "store_preimage"] JSON.as_string diff --git a/tezt/lib_tezos/rollup.mli b/tezt/lib_tezos/rollup.mli index 46abb4862c06..e9848c147a2e 100644 --- a/tezt/lib_tezos/rollup.mli +++ b/tezt/lib_tezos/rollup.mli @@ -270,8 +270,9 @@ module Dal : sig val shards : slot_header:string -> int list -> (Dal_node.t, string list) RPC_core.t - (** [dac_store_preimage data] posts [data] on dac/store_preimage *) - val dac_store_preimage : string -> (Dal_node.t, string) RPC_core.t + (** [dac_store_preimage data pagination_scheme] posts [data] on dac/store_preimage with + pagination_scheme [pagination_scheme]*) + val dac_store_preimage : string -> string -> (Dal_node.t, string) RPC_core.t end module RPC : sig diff --git a/tezt/lib_tezos/sc_rollup_node.ml b/tezt/lib_tezos/sc_rollup_node.ml index ccda3a431b01..eddf5b79a645 100644 --- a/tezt/lib_tezos/sc_rollup_node.ml +++ b/tezt/lib_tezos/sc_rollup_node.ml @@ -172,22 +172,6 @@ module Config_file = struct let update sc_node update = read sc_node |> update |> write sc_node end -let spawn_import sc_node ~pvm_name ~filename = - spawn_command sc_node - @@ [ - "import"; - "--data-dir"; - data_dir sc_node; - "--pvm-name"; - pvm_name; - "--filename"; - filename; - ] - -let import sc_node ~pvm_name ~filename = - let process = spawn_import sc_node ~pvm_name ~filename in - Process.check_and_read_stdout process - let trigger_ready sc_node value = let pending = sc_node.persistent_state.pending_ready in sc_node.persistent_state.pending_ready <- [] ; diff --git a/tezt/lib_tezos/sc_rollup_node.mli b/tezt/lib_tezos/sc_rollup_node.mli index 18b862783191..c5dc1393eae7 100644 --- a/tezt/lib_tezos/sc_rollup_node.mli +++ b/tezt/lib_tezos/sc_rollup_node.mli @@ -166,13 +166,6 @@ val wait_for_ready : t -> unit Lwt.t passed. *) val wait_for_level : ?timeout:float -> t -> int -> int Lwt.t -(** [import sc_node ~pvm_name ~filename] makes the contents of - [filename] available as raw data chunks to the rollup assuming - that it runs according to a given [pvm_name]. - Returns the hash of the first of these chunks. - The implementation is PVM-dependent. *) -val import : t -> pvm_name:string -> filename:string -> string Lwt.t - (** [wait_for ?where sc_node event_name filter] waits for the SCORU node [sc_node] to emit an event named [name] (usually this is the name the event is declared with, concatenated with [".v0"]). [wait_for] continues to wait diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index 5dcc50ac9b6a..60fdc33b279c 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -132,7 +132,7 @@ let with_fresh_rollup ?(pvm_name = "arith") ?dal_node f tezos_node tezos_client ~src:bootstrap1_key ~kind:pvm_name ~boot_sector:"" - ~parameters_ty:"unit" + ~parameters_ty:"string" tezos_client in let sc_rollup_node = @@ -1045,11 +1045,16 @@ let test_dal_node_startup = let* () = Dal_node.terminate dal_node in return () -let send_messages ?(src = Constant.bootstrap2.alias) client msgs = - let msg = Ezjsonm.(to_string ~minify:true @@ list Ezjsonm.string msgs) in +let send_messages ?(src = Constant.bootstrap2.alias) ?(f = Fun.id) client msgs = + let msg = f @@ Ezjsonm.(to_string ~minify:true @@ list Ezjsonm.string msgs) in let* () = Client.Sc_rollup.send_message ~hooks ~src ~msg client in Client.bake_for_and_wait client +let bake_levels ?hook n client = + fold n () @@ fun i () -> + let* () = match hook with None -> unit | Some hook -> hook i in + Client.bake_for_and_wait client + let rollup_node_stores_dal_slots ?expand_test _protocol dal_node sc_rollup_node sc_rollup_address node client _pvm_name = (* Check that the rollup node stores the slots published in a block, along with slot headers: @@ -1319,21 +1324,32 @@ let rollup_node_interprets_dal_pages client sc_rollup sc_rollup_node = return () (* DAC tests *) +let assert_valid_root_hash expected_rh actual_rh = + Check.( + (actual_rh = expected_rh) + string + ~error_msg:"Invalid root hash returned (Current:%L <> Expected: %R)") -let test_dal_node_handles_dac_store_preimage _protocol dal_node sc_rollup_node - _sc_rollup_address _node _client pvm_name = +let assert_preimage expected_preimage actual_preimage = + Check.( + (actual_preimage = expected_preimage) + string + ~error_msg: + "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 = let preimage = "test" in let* actual_rh = - RPC.call dal_node (Rollup.Dal.RPC.dac_store_preimage preimage) + RPC.call + dal_node + (Rollup.Dal.RPC.dac_store_preimage preimage "Merkle_tree_V0") in (* Expected reveal hash equals to the result of [Tezos_dal_alpha.Dac_pages_encoding.Merkle_tree.V0.serialize_payload "test"]. *) let expected_rh = "scrrh1Y5hijnFNJPb96EFTY9SjZ4epyaYF9xU3Eid9KCj9vda25H8W" in - Check.( - (actual_rh = expected_rh) - string - ~error_msg:"Invalid root hash returned (Current:%L <> Expected: %R)") ; + assert_valid_root_hash expected_rh actual_rh ; let filename = Filename.concat (Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) pvm_name) @@ -1346,13 +1362,124 @@ let test_dal_node_handles_dac_store_preimage _protocol dal_node sc_rollup_node let recovered_preimage = String.sub recovered_payload 5 (String.length recovered_payload - 5) in - Check.( - (recovered_preimage = preimage) - string - ~error_msg: - "Preimage does not match expected value (Current:%L <> Expected: %R)") ; + assert_preimage preimage recovered_preimage ; + return () + +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 preimage = "test" in + let* actual_rh = + RPC.call + dal_node + (Rollup.Dal.RPC.dac_store_preimage preimage "Hash_chain_V0") + in + (* Expected reveal hash equals to the result of + [Tezos_dal_alpha.Dac_pages_encoding.Hash_chain.V0.serialize_payload "test"]. + *) + let expected_rh = "scrrh1hYNyzXmagRDi22knBt5dhMMi6Sivdo1ztf5wXiectRnzbSyX" in + assert_valid_root_hash expected_rh actual_rh ; + let filename = + Filename.concat + (Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) pvm_name) + actual_rh + in + let cin = open_in filename in + let recovered_payload = really_input_string cin (in_channel_length cin) in + let () = close_in cin in + let recovered_preimage = + String.sub recovered_payload 0 (String.length preimage) + in + assert_preimage preimage recovered_preimage ; return () +let test_rollup_arith_uses_reveals _protocol dal_node sc_rollup_node + sc_rollup_address _node client _pvm_name = + let* genesis_info = + RPC.Client.call ~hooks client + @@ RPC.get_chain_block_context_sc_rollups_sc_rollup_genesis_info + sc_rollup_address + in + let init_level = JSON.(genesis_info |-> "level" |> as_int) in + let* () = Sc_rollup_node.run sc_rollup_node [] in + let* level = + Sc_rollup_node.wait_for_level ~timeout:120. sc_rollup_node init_level + in + + let nadd = 32 * 1024 in + let data = + (* let buf = Buffer.create (nadd * 3 + 2) in *) + let rec aux b = function + | n when n > 0 -> + Buffer.add_string b "1 +" ; + (aux [@tailcall]) b (n - 1) + | _ -> + Buffer.add_string b "value" ; + String.of_bytes (Buffer.to_bytes b) + in + let buf = Buffer.create ((nadd * 3) + 2) in + Buffer.add_string buf "0 " ; + aux buf nadd + in + let* actual_rh = + RPC.call dal_node (Rollup.Dal.RPC.dac_store_preimage data "Hash_chain_V0") + in + let expected_rh = "scrrh1bZAo4kfAohhKcvgKc4yLHYN49EPuHezNXgFraa1mEbKSTCwf" in + assert_valid_root_hash expected_rh actual_rh ; + let* () = + send_messages client ["hash:" ^ actual_rh] ~f:(fun s -> "text:" ^ s) + in + let* () = bake_levels 2 client in + let* _ = + Sc_rollup_node.wait_for_level ~timeout:120. sc_rollup_node (level + 2) + in + let sc_rollup_client = Sc_rollup_client.create sc_rollup_node in + let*! encoded_value = + Sc_rollup_client.state_value ~hooks sc_rollup_client ~key:"vars/value" + in + let value = + match Data_encoding.(Binary.of_bytes int31) @@ encoded_value with + | Error error -> + failwith + (Format.asprintf + "The arithmetic PVM has an unexpected state: %a" + Data_encoding.Binary.pp_read_error + error) + | Ok x -> x + in + Check.( + (value = nadd) int ~error_msg:"Invalid value in rollup state (%L <> %R)") ; + unit + +let test_reveals_fails_on_wrong_hash _protocol dal_node sc_rollup_node + sc_rollup_address _node client _pvm_name = + let data = "Some data that is not related to the hash" in + let _actual_rh = + RPC.call dal_node (Rollup.Dal.RPC.dac_store_preimage data "Hash_chain_V0") + in + let errorneous_hash = + "scrrh1kXE3tnCVTJ21aDNVeaV86e8rS6jtiMEDpjZJtDnLXRThQdmy" + in + let* genesis_info = + RPC.Client.call ~hooks client + @@ RPC.get_chain_block_context_sc_rollups_sc_rollup_genesis_info + sc_rollup_address + in + let init_level = JSON.(genesis_info |-> "level" |> as_int) in + + let* () = Sc_rollup_node.run sc_rollup_node [] in + (* Prepare the handler to wait for the rollup node to fail before + sending the L1 message that will trigger the failure. This + ensures that the failure handler can access the status code + of the rollup node even after it has terminated. *) + let expect_failure = Sc_rollup_node.wait_for_failure_handler sc_rollup_node in + let* _level = + Sc_rollup_node.wait_for_level ~timeout:120. sc_rollup_node init_level + in + let* () = + send_messages client ["hash:" ^ errorneous_hash] ~f:(fun s -> "text:" ^ s) + in + expect_failure () + let test_dal_node_imports_dac_member = Protocol.register_test ~__FILE__ @@ -1454,6 +1581,18 @@ let register ~protocols = (rollup_node_stores_dal_slots ~expand_test:rollup_node_interprets_dal_pages) protocols ; scenario_with_all_nodes - "dac_handles_reveal_data" - test_dal_node_handles_dac_store_preimage + "dac_reveals_data_merkle_tree_v0" + test_dal_node_handles_dac_store_preimage_merkle_V0 + protocols ; + scenario_with_all_nodes + "dac_reveals_data_hash_chain_v0" + test_dal_node_handles_dac_store_preimage_hash_chain_V0 + protocols ; + scenario_with_all_nodes + "dac_rollup_arith_uses_reveals" + test_rollup_arith_uses_reveals + protocols ; + scenario_with_all_nodes + "dac_rollup_arith_wrong_hash" + test_reveals_fails_on_wrong_hash protocols diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_handles_reveal_data).out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_reveals_data_hash_chain_v0).out similarity index 93% rename from tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_handles_reveal_data).out rename to tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_reveals_data_hash_chain_v0).out index d973460ab5a0..88197bb0efa3 100644 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_handles_reveal_data).out +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_reveals_data_hash_chain_v0).out @@ -1,5 +1,5 @@ -./octez-client --wait none originate sc rollup from '[PUBLIC_KEY_HASH]' of kind arith of type unit booting with --burn-cap 9999999 +./octez-client --wait none originate sc rollup from '[PUBLIC_KEY_HASH]' of kind arith of type string booting with --burn-cap 9999999 Node is bootstrapped. Estimated gas: 2709.909 units (will add 100 for safety) Estimated storage: 6520 bytes added (will add 20 for safety) @@ -21,7 +21,7 @@ This sequence of operations was run: payload fees(the block proposer) ....... +ꜩ0.000629 Smart contract rollup origination: Kind: arith - Parameter type: unit + Parameter type: string Boot sector Blake2B hash: '0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' This smart contract rollup origination was successfully applied Consumed gas: 2709.909 diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_reveals_data_merkle_tree_v0).out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_reveals_data_merkle_tree_v0).out new file mode 100644 index 000000000000..88197bb0efa3 --- /dev/null +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_reveals_data_merkle_tree_v0).out @@ -0,0 +1,34 @@ + +./octez-client --wait none originate sc rollup from '[PUBLIC_KEY_HASH]' of kind arith of type string booting with --burn-cap 9999999 +Node is bootstrapped. +Estimated gas: 2709.909 units (will add 100 for safety) +Estimated storage: 6520 bytes added (will add 20 for safety) +Operation successfully injected in the node. +Operation hash is '[OPERATION_HASH]' +NOT waiting for the operation to be included. +Use command + octez-client wait for [OPERATION_HASH] to be included --confirmations 1 --branch [BLOCK_HASH] +and/or an external block explorer to make sure that it has been included. +This sequence of operations was run: + Manager signed operations: + From: [PUBLIC_KEY_HASH] + Fee to the baker: ꜩ0.000629 + Expected counter: 1 + Gas limit: 2810 + Storage limit: 6540 bytes + Balance updates: + [PUBLIC_KEY_HASH] ... -ꜩ0.000629 + payload fees(the block proposer) ....... +ꜩ0.000629 + Smart contract rollup origination: + Kind: arith + Parameter type: string + Boot sector Blake2B hash: '0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' + This smart contract rollup origination was successfully applied + Consumed gas: 2709.909 + Storage size: 6520 bytes + Address: [SC_ROLLUP_HASH] + Genesis commitment hash: [SC_ROLLUP_COMMITMENT_HASH] + Balance updates: + [PUBLIC_KEY_HASH] ... -ꜩ1.63 + storage fees ........................... +ꜩ1.63 + diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_rollup_arith_uses_reveals).out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_rollup_arith_uses_reveals).out new file mode 100644 index 000000000000..021bdfbd7b40 --- /dev/null +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_rollup_arith_uses_reveals).out @@ -0,0 +1,77 @@ + +./octez-client --wait none originate sc rollup from '[PUBLIC_KEY_HASH]' of kind arith of type string booting with --burn-cap 9999999 +Node is bootstrapped. +Estimated gas: 2709.909 units (will add 100 for safety) +Estimated storage: 6520 bytes added (will add 20 for safety) +Operation successfully injected in the node. +Operation hash is '[OPERATION_HASH]' +NOT waiting for the operation to be included. +Use command + octez-client wait for [OPERATION_HASH] to be included --confirmations 1 --branch [BLOCK_HASH] +and/or an external block explorer to make sure that it has been included. +This sequence of operations was run: + Manager signed operations: + From: [PUBLIC_KEY_HASH] + Fee to the baker: ꜩ0.000629 + Expected counter: 1 + Gas limit: 2810 + Storage limit: 6540 bytes + Balance updates: + [PUBLIC_KEY_HASH] ... -ꜩ0.000629 + payload fees(the block proposer) ....... +ꜩ0.000629 + Smart contract rollup origination: + Kind: arith + Parameter type: string + Boot sector Blake2B hash: '0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' + This smart contract rollup origination was successfully applied + Consumed gas: 2709.909 + Storage size: 6520 bytes + Address: [SC_ROLLUP_HASH] + Genesis commitment hash: [SC_ROLLUP_COMMITMENT_HASH] + Balance updates: + [PUBLIC_KEY_HASH] ... -ꜩ1.63 + storage fees ........................... +ꜩ1.63 + + +./octez-client rpc get '/chains/main/blocks/head/context/sc_rollups/sc_rollup/[SC_ROLLUP_HASH]/genesis_info' +{ "level": 2, + "commitment_hash": "[SC_ROLLUP_COMMITMENT_HASH]" } + +./octez-client --wait none send sc rollup message 'text:["hash:scrrh1bZAo4kfAohhKcvgKc4yLHYN49EPuHezNXgFraa1mEbKSTCwf"]' from bootstrap2 +Node is bootstrapped. +Estimated gas: 1001.463 units (will add 100 for safety) +Estimated storage: no bytes added +Operation successfully injected in the node. +Operation hash is '[OPERATION_HASH]' +NOT waiting for the operation to be included. +Use command + octez-client wait for [OPERATION_HASH] to be included --confirmations 1 --branch [BLOCK_HASH] +and/or an external block explorer to make sure that it has been included. +This sequence of operations was run: + Manager signed operations: + From: [PUBLIC_KEY_HASH] + Fee to the baker: ꜩ0.000402 + Expected counter: 1 + Gas limit: 1102 + Storage limit: 0 bytes + Balance updates: + [PUBLIC_KEY_HASH] ... -ꜩ0.000402 + payload fees(the block proposer) ....... +ꜩ0.000402 + Smart contract rollup messages submission + This smart contract rollup messages submission was successfully applied + Consumed gas: 1001.463 + Resulting inbox state: { level = 3 + current messages hash = hash: [SC_ROLLUP_INBOX_LEVEL_TREE_HASH] + level: 3 nb_messages_in_commitment_period = 9 + old_levels_messages = + content = hash: [SC_ROLLUP_INBOX_LEVEL_TREE_HASH] + level: 2 + index = 2 + back_pointers = [SC_ROLLUP_INBOX_HASH] + [SC_ROLLUP_INBOX_HASH] + + } + + +./octez-sc-rollup-client-alpha get state value for vars/value --block head +"\000\000\128\000" diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_rollup_arith_wrong_hash).out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_rollup_arith_wrong_hash).out new file mode 100644 index 000000000000..3c13b62c32e8 --- /dev/null +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (dac_rollup_arith_wrong_hash).out @@ -0,0 +1,74 @@ + +./octez-client --wait none originate sc rollup from '[PUBLIC_KEY_HASH]' of kind arith of type string booting with --burn-cap 9999999 +Node is bootstrapped. +Estimated gas: 2709.909 units (will add 100 for safety) +Estimated storage: 6520 bytes added (will add 20 for safety) +Operation successfully injected in the node. +Operation hash is '[OPERATION_HASH]' +NOT waiting for the operation to be included. +Use command + octez-client wait for [OPERATION_HASH] to be included --confirmations 1 --branch [BLOCK_HASH] +and/or an external block explorer to make sure that it has been included. +This sequence of operations was run: + Manager signed operations: + From: [PUBLIC_KEY_HASH] + Fee to the baker: ꜩ0.000629 + Expected counter: 1 + Gas limit: 2810 + Storage limit: 6540 bytes + Balance updates: + [PUBLIC_KEY_HASH] ... -ꜩ0.000629 + payload fees(the block proposer) ....... +ꜩ0.000629 + Smart contract rollup origination: + Kind: arith + Parameter type: string + Boot sector Blake2B hash: '0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' + This smart contract rollup origination was successfully applied + Consumed gas: 2709.909 + Storage size: 6520 bytes + Address: [SC_ROLLUP_HASH] + Genesis commitment hash: [SC_ROLLUP_COMMITMENT_HASH] + Balance updates: + [PUBLIC_KEY_HASH] ... -ꜩ1.63 + storage fees ........................... +ꜩ1.63 + + +./octez-client rpc get '/chains/main/blocks/head/context/sc_rollups/sc_rollup/[SC_ROLLUP_HASH]/genesis_info' +{ "level": 2, + "commitment_hash": "[SC_ROLLUP_COMMITMENT_HASH]" } + +./octez-client --wait none send sc rollup message 'text:["hash:scrrh1kXE3tnCVTJ21aDNVeaV86e8rS6jtiMEDpjZJtDnLXRThQdmy"]' from bootstrap2 +Node is bootstrapped. +Estimated gas: 1001.463 units (will add 100 for safety) +Estimated storage: no bytes added +Operation successfully injected in the node. +Operation hash is '[OPERATION_HASH]' +NOT waiting for the operation to be included. +Use command + octez-client wait for [OPERATION_HASH] to be included --confirmations 1 --branch [BLOCK_HASH] +and/or an external block explorer to make sure that it has been included. +This sequence of operations was run: + Manager signed operations: + From: [PUBLIC_KEY_HASH] + Fee to the baker: ꜩ0.000402 + Expected counter: 1 + Gas limit: 1102 + Storage limit: 0 bytes + Balance updates: + [PUBLIC_KEY_HASH] ... -ꜩ0.000402 + payload fees(the block proposer) ....... +ꜩ0.000402 + Smart contract rollup messages submission + This smart contract rollup messages submission was successfully applied + Consumed gas: 1001.463 + Resulting inbox state: { level = 3 + current messages hash = hash: [SC_ROLLUP_INBOX_LEVEL_TREE_HASH] + level: 3 nb_messages_in_commitment_period = 9 + old_levels_messages = + content = hash: [SC_ROLLUP_INBOX_LEVEL_TREE_HASH] + level: 2 + index = 2 + back_pointers = [SC_ROLLUP_INBOX_HASH] + [SC_ROLLUP_INBOX_HASH] + + } + diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_applies_dal_pages).out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_applies_dal_pages).out index ea9cc1c5b2af..424aef44caff 100644 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_applies_dal_pages).out +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_applies_dal_pages).out @@ -1,5 +1,5 @@ -./octez-client --wait none originate sc rollup from '[PUBLIC_KEY_HASH]' of kind arith of type unit booting with --burn-cap 9999999 +./octez-client --wait none originate sc rollup from '[PUBLIC_KEY_HASH]' of kind arith of type string booting with --burn-cap 9999999 Node is bootstrapped. Estimated gas: 2709.909 units (will add 100 for safety) Estimated storage: 6520 bytes added (will add 20 for safety) @@ -21,7 +21,7 @@ This sequence of operations was run: payload fees(the block proposer) ....... +ꜩ0.000629 Smart contract rollup origination: Kind: arith - Parameter type: unit + Parameter type: string Boot sector Blake2B hash: '0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' This smart contract rollup origination was successfully applied Consumed gas: 2709.909 diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_downloads_slots).out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_downloads_slots).out index b6b8ee2173c0..d25763466e73 100644 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_downloads_slots).out +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_downloads_slots).out @@ -1,5 +1,5 @@ -./octez-client --wait none originate sc rollup from '[PUBLIC_KEY_HASH]' of kind arith of type unit booting with --burn-cap 9999999 +./octez-client --wait none originate sc rollup from '[PUBLIC_KEY_HASH]' of kind arith of type string booting with --burn-cap 9999999 Node is bootstrapped. Estimated gas: 2709.909 units (will add 100 for safety) Estimated storage: 6520 bytes added (will add 20 for safety) @@ -21,7 +21,7 @@ This sequence of operations was run: payload fees(the block proposer) ....... +ꜩ0.000629 Smart contract rollup origination: Kind: arith - Parameter type: unit + Parameter type: string Boot sector Blake2B hash: '0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' This smart contract rollup origination was successfully applied Consumed gas: 2709.909 diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index 3c07ee4ee674..14252cb9890f 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -2439,62 +2439,6 @@ let test_boot_sector_is_evaluated ~boot_sector1 ~boot_sector2 ~kind = ~error_msg:"State hashes should be different! (%L, %R)") ; unit -let test_rollup_arith_uses_reveals ~kind = - let nadd = 32 * 1024 in - test_full_scenario - ~timeout:120 - ~kind - { - tags = ["reveals"]; - variant = None; - description = "rollup node correctly handles reveals"; - } - @@ fun sc_rollup_node sc_rollup_client sc_rollup _node client -> - let filename = - let filename, cout = Filename.open_temp_file "sc_rollup" ".in" in - output_string cout "0 " ; - for _i = 1 to nadd do - output_string cout "1 + " - done ; - output_string cout "value" ; - close_out cout ; - filename - in - let* hash = Sc_rollup_node.import sc_rollup_node ~pvm_name:kind ~filename in - let* genesis_info = - RPC.Client.call ~hooks client - @@ RPC.get_chain_block_context_sc_rollups_sc_rollup_genesis_info sc_rollup - in - let init_level = JSON.(genesis_info |-> "level" |> as_int) in - - let* () = Sc_rollup_node.run sc_rollup_node [] in - let* level = - Sc_rollup_node.wait_for_level ~timeout:120. sc_rollup_node init_level - in - - let* () = send_text_messages client ["hash:" ^ hash] in - let* () = bake_levels 2 client in - let* _ = - Sc_rollup_node.wait_for_level ~timeout:120. sc_rollup_node (level + 1) - in - - let*! encoded_value = - Sc_rollup_client.state_value ~hooks sc_rollup_client ~key:"vars/value" - in - let value = - match Data_encoding.(Binary.of_bytes int31) @@ encoded_value with - | Error error -> - failwith - (Format.asprintf - "The arithmetic PVM has an unexpected state: %a" - Data_encoding.Binary.pp_read_error - error) - | Ok x -> x - in - Check.( - (value = nadd) int ~error_msg:"Invalid value in rollup state (%L <> %R)") ; - unit - let test_reveals_fails_on_wrong_hash ~kind = test_full_scenario ~timeout:120 @@ -3899,9 +3843,6 @@ let register ~protocols = ~kind:"wasm_2_0_0" ~kernel_name:"no_parse_bad_fingerprint" ~internal:false ; - (* DAC tests, not supported yet by the Wasm PVM *) - test_rollup_arith_uses_reveals protocols ~kind:"arith" ; - test_reveals_fails_on_wrong_hash protocols ~kind:"arith" ; (* Shared tezts - will be executed for both PVMs. *) register ~kind:"wasm_2_0_0" ~protocols ; register ~kind:"arith" ~protocols -- GitLab From d745ec3227da28444b36af4dd12c0efc708adb4b Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Mon, 28 Nov 2022 14:27:51 +0000 Subject: [PATCH 2/2] Dal node: introduce page structure in hash_chain scheme Dal node: comment fix up --- src/proto_alpha/lib_dal/RPC.ml | 4 +- src/proto_alpha/lib_dal/dac_pages_encoding.ml | 23 +++-- .../lib_dal/test/test_dac_pages_encoding.ml | 3 +- tezt/lib_tezos/rollup.ml | 4 +- tezt/lib_tezos/rollup.mli | 10 +- tezt/tests/dal.ml | 92 +++++++++++-------- 6 files changed, 82 insertions(+), 54 deletions(-) diff --git a/src/proto_alpha/lib_dal/RPC.ml b/src/proto_alpha/lib_dal/RPC.ml index 5b2a557abc8e..3f3684d6b197 100644 --- a/src/proto_alpha/lib_dal/RPC.ml +++ b/src/proto_alpha/lib_dal/RPC.ml @@ -56,10 +56,12 @@ module DAC = struct let for_each_page (hash, page_contents) = Hash_storage.save_bytes reveal_data_dir hash page_contents in - let size = Protocol.Alpha_context.Constants.sc_rollup_message_size_limit 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 diff --git a/src/proto_alpha/lib_dal/dac_pages_encoding.ml b/src/proto_alpha/lib_dal/dac_pages_encoding.ml index 8ca0faa24390..710a3f0f5f05 100644 --- a/src/proto_alpha/lib_dal/dac_pages_encoding.ml +++ b/src/proto_alpha/lib_dal/dac_pages_encoding.ml @@ -388,13 +388,14 @@ module Hash_chain = struct module type PAGE_FMT = sig type h + type page = {succ_hash : h; content : string} + val content_limit : int - val hash_to_string : h -> string + val serialize_hash : h -> string - (** Serializes a successor hash and content into a single page (an element in - the hash link) *) - val serialize_content : h -> string -> string + (** Serializes a single page (an element in the hash link). *) + val serialize_page : page -> string end module Make (Hashing_scheme : sig @@ -417,7 +418,8 @@ module Hash_chain = struct let page = match linked_pages with | [] -> chunk - | (succ_hash, _) :: _ -> P.serialize_content succ_hash chunk + | (succ_hash, _) :: _ -> + P.serialize_page {succ_hash; content = chunk} in let page = Bytes.of_string page in let hash = hash page in @@ -457,12 +459,17 @@ module Hash_chain = struct (struct type h = Sc_rollup_reveal_hash.t + type page = {succ_hash : h; content : string} + let content_limit = (4 * 1024) - 100 (* We reserve 100 bytes for the continuation hash. *) - let hash_to_string = Sc_rollup_reveal_hash.to_b58check + let serialize_hash = Sc_rollup_reveal_hash.to_b58check - let serialize_content succ_hash content = - Format.asprintf "%s hash:%s" content (hash_to_string succ_hash) + let serialize_page page = + Format.asprintf + "%s hash:%s" + page.content + (serialize_hash page.succ_hash) end) end diff --git a/src/proto_alpha/lib_dal/test/test_dac_pages_encoding.ml b/src/proto_alpha/lib_dal/test/test_dac_pages_encoding.ml index 60e3999dcd72..9ba72106cd32 100644 --- a/src/proto_alpha/lib_dal/test/test_dac_pages_encoding.ml +++ b/src/proto_alpha/lib_dal/test/test_dac_pages_encoding.ml @@ -467,8 +467,7 @@ end module Hash_chain = struct (* Return substring of [str] after the first [n] char. Returns the original string - if n <=0. Returns an empty string if the substring after the first [n] chars - is empty. *) + if n <= 0. Returns an empty string if n > String.length str *) let take_after str n = let n = max 0 n in String.sub str (min (String.length str) n) (max 0 (String.length str - n)) diff --git a/tezt/lib_tezos/rollup.ml b/tezt/lib_tezos/rollup.ml index 7b8fc0af54f0..a62599ea0917 100644 --- a/tezt/lib_tezos/rollup.ml +++ b/tezt/lib_tezos/rollup.ml @@ -609,13 +609,13 @@ module Dal = struct make ~data POST ["shards"; slot_header] (fun json -> JSON.(json |> as_list |> List.map encode)) - let dac_store_preimage preimage pagination_scheme = + let dac_store_preimage ~payload ~pagination_scheme = let preimage = JSON.parse ~origin:"dal_node_dac_store_preimage_rpc" (Format.sprintf {|{"payload":%s,"pagination_scheme":"%s"}|} - (encode_bytes_to_hex_string preimage) + (encode_bytes_to_hex_string payload) pagination_scheme) in let data = JSON.unannotate preimage in diff --git a/tezt/lib_tezos/rollup.mli b/tezt/lib_tezos/rollup.mli index e9848c147a2e..8a70ad991bfe 100644 --- a/tezt/lib_tezos/rollup.mli +++ b/tezt/lib_tezos/rollup.mli @@ -270,9 +270,13 @@ module Dal : sig val shards : slot_header:string -> int list -> (Dal_node.t, string list) RPC_core.t - (** [dac_store_preimage data pagination_scheme] posts [data] on dac/store_preimage with - pagination_scheme [pagination_scheme]*) - val dac_store_preimage : string -> string -> (Dal_node.t, string) 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. *) + val dac_store_preimage : + payload:string -> + pagination_scheme:string -> + (Dal_node.t, string) RPC_core.t end module RPC : sig diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index 60fdc33b279c..cb694a31d09a 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -1045,15 +1045,16 @@ let test_dal_node_startup = let* () = Dal_node.terminate dal_node in return () -let send_messages ?(src = Constant.bootstrap2.alias) ?(f = Fun.id) client msgs = - let msg = f @@ Ezjsonm.(to_string ~minify:true @@ list Ezjsonm.string msgs) in +let send_messages ?(src = Constant.bootstrap2.alias) ?(alter_final_msg = Fun.id) + client msgs = + let msg = + alter_final_msg + @@ Ezjsonm.(to_string ~minify:true @@ list Ezjsonm.string msgs) + in let* () = Client.Sc_rollup.send_message ~hooks ~src ~msg client in Client.bake_for_and_wait client -let bake_levels ?hook n client = - fold n () @@ fun i () -> - let* () = match hook with None -> unit | Some hook -> hook i in - Client.bake_for_and_wait client +let bake_levels n client = repeat n (fun () -> Client.bake_for_and_wait client) let rollup_node_stores_dal_slots ?expand_test _protocol dal_node sc_rollup_node sc_rollup_address node client _pvm_name = @@ -1324,32 +1325,34 @@ let rollup_node_interprets_dal_pages client sc_rollup sc_rollup_node = return () (* DAC tests *) -let assert_valid_root_hash expected_rh actual_rh = +let check_valid_root_hash expected_rh actual_rh = Check.( (actual_rh = expected_rh) string - ~error_msg:"Invalid root hash returned (Current:%L <> Expected: %R)") + ~error_msg:"Invalid root hash returned (Current: %L <> Expected: %R)") -let assert_preimage expected_preimage actual_preimage = +let check_preimage expected_preimage actual_preimage = Check.( (actual_preimage = expected_preimage) string ~error_msg: - "Preimage does not match expected value (Current:%L <> Expected: %R)") + "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 = - let preimage = "test" in + let payload = "test" in let* actual_rh = RPC.call dal_node - (Rollup.Dal.RPC.dac_store_preimage preimage "Merkle_tree_V0") + (Rollup.Dal.RPC.dac_store_preimage + ~payload + ~pagination_scheme:"Merkle_tree_V0") in (* Expected reveal hash equals to the result of [Tezos_dal_alpha.Dac_pages_encoding.Merkle_tree.V0.serialize_payload "test"]. *) let expected_rh = "scrrh1Y5hijnFNJPb96EFTY9SjZ4epyaYF9xU3Eid9KCj9vda25H8W" in - assert_valid_root_hash expected_rh actual_rh ; + check_valid_root_hash expected_rh actual_rh ; let filename = Filename.concat (Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) pvm_name) @@ -1362,22 +1365,24 @@ let test_dal_node_handles_dac_store_preimage_merkle_V0 _protocol dal_node let recovered_preimage = String.sub recovered_payload 5 (String.length recovered_payload - 5) in - assert_preimage preimage recovered_preimage ; - return () + check_preimage payload recovered_preimage ; + 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 preimage = "test" in + let payload = "test" in let* actual_rh = RPC.call dal_node - (Rollup.Dal.RPC.dac_store_preimage preimage "Hash_chain_V0") + (Rollup.Dal.RPC.dac_store_preimage + ~payload + ~pagination_scheme:"Hash_chain_V0") in (* Expected reveal hash equals to the result of [Tezos_dal_alpha.Dac_pages_encoding.Hash_chain.V0.serialize_payload "test"]. *) let expected_rh = "scrrh1hYNyzXmagRDi22knBt5dhMMi6Sivdo1ztf5wXiectRnzbSyX" in - assert_valid_root_hash expected_rh actual_rh ; + check_valid_root_hash expected_rh actual_rh ; let filename = Filename.concat (Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) pvm_name) @@ -1387,10 +1392,10 @@ let test_dal_node_handles_dac_store_preimage_hash_chain_V0 _protocol dal_node let recovered_payload = really_input_string cin (in_channel_length cin) in let () = close_in cin in let recovered_preimage = - String.sub recovered_payload 0 (String.length preimage) + String.sub recovered_payload 0 (String.length payload) in - assert_preimage preimage recovered_preimage ; - return () + check_preimage payload recovered_preimage ; + unit let test_rollup_arith_uses_reveals _protocol dal_node sc_rollup_node sc_rollup_address _node client _pvm_name = @@ -1404,29 +1409,34 @@ let test_rollup_arith_uses_reveals _protocol dal_node sc_rollup_node let* level = Sc_rollup_node.wait_for_level ~timeout:120. sc_rollup_node init_level in - let nadd = 32 * 1024 in - let data = - (* let buf = Buffer.create (nadd * 3 + 2) in *) - let rec aux b = function - | n when n > 0 -> - Buffer.add_string b "1 +" ; - (aux [@tailcall]) b (n - 1) - | _ -> - Buffer.add_string b "value" ; - String.of_bytes (Buffer.to_bytes b) + let payload = + let rec aux b n = + if n > 0 then ( + Buffer.add_string b "1 +" ; + (aux [@tailcall]) b (n - 1)) + else ( + Buffer.add_string b "value" ; + String.of_bytes (Buffer.to_bytes b)) in let buf = Buffer.create ((nadd * 3) + 2) in Buffer.add_string buf "0 " ; aux buf nadd in let* actual_rh = - RPC.call dal_node (Rollup.Dal.RPC.dac_store_preimage data "Hash_chain_V0") + RPC.call + dal_node + (Rollup.Dal.RPC.dac_store_preimage + ~payload + ~pagination_scheme:"Hash_chain_V0") in let expected_rh = "scrrh1bZAo4kfAohhKcvgKc4yLHYN49EPuHezNXgFraa1mEbKSTCwf" in - assert_valid_root_hash expected_rh actual_rh ; + check_valid_root_hash expected_rh actual_rh ; let* () = - send_messages client ["hash:" ^ actual_rh] ~f:(fun s -> "text:" ^ s) + send_messages + client + ["hash:" ^ actual_rh] + ~alter_final_msg:(fun s -> "text:" ^ s) in let* () = bake_levels 2 client in let* _ = @@ -1452,9 +1462,13 @@ let test_rollup_arith_uses_reveals _protocol dal_node sc_rollup_node let test_reveals_fails_on_wrong_hash _protocol dal_node sc_rollup_node sc_rollup_address _node client _pvm_name = - let data = "Some data that is not related to the hash" in + let payload = "Some data that is not related to the hash" in let _actual_rh = - RPC.call dal_node (Rollup.Dal.RPC.dac_store_preimage data "Hash_chain_V0") + RPC.call + dal_node + (Rollup.Dal.RPC.dac_store_preimage + ~payload + ~pagination_scheme:"Hash_chain_V0") in let errorneous_hash = "scrrh1kXE3tnCVTJ21aDNVeaV86e8rS6jtiMEDpjZJtDnLXRThQdmy" @@ -1465,7 +1479,6 @@ let test_reveals_fails_on_wrong_hash _protocol dal_node sc_rollup_node sc_rollup_address in let init_level = JSON.(genesis_info |-> "level" |> as_int) in - let* () = Sc_rollup_node.run sc_rollup_node [] in (* Prepare the handler to wait for the rollup node to fail before sending the L1 message that will trigger the failure. This @@ -1476,7 +1489,10 @@ let test_reveals_fails_on_wrong_hash _protocol dal_node sc_rollup_node Sc_rollup_node.wait_for_level ~timeout:120. sc_rollup_node init_level in let* () = - send_messages client ["hash:" ^ errorneous_hash] ~f:(fun s -> "text:" ^ s) + send_messages + client + ["hash:" ^ errorneous_hash] + ~alter_final_msg:(fun s -> "text:" ^ s) in expect_failure () -- GitLab