From 38f2779144a55d050b273ace69fb93335eb0cfd8 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 24 Feb 2023 10:56:26 +0100 Subject: [PATCH 01/12] SCORU/Node: allow multiple instances of commitment and batcher workers This is simply to avoid conflicts in the declaration of events for the tests. --- src/proto_alpha/lib_sc_rollup_node/batcher.ml | 25 ++++++++++++++++++ .../batcher_worker_types.ml | 13 ---------- .../batcher_worker_types.mli | 2 -- .../lib_sc_rollup_node/commitment.ml | 26 +++++++++++++++++++ .../publisher_worker_types.ml | 13 ---------- .../publisher_worker_types.mli | 2 -- 6 files changed, 51 insertions(+), 30 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/batcher.ml b/src/proto_alpha/lib_sc_rollup_node/batcher.ml index 27aa3059d536..f3fcf5434079 100644 --- a/src/proto_alpha/lib_sc_rollup_node/batcher.ml +++ b/src/proto_alpha/lib_sc_rollup_node/batcher.ml @@ -34,6 +34,10 @@ end module Batched_messages = Hash_queue.Make (L2_message.Hash) (L2_batched_message) +(* Count instances of the batcher functor to allow for multiple worker events + without conflicts. *) +let instances_count = ref 0 + module type S = sig type status = Pending_batch | Batched of Injector.Inj_operation.hash @@ -61,6 +65,8 @@ module type S = sig end module Make (Simulation : Simulation.S) : S = struct + let () = incr instances_count + module PVM = Simulation.PVM type status = Pending_batch | Batched of Injector.Inj_operation.hash @@ -265,6 +271,25 @@ module Make (Simulation : Simulation.S) : S = struct } end + module Name = struct + (* We only have a single batcher in the node *) + type t = unit + + let encoding = Data_encoding.unit + + let base = + (* But we can have multiple instances in the unit tests. This is just to + avoid conflicts in the events declarations. *) + [ + ("sc_rollup_batcher" + ^ if !instances_count = 1 then "" else string_of_int !instances_count); + ] + + let pp _ _ = () + + let equal () () = true + end + module Worker = Worker.MakeSingle (Name) (Request) (Types) type worker = Worker.infinite Worker.queue Worker.t diff --git a/src/proto_alpha/lib_sc_rollup_node/batcher_worker_types.ml b/src/proto_alpha/lib_sc_rollup_node/batcher_worker_types.ml index 63afd1221a6d..c0fe7350814c 100644 --- a/src/proto_alpha/lib_sc_rollup_node/batcher_worker_types.ml +++ b/src/proto_alpha/lib_sc_rollup_node/batcher_worker_types.ml @@ -75,16 +75,3 @@ module Request = struct level | Batch -> Format.pp_print_string ppf "batch" end - -module Name = struct - (* We only have a single batcher right now *) - type t = unit - - let encoding = Data_encoding.unit - - let base = ["sc_rollup_batcher"] - - let pp _ _ = () - - let equal () () = true -end diff --git a/src/proto_alpha/lib_sc_rollup_node/batcher_worker_types.mli b/src/proto_alpha/lib_sc_rollup_node/batcher_worker_types.mli index 681291fbd22f..d3a3fd2c09e9 100644 --- a/src/proto_alpha/lib_sc_rollup_node/batcher_worker_types.mli +++ b/src/proto_alpha/lib_sc_rollup_node/batcher_worker_types.mli @@ -42,5 +42,3 @@ module Request : sig with type ('a, 'request_error) t := ('a, 'request_error) t and type view := view end - -module Name : Worker_intf.NAME with type t = unit diff --git a/src/proto_alpha/lib_sc_rollup_node/commitment.ml b/src/proto_alpha/lib_sc_rollup_node/commitment.ml index f1c09fefe9d2..44210fafa900 100644 --- a/src/proto_alpha/lib_sc_rollup_node/commitment.ml +++ b/src/proto_alpha/lib_sc_rollup_node/commitment.ml @@ -81,7 +81,13 @@ let sc_rollup_challenge_window node_ctxt = let next_commitment_level node_ctxt last_commitment_level = add_level last_commitment_level (sc_rollup_commitment_period node_ctxt) +(* Count instances of the commitment functor to allow for multiple worker events + without conflicts. *) +let instances_count = ref 0 + module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct + let () = incr instances_count + module PVM = PVM type state = Node_context.ro @@ -406,6 +412,26 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct type parameters = {node_ctxt : Node_context.ro} end + module Name = struct + (* We only have a single committer in the node *) + type t = unit + + let encoding = Data_encoding.unit + + let base = + (* But we can have multiple instances in the unit tests. This is just to + avoid conflicts in the events declarations. *) + [ + ("sc_rollup_commitment_publisher" + ^ if !instances_count = 1 then "" else string_of_int !instances_count + ); + ] + + let pp _ _ = () + + let equal () () = true + end + module Worker = Worker.MakeSingle (Name) (Request) (Types) type worker = Worker.infinite Worker.queue Worker.t diff --git a/src/proto_alpha/lib_sc_rollup_node/publisher_worker_types.ml b/src/proto_alpha/lib_sc_rollup_node/publisher_worker_types.ml index 2ae31036e310..c10e697273e3 100644 --- a/src/proto_alpha/lib_sc_rollup_node/publisher_worker_types.ml +++ b/src/proto_alpha/lib_sc_rollup_node/publisher_worker_types.ml @@ -55,16 +55,3 @@ module Request = struct | Publish -> Format.pp_print_string ppf "publish" | Cement -> Format.pp_print_string ppf "cement" end - -module Name = struct - (* We only have a single commitment publisher right now *) - type t = unit - - let encoding = Data_encoding.unit - - let base = ["sc_rollup_commitment_publisher"] - - let pp _ _ = () - - let equal () () = true -end diff --git a/src/proto_alpha/lib_sc_rollup_node/publisher_worker_types.mli b/src/proto_alpha/lib_sc_rollup_node/publisher_worker_types.mli index ab244e31a486..eed1fdcad468 100644 --- a/src/proto_alpha/lib_sc_rollup_node/publisher_worker_types.mli +++ b/src/proto_alpha/lib_sc_rollup_node/publisher_worker_types.mli @@ -38,5 +38,3 @@ module Request : sig with type ('a, 'request_error) t := ('a, 'request_error) t and type view := view end - -module Name : Worker_intf.NAME with type t = unit -- GitLab From c391e5f5f56afbadcaecff39ddcd5bd24642c46e Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 24 Feb 2023 10:56:54 +0100 Subject: [PATCH 02/12] SCORU/Node: one directory per RPC server instance --- .../lib_sc_rollup_node/RPC_server.ml | 198 +++++++++--------- 1 file changed, 100 insertions(+), 98 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/RPC_server.ml b/src/proto_alpha/lib_sc_rollup_node/RPC_server.ml index ea3457213c2a..bc9bcbf82b63 100644 --- a/src/proto_alpha/lib_sc_rollup_node/RPC_server.ml +++ b/src/proto_alpha/lib_sc_rollup_node/RPC_server.ml @@ -53,110 +53,112 @@ end let get_dal_processed_slots node_ctxt block = Node_context.list_slots_statuses node_ctxt ~confirmed_in_block_hash:block -module Global_directory = Make_directory (struct - include Sc_rollup_services.Global - - type context = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Proof_helpers_directory = Make_directory (struct - include Sc_rollup_services.Global.Helpers - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type context = Node_context.rw - - let context_of_prefix node_ctxt () = return node_ctxt -end) - -module Local_directory = Make_directory (struct - include Sc_rollup_services.Local - - type context = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Block_directory = Make_directory (struct - include Sc_rollup_services.Global.Block - - type context = Node_context.ro * Block_hash.t - - let context_of_prefix node_ctxt (((), block) : prefix) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block) -end) - -module Outbox_directory = Make_directory (struct - include Sc_rollup_services.Global.Block.Outbox - - type context = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t - - let context_of_prefix node_ctxt (((), block), level) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block, level) -end) - -module Common = struct - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.block - @@ fun (node_ctxt, block) () () -> - Node_context.get_full_l2_block node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.num_messages - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* l2_block = Node_context.get_l2_block node_ctxt block in - let+ num_messages = - Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness - in - Z.of_int num_messages - - let () = - Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address - @@ fun node_ctxt () () -> return @@ node_ctxt.rollup_address - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_head - @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_level - @@ fun node_ctxt () () -> get_head_level_opt node_ctxt - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.hash - @@ fun (_node_ctxt, block) () () -> return block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.level - @@ fun (node_ctxt, block) () () -> - Node_context.level_of_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.inbox - @@ fun (node_ctxt, block) () () -> - Node_context.get_inbox_by_block_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ l2_block = Node_context.get_l2_block node_ctxt block in - Z.of_int64 l2_block.num_ticks -end - module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct module PVM = Simulation.PVM module Interpreter = Simulation.Interpreter module Outbox = Outbox.Make (PVM) module Free_pvm = Interpreter.Free_pvm + module Global_directory = Make_directory (struct + include Sc_rollup_services.Global + + type context = Node_context.ro + + let context_of_prefix node_ctxt () = + return (Node_context.readonly node_ctxt) + end) + + module Proof_helpers_directory = Make_directory (struct + include Sc_rollup_services.Global.Helpers + + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type context = Node_context.rw + + let context_of_prefix node_ctxt () = return node_ctxt + end) + + module Local_directory = Make_directory (struct + include Sc_rollup_services.Local + + type context = Node_context.ro + + let context_of_prefix node_ctxt () = + return (Node_context.readonly node_ctxt) + end) + + module Block_directory = Make_directory (struct + include Sc_rollup_services.Global.Block + + type context = Node_context.ro * Block_hash.t + + let context_of_prefix node_ctxt (((), block) : prefix) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block) + end) + + module Outbox_directory = Make_directory (struct + include Sc_rollup_services.Global.Block.Outbox + + type context = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t + + let context_of_prefix node_ctxt (((), block), level) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block, level) + end) + + module Common = struct + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.block + @@ fun (node_ctxt, block) () () -> + Node_context.get_full_l2_block node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.num_messages + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* l2_block = Node_context.get_l2_block node_ctxt block in + let+ num_messages = + Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness + in + Z.of_int num_messages + + let () = + Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address + @@ fun node_ctxt () () -> return @@ node_ctxt.rollup_address + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_head + @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_level + @@ fun node_ctxt () () -> get_head_level_opt node_ctxt + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.hash + @@ fun (_node_ctxt, block) () () -> return block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.level + @@ fun (node_ctxt, block) () () -> + Node_context.level_of_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.inbox + @@ fun (node_ctxt, block) () () -> + Node_context.get_inbox_by_block_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ l2_block = Node_context.get_l2_block node_ctxt block in + Z.of_int64 l2_block.num_ticks + end + let get_state (node_ctxt : _ Node_context.t) block_hash = let open Lwt_result_syntax in let* ctxt = Node_context.checkout_context node_ctxt block_hash in -- GitLab From 61af4060eae146e313d821a23e9a3da6cf5bd649 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Wed, 1 Mar 2023 11:22:32 +0100 Subject: [PATCH 03/12] SCORU/Node: expose internal functions for unit tests --- src/lib_crawler/layer_1.ml | 13 ++ src/lib_crawler/layer_1.mli | 9 ++ src/proto_alpha/lib_sc_rollup_node/daemon.ml | 62 ++++++++++ src/proto_alpha/lib_sc_rollup_node/inbox.ml | 114 ++++++++++-------- src/proto_alpha/lib_sc_rollup_node/inbox.mli | 19 +++ .../lib_sc_rollup_node/node_context.ml | 54 +++++++++ .../lib_sc_rollup_node/node_context.mli | 14 +++ 7 files changed, 236 insertions(+), 49 deletions(-) diff --git a/src/lib_crawler/layer_1.ml b/src/lib_crawler/layer_1.ml index 70a128dec832..2431fc7d5eb0 100644 --- a/src/lib_crawler/layer_1.ml +++ b/src/lib_crawler/layer_1.ml @@ -308,3 +308,16 @@ let get_tezos_reorg_for_new_head l1_state ?get_old_predecessor old_head new_head ?get_old_predecessor old_head new_head + +module Internal_for_tests = struct + let dummy cctxt = + let heads, _push = Lwt_stream.create () in + { + name = "dummy_layer_1_for_tests"; + reconnection_delay = 5.0; + heads; + cctxt = (cctxt :> Client_context.full); + stopper = Fun.id; + running = false; + } +end diff --git a/src/lib_crawler/layer_1.mli b/src/lib_crawler/layer_1.mli index e03581a8734d..847767538340 100644 --- a/src/lib_crawler/layer_1.mli +++ b/src/lib_crawler/layer_1.mli @@ -93,3 +93,12 @@ val get_tezos_reorg_for_new_head : [`Head of Block_hash.t * int32 | `Level of int32] -> Block_hash.t * int32 -> (Block_hash.t * int32) Reorg.t tzresult Lwt.t + +(**/**) + +module Internal_for_tests : sig + (** Create a dummy Layer 1 object that does not follow any L1 chain. This + function is only to be used as a placeholder for unit tests (that do not + exercise the Layer 1 connection). *) + val dummy : #Client_context.full -> t +end diff --git a/src/proto_alpha/lib_sc_rollup_node/daemon.ml b/src/proto_alpha/lib_sc_rollup_node/daemon.ml index 6c40166adcd2..b824df25ac95 100644 --- a/src/proto_alpha/lib_sc_rollup_node/daemon.ml +++ b/src/proto_alpha/lib_sc_rollup_node/daemon.ml @@ -564,6 +564,68 @@ module Make (PVM : Pvm.S) = struct Format.eprintf "%!%a@.Exiting.@." pp_print_trace e ; let*! _ = Lwt_exit.exit_and_wait 1 in return_unit) + + module Internal_for_tests = struct + (** Same as {!process_head} but only builds and stores the L2 block + corresponding to [messages]. It is used by the unit tests to build an L2 + chain. *) + let process_messages (node_ctxt : _ Node_context.t) ~is_migration_block + ~predecessor ~predecessor_timestamp head messages = + let open Lwt_result_syntax in + let* () = Node_context.save_level node_ctxt head in + let* inbox_hash, inbox, inbox_witness, messages, ctxt = + Inbox.Internal_for_tests.process_messages + node_ctxt + ~is_migration_block + ~predecessor + ~predecessor_timestamp + ~level:head.level + messages + in + let* ctxt, _num_messages, num_ticks, initial_tick = + Components.Interpreter.process_head + node_ctxt + ctxt + ~predecessor + head + (inbox, messages) + in + let*! context_hash = Context.commit ctxt in + let* commitment_hash = + Components.Commitment.process_head + node_ctxt + ~predecessor:predecessor.Layer1.hash + head + ctxt + in + let level = Raw_level.of_int32_exn head.level in + let* previous_commitment_hash = + if level = node_ctxt.genesis_info.Sc_rollup.Commitment.level then + (* Previous commitment for rollup genesis is itself. *) + return node_ctxt.genesis_info.Sc_rollup.Commitment.commitment_hash + else + let+ pred = Node_context.get_l2_block node_ctxt predecessor.hash in + Sc_rollup_block.most_recent_commitment pred.header + in + let header = + Sc_rollup_block. + { + block_hash = head.hash; + level; + predecessor = predecessor.hash; + commitment_hash; + previous_commitment_hash; + context = context_hash; + inbox_witness; + inbox_hash; + } + in + let l2_block = + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} + in + let* () = Node_context.save_l2_head node_ctxt l2_block in + return l2_block + end end let run ~data_dir (configuration : Configuration.t) diff --git a/src/proto_alpha/lib_sc_rollup_node/inbox.ml b/src/proto_alpha/lib_sc_rollup_node/inbox.ml index ae42c820870e..61a8e4b3226a 100644 --- a/src/proto_alpha/lib_sc_rollup_node/inbox.ml +++ b/src/proto_alpha/lib_sc_rollup_node/inbox.ml @@ -133,6 +133,52 @@ let add_messages ~is_migration_block ~predecessor_timestamp ~predecessor inbox inbox, messages_with_protocol_internal_messages ) +let process_messages (node_ctxt : _ Node_context.t) ~is_migration_block + ~predecessor ~predecessor_timestamp ~level messages = + let open Lwt_result_syntax in + let* inbox = Node_context.inbox_of_head node_ctxt predecessor in + let inbox_metrics = Metrics.Inbox.metrics in + Prometheus.Gauge.set inbox_metrics.head_inbox_level @@ Int32.to_float level ; + let*? level = Environment.wrap_tzresult @@ Raw_level.of_int32 level in + let* ctxt = + if Raw_level.(level <= node_ctxt.Node_context.genesis_info.level) then + (* This is before we have interpreted the boot sector, so we start + with an empty context in genesis *) + return (Context.empty node_ctxt.context) + else Node_context.checkout_context node_ctxt predecessor.hash + in + let* ( _messages_history, + witness_hash, + inbox, + messages_with_protocol_internal_messages ) = + add_messages + ~is_migration_block + ~predecessor_timestamp + ~predecessor:predecessor.hash + inbox + messages + in + Metrics.Inbox.Stats.head_messages_list := + messages_with_protocol_internal_messages ; + let* () = + Node_context.save_messages + node_ctxt + witness_hash + { + is_migration_block; + predecessor = predecessor.hash; + predecessor_timestamp; + messages; + } + in + let* inbox_hash = Node_context.save_inbox node_ctxt inbox in + return + ( inbox_hash, + inbox, + witness_hash, + messages_with_protocol_internal_messages, + ctxt ) + let process_head (node_ctxt : _ Node_context.t) ~predecessor Layer1.{level; hash = head_hash} = let open Lwt_result_syntax in @@ -140,68 +186,34 @@ let process_head (node_ctxt : _ Node_context.t) ~predecessor Raw_level.to_int32 node_ctxt.genesis_info.level |> Int32.succ in if level >= first_inbox_level then ( - (* - - We compute the inbox of this block using the inbox of its - predecessor. That way, the computation of inboxes is robust - to chain reorganization. - - *) - let* inbox = Node_context.inbox_of_head node_ctxt predecessor in - let inbox_metrics = Metrics.Inbox.metrics in - Prometheus.Gauge.set inbox_metrics.head_inbox_level @@ Int32.to_float level ; - let*? level = Environment.wrap_tzresult @@ Raw_level.of_int32 level in - let* ctxt = - if Raw_level.(level <= node_ctxt.Node_context.genesis_info.level) then - (* This is before we have interpreted the boot sector, so we start - with an empty context in genesis *) - return (Context.empty node_ctxt.context) - else Node_context.checkout_context node_ctxt predecessor.hash - in + (* We compute the inbox of this block using the inbox of its + predecessor. That way, the computation of inboxes is robust to chain + reorganization. *) let* ( is_migration_block, collected_messages, predecessor_timestamp, predecessor_hash ) = get_messages node_ctxt head_hash in + assert (Block_hash.(predecessor.Layer1.hash = predecessor_hash)) ; let*! () = - Inbox_event.get_messages - head_hash - (Raw_level.to_int32 level) - (List.length collected_messages) + Inbox_event.get_messages head_hash level (List.length collected_messages) in - let* ( _messages_history, - witness_hash, - inbox, - messages_with_protocol_internal_messages ) = - add_messages + let* (( _inbox_hash, + inbox, + _witness_hash, + _messages_with_protocol_internal_messages, + _ctxt ) as res) = + process_messages + node_ctxt ~is_migration_block + ~predecessor ~predecessor_timestamp - ~predecessor:predecessor_hash - inbox + ~level collected_messages in - Metrics.Inbox.Stats.head_messages_list := - messages_with_protocol_internal_messages ; - let* () = - Node_context.save_messages - node_ctxt - witness_hash - { - is_migration_block; - predecessor = predecessor_hash; - predecessor_timestamp; - messages = collected_messages; - } - in let* () = same_inbox_as_layer_1 node_ctxt head_hash inbox in - let* inbox_hash = Node_context.save_inbox node_ctxt inbox in - return - ( inbox_hash, - inbox, - witness_hash, - messages_with_protocol_internal_messages, - ctxt )) + return res) else let* inbox = Node_context.genesis_inbox node_ctxt in return @@ -238,3 +250,7 @@ let payloads_history_of_messages ~is_migration_block ~predecessor messages in payloads_history + +module Internal_for_tests = struct + let process_messages = process_messages +end diff --git a/src/proto_alpha/lib_sc_rollup_node/inbox.mli b/src/proto_alpha/lib_sc_rollup_node/inbox.mli index 23c035774f3a..58023fccfe4e 100644 --- a/src/proto_alpha/lib_sc_rollup_node/inbox.mli +++ b/src/proto_alpha/lib_sc_rollup_node/inbox.mli @@ -82,3 +82,22 @@ val payloads_history_of_messages : predecessor_timestamp:Timestamp.time -> Sc_rollup.Inbox_message.t list -> Sc_rollup.Inbox_merkelized_payload_hashes.History.t tzresult + +(**/**) + +module Internal_for_tests : sig + val process_messages : + Node_context.rw -> + is_migration_block:bool -> + predecessor:Layer1.head -> + predecessor_timestamp:Timestamp.time -> + level:int32 -> + Sc_rollup.Inbox_message.t list -> + (Sc_rollup.Inbox.Hash.t + * Sc_rollup.Inbox.t + * Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t + * Sc_rollup.Inbox_message.t list + * Context.rw) + tzresult + Lwt.t +end diff --git a/src/proto_alpha/lib_sc_rollup_node/node_context.ml b/src/proto_alpha/lib_sc_rollup_node/node_context.ml index f722a271d211..ed848a8f4de1 100644 --- a/src/proto_alpha/lib_sc_rollup_node/node_context.ml +++ b/src/proto_alpha/lib_sc_rollup_node/node_context.ml @@ -717,3 +717,57 @@ let find_confirmed_slots_histories {store; _} = let save_confirmed_slots_histories {store; _} = Store.Dal_confirmed_slots_histories.add store.irmin_store + +module Internal_for_tests = struct + let create_node_context cctxt + ?(constants = Default_parameters.constants_mainnet) ~data_dir kind = + let open Lwt_result_syntax in + let l2_blocks_cache_size = Configuration.default_l2_blocks_cache_size in + let protocol_constants = + constants + |> Data_encoding.Binary.to_bytes_exn Constants.Parametric.encoding + |> Data_encoding.Binary.of_bytes_exn Constants_parametric_repr.encoding + |> Constants_repr.all_of_parametric + |> Data_encoding.Binary.to_bytes_exn Constants_repr.encoding + |> Data_encoding.Binary.of_bytes_exn Constants.encoding + in + let* store = + Store.load + Read_write + ~l2_blocks_cache_size + Configuration.(default_storage_dir data_dir) + in + let*! context = + Context.load Read_write (Configuration.default_context_dir data_dir) + in + let genesis_info = + Sc_rollup.Commitment.{level = Raw_level.root; commitment_hash = Hash.zero} + in + let l1_ctxt = Layer1.Internal_for_tests.dummy cctxt in + let lcc = + Reference.new_ + {commitment = Sc_rollup.Commitment.Hash.zero; level = Raw_level.root} + in + let lpc = Reference.new_ None in + return + { + cctxt; + dal_cctxt = None; + data_dir; + l1_ctxt; + rollup_address = Sc_rollup.Address.zero; + mode = Observer; + operators = Configuration.Operator_purpose_map.empty; + genesis_info; + lcc; + lpc; + kind; + injector_retention_period = 0; + block_finality_time = 2; + fee_parameters = Configuration.default_fee_parameters; + protocol_constants; + loser_mode = Loser_mode.no_failures; + store; + context; + } +end diff --git a/src/proto_alpha/lib_sc_rollup_node/node_context.mli b/src/proto_alpha/lib_sc_rollup_node/node_context.mli index c2bbebd6f04d..7583b4377195 100644 --- a/src/proto_alpha/lib_sc_rollup_node/node_context.mli +++ b/src/proto_alpha/lib_sc_rollup_node/node_context.mli @@ -426,3 +426,17 @@ val find_confirmed_slots_histories : val save_confirmed_slots_histories : rw -> Block_hash.t -> Dal.Slots_history.History_cache.t -> unit tzresult Lwt.t + +(**/**) + +module Internal_for_tests : sig + (** Create a node context which really stores data on disk but does not + connect to any layer 1 node. It is meant to be used in unit tests for the + rollup node functions. *) + val create_node_context : + Protocol_client_context.full -> + ?constants:Constants.Parametric.t -> + data_dir:string -> + Sc_rollup.Kind.t -> + Store_sigs.rw t tzresult Lwt.t +end -- GitLab From b10d6fc8312706a3647181cbf0a2efd4e535e975 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 23 Feb 2023 21:52:39 +0100 Subject: [PATCH 04/12] SCORU/Node: retrieve genesis inbox from disk first before RPC --- .../lib_sc_rollup_node/interpreter.ml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/interpreter.ml b/src/proto_alpha/lib_sc_rollup_node/interpreter.ml index 5276c655db4c..99abd3eb93a0 100644 --- a/src/proto_alpha/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/lib_sc_rollup_node/interpreter.ml @@ -119,15 +119,15 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct let state_of_head node_ctxt ctxt Layer1.{hash; level} = let open Lwt_result_syntax in - let genesis_level = - Raw_level.to_int32 node_ctxt.Node_context.genesis_info.level - in - if level = genesis_level then genesis_state hash node_ctxt ctxt - else - let*! state = PVM.State.find ctxt in - match state with - | None -> tzfail (Sc_rollup_node_errors.Missing_PVM_state (hash, level)) - | Some state -> return (ctxt, state) + let*! state = PVM.State.find ctxt in + match state with + | None -> + let genesis_level = + Raw_level.to_int32 node_ctxt.Node_context.genesis_info.level + in + if level = genesis_level then genesis_state hash node_ctxt ctxt + else tzfail (Sc_rollup_node_errors.Missing_PVM_state (hash, level)) + | Some state -> return (ctxt, state) (** [transition_pvm node_ctxt predecessor head] runs a PVM at the previous state from block [predecessor] by consuming as many messages -- GitLab From 3624bf516534a3f3cadad0761f91ae054c7250f5 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Wed, 29 Mar 2023 09:31:45 +0200 Subject: [PATCH 05/12] Injector: events with parameters section Prevent duplicate events in the tezt main binary. --- src/lib_injector/injector_events.ml | 2 +- src/lib_injector/injector_functor.ml | 12 ++++++++++++ src/lib_injector/injector_worker_types.ml | 12 ------------ src/lib_injector/injector_worker_types.mli | 2 -- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/lib_injector/injector_events.ml b/src/lib_injector/injector_events.ml index ff6ff1062819..a9ab5a36ea12 100644 --- a/src/lib_injector/injector_events.ml +++ b/src/lib_injector/injector_events.ml @@ -35,7 +35,7 @@ module Make struct include Internal_event.Simple - let section = Parameters.events_section + let section = Parameters.events_section @ ["injector"] let monitoring_error = declare_1 diff --git a/src/lib_injector/injector_functor.ml b/src/lib_injector/injector_functor.ml index 729265213423..0c7b4492b4ef 100644 --- a/src/lib_injector/injector_functor.ml +++ b/src/lib_injector/injector_functor.ml @@ -989,6 +989,18 @@ struct } end + module Name = struct + type t = Signature.public_key_hash + + let encoding = Signature.Public_key_hash.encoding + + let base = Parameters.events_section @ ["injector"] + + let pp = Signature.Public_key_hash.pp_short + + let equal = Signature.Public_key_hash.equal + end + (* The worker for the injector. *) module Worker = Worker.MakeSingle (Name) (Request) (Types) diff --git a/src/lib_injector/injector_worker_types.ml b/src/lib_injector/injector_worker_types.ml index bd4c61d94b92..89aea4f06926 100644 --- a/src/lib_injector/injector_worker_types.ml +++ b/src/lib_injector/injector_worker_types.ml @@ -80,15 +80,3 @@ module Request (L1_operation : INJECTOR_OPERATION) = struct level | Inject -> Format.fprintf ppf "injection" end - -module Name = struct - type t = Signature.public_key_hash - - let encoding = Signature.Public_key_hash.encoding - - let base = ["injector"] - - let pp = Signature.Public_key_hash.pp_short - - let equal = Signature.Public_key_hash.equal -end diff --git a/src/lib_injector/injector_worker_types.mli b/src/lib_injector/injector_worker_types.mli index 829bf8037613..98f748fa813a 100644 --- a/src/lib_injector/injector_worker_types.mli +++ b/src/lib_injector/injector_worker_types.mli @@ -39,5 +39,3 @@ module Request (Inj_operation : INJECTOR_OPERATION) : sig with type ('a, 'request_error) t := ('a, 'request_error) t and type view := view end - -module Name : Worker_intf.NAME with type t = Signature.public_key_hash -- GitLab From 44dd8c6198d01b23e06887810d10e74a368218c2 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 28 Mar 2023 18:46:26 +0200 Subject: [PATCH 06/12] SCORU/Node: register errors with protocol prefix --- src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml | 4 ++-- src/proto_alpha/lib_sc_rollup_node/layer1.ml | 2 +- src/proto_alpha/lib_sc_rollup_node/reveals.ml | 6 +++--- src/proto_alpha/lib_sc_rollup_node/sc_rollup_node_errors.ml | 4 ++++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml index 0f1ed5f27595..5744e9f67728 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml +++ b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml @@ -38,7 +38,7 @@ type error += | Dal_invalid_page_for_slot of Dal.Page.t let () = - register_error_kind + Sc_rollup_node_errors.register_error_kind `Permanent ~id:"dal_pages_request.dal_slot_not_found_in_store" ~title:"Dal slot not found in store" @@ -48,7 +48,7 @@ let () = Data_encoding.(obj1 (req "slot_id" Dal.Slot.Header.id_encoding)) (function Dal_slot_not_found_in_store slot_id -> Some slot_id | _ -> None) (fun slot_id -> Dal_slot_not_found_in_store slot_id) ; - register_error_kind + Sc_rollup_node_errors.register_error_kind `Permanent ~id:"dal_pages_request.dal_invalid_page_for_slot" ~title:"Invalid Dal page requested for slot" diff --git a/src/proto_alpha/lib_sc_rollup_node/layer1.ml b/src/proto_alpha/lib_sc_rollup_node/layer1.ml index d32f5b6d5c3f..70c02794a585 100644 --- a/src/proto_alpha/lib_sc_rollup_node/layer1.ml +++ b/src/proto_alpha/lib_sc_rollup_node/layer1.ml @@ -36,7 +36,7 @@ open Protocol_client_context type error += Cannot_find_block of Block_hash.t let () = - register_error_kind + Sc_rollup_node_errors.register_error_kind ~id:"sc_rollup.node.cannot_find_block" ~title:"Cannot find block from L1" ~description:"A block couldn't be found from the L1 node" diff --git a/src/proto_alpha/lib_sc_rollup_node/reveals.ml b/src/proto_alpha/lib_sc_rollup_node/reveals.ml index e61edb2d4003..59fb19e4a7d0 100644 --- a/src/proto_alpha/lib_sc_rollup_node/reveals.ml +++ b/src/proto_alpha/lib_sc_rollup_node/reveals.ml @@ -32,7 +32,7 @@ type error += | Could_not_encode_raw_data let () = - register_error_kind + Sc_rollup_node_errors.register_error_kind ~id:"sc_rollup.node.wrong_hash_of_reveal_preimage" ~title:"Hash of reveal preimage is not correct" ~description:"Hash of reveal preimage is not correct." @@ -52,7 +52,7 @@ let () = (function | Wrong_hash {found; expected} -> Some (found, expected) | _ -> None) (fun (found, expected) -> Wrong_hash {found; expected}) ; - register_error_kind + Sc_rollup_node_errors.register_error_kind ~id:"sc_rollup.node.could_not_open_reveal_preimage_file" ~title:"Could not open reveal preimage file" ~description:"Could not open reveal preimage file." @@ -66,7 +66,7 @@ let () = (function | Could_not_open_preimage_file filename -> Some filename | _ -> None) (fun filename -> Could_not_open_preimage_file filename) ; - register_error_kind + Sc_rollup_node_errors.register_error_kind ~id:"sc_rollup.node.could_not_encode_raw_data" ~title:"Could not encode raw data to reveal" ~description:"Could not encode raw data to reveal." diff --git a/src/proto_alpha/lib_sc_rollup_node/sc_rollup_node_errors.ml b/src/proto_alpha/lib_sc_rollup_node/sc_rollup_node_errors.ml index c2b5d2a39946..fb10fab765cf 100644 --- a/src/proto_alpha/lib_sc_rollup_node/sc_rollup_node_errors.ml +++ b/src/proto_alpha/lib_sc_rollup_node/sc_rollup_node_errors.ml @@ -25,6 +25,10 @@ open Protocol.Alpha_context +let make_id id = String.concat "." [Protocol.name; id] + +let register_error_kind ~id = register_error_kind ~id:(make_id id) + type error += | Cannot_produce_proof of Sc_rollup.Inbox.t * Raw_level.t | Missing_mode_operators of {mode : string; missing_operators : string list} -- GitLab From f593334c62a922370bfcd1be5a77a15590cf19df Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Wed, 29 Mar 2023 09:32:15 +0200 Subject: [PATCH 07/12] SCORU/Node: events with protocol name Prevent duplicate events in the tezt main binary. --- src/proto_alpha/lib_sc_rollup_node/batcher.ml | 10 ++++++---- src/proto_alpha/lib_sc_rollup_node/batcher_events.ml | 2 +- src/proto_alpha/lib_sc_rollup_node/commitment.ml | 12 +++++++----- .../lib_sc_rollup_node/commitment_event.ml | 4 +++- .../lib_sc_rollup_node/commitment_event.mli | 3 +++ src/proto_alpha/lib_sc_rollup_node/daemon_event.ml | 2 +- .../lib_sc_rollup_node/dal_slots_tracker_event.ml | 2 +- src/proto_alpha/lib_sc_rollup_node/event.ml | 2 +- src/proto_alpha/lib_sc_rollup_node/inbox_event.ml | 2 +- src/proto_alpha/lib_sc_rollup_node/injector.ml | 2 +- .../lib_sc_rollup_node/interpreter_event.ml | 2 +- src/proto_alpha/lib_sc_rollup_node/layer1_event.ml | 2 +- .../lib_sc_rollup_node/refutation_game_event.ml | 3 ++- tezt/tests/sc_rollup.ml | 7 +------ 14 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/batcher.ml b/src/proto_alpha/lib_sc_rollup_node/batcher.ml index f3fcf5434079..f2090acd0697 100644 --- a/src/proto_alpha/lib_sc_rollup_node/batcher.ml +++ b/src/proto_alpha/lib_sc_rollup_node/batcher.ml @@ -280,10 +280,12 @@ module Make (Simulation : Simulation.S) : S = struct let base = (* But we can have multiple instances in the unit tests. This is just to avoid conflicts in the events declarations. *) - [ - ("sc_rollup_batcher" - ^ if !instances_count = 1 then "" else string_of_int !instances_count); - ] + Batcher_events.Worker.section + @ [ + ("worker" + ^ if !instances_count = 1 then "" else string_of_int !instances_count + ); + ] let pp _ _ = () diff --git a/src/proto_alpha/lib_sc_rollup_node/batcher_events.ml b/src/proto_alpha/lib_sc_rollup_node/batcher_events.ml index 5d0ad969a3c0..da9ad33fa984 100644 --- a/src/proto_alpha/lib_sc_rollup_node/batcher_events.ml +++ b/src/proto_alpha/lib_sc_rollup_node/batcher_events.ml @@ -25,7 +25,7 @@ include Internal_event.Simple -let section = ["sc_rollup_node"; "batcher"] +let section = [Protocol.name; "sc_rollup_node"; "batcher"] let queue = declare_1 diff --git a/src/proto_alpha/lib_sc_rollup_node/commitment.ml b/src/proto_alpha/lib_sc_rollup_node/commitment.ml index 44210fafa900..237c288a0ba9 100644 --- a/src/proto_alpha/lib_sc_rollup_node/commitment.ml +++ b/src/proto_alpha/lib_sc_rollup_node/commitment.ml @@ -421,11 +421,13 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct let base = (* But we can have multiple instances in the unit tests. This is just to avoid conflicts in the events declarations. *) - [ - ("sc_rollup_commitment_publisher" - ^ if !instances_count = 1 then "" else string_of_int !instances_count - ); - ] + Commitment_event.section + @ [ + ("publisher" + ^ + if !instances_count = 1 then "" else string_of_int !instances_count + ); + ] let pp _ _ = () diff --git a/src/proto_alpha/lib_sc_rollup_node/commitment_event.ml b/src/proto_alpha/lib_sc_rollup_node/commitment_event.ml index 7f9135d2792a..e5d62ef4337c 100644 --- a/src/proto_alpha/lib_sc_rollup_node/commitment_event.ml +++ b/src/proto_alpha/lib_sc_rollup_node/commitment_event.ml @@ -30,7 +30,7 @@ open Publisher_worker_types module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "commitment"] + let section = [Protocol.name; "sc_rollup_node"; "commitment"] let starting = declare_0 @@ -157,6 +157,8 @@ let stopping = Simple.(emit stopping) open Sc_rollup.Commitment +let section = Simple.section + let emit_commitment_event f commitment_hash {predecessor; inbox_level; compressed_state; number_of_ticks} = Simple.( diff --git a/src/proto_alpha/lib_sc_rollup_node/commitment_event.mli b/src/proto_alpha/lib_sc_rollup_node/commitment_event.mli index 8f4baf9b0e14..6f583d61cc72 100644 --- a/src/proto_alpha/lib_sc_rollup_node/commitment_event.mli +++ b/src/proto_alpha/lib_sc_rollup_node/commitment_event.mli @@ -33,6 +33,9 @@ val starting : unit -> unit Lwt.t val stopping : unit -> unit Lwt.t +(** Section for commitment events. *) +val section : string list + (** [commitment_stored commitment_hash commitment] emits the event that the [commitment] was stored. *) val commitment_stored : diff --git a/src/proto_alpha/lib_sc_rollup_node/daemon_event.ml b/src/proto_alpha/lib_sc_rollup_node/daemon_event.ml index cf8b076f59f2..73d701430da0 100644 --- a/src/proto_alpha/lib_sc_rollup_node/daemon_event.ml +++ b/src/proto_alpha/lib_sc_rollup_node/daemon_event.ml @@ -26,7 +26,7 @@ module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "daemon"] + let section = [Protocol.name; "sc_rollup_node"; "daemon"] let head_processing = declare_3 diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_slots_tracker_event.ml b/src/proto_alpha/lib_sc_rollup_node/dal_slots_tracker_event.ml index 84ed4506c47f..05620638edb9 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_slots_tracker_event.ml +++ b/src/proto_alpha/lib_sc_rollup_node/dal_slots_tracker_event.ml @@ -29,7 +29,7 @@ open Alpha_context module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "dal_slots_tracker"] + let section = [Protocol.name; "sc_rollup_node"; "dal_slots_tracker"] let slot_has_been_confirmed = declare_3 diff --git a/src/proto_alpha/lib_sc_rollup_node/event.ml b/src/proto_alpha/lib_sc_rollup_node/event.ml index ce5d693db33d..ea2fa9aaa35e 100644 --- a/src/proto_alpha/lib_sc_rollup_node/event.ml +++ b/src/proto_alpha/lib_sc_rollup_node/event.ml @@ -26,7 +26,7 @@ module Simple = struct include Internal_event.Simple - let section = ["smart_rollup_node"] + let section = [Protocol.name; "smart_rollup_node"] let starting_node = declare_0 diff --git a/src/proto_alpha/lib_sc_rollup_node/inbox_event.ml b/src/proto_alpha/lib_sc_rollup_node/inbox_event.ml index 18d83fc56dcd..ed732319c4dc 100644 --- a/src/proto_alpha/lib_sc_rollup_node/inbox_event.ml +++ b/src/proto_alpha/lib_sc_rollup_node/inbox_event.ml @@ -26,7 +26,7 @@ module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "inbox"] + let section = [Protocol.name; "sc_rollup_node"; "inbox"] let starting = declare_0 diff --git a/src/proto_alpha/lib_sc_rollup_node/injector.ml b/src/proto_alpha/lib_sc_rollup_node/injector.ml index 3f4a3648e8fd..1431139541d5 100644 --- a/src/proto_alpha/lib_sc_rollup_node/injector.ml +++ b/src/proto_alpha/lib_sc_rollup_node/injector.ml @@ -37,7 +37,7 @@ module Parameters : and type Operation.t = L1_operation.t = struct type state = Node_context.ro - let events_section = ["sc_rollup.injector"] + let events_section = [Protocol.name; "sc_rollup_node"] module Tag : TAG with type t = Configuration.purpose = struct type t = Configuration.purpose diff --git a/src/proto_alpha/lib_sc_rollup_node/interpreter_event.ml b/src/proto_alpha/lib_sc_rollup_node/interpreter_event.ml index ffc016b9123b..80fab8ac3274 100644 --- a/src/proto_alpha/lib_sc_rollup_node/interpreter_event.ml +++ b/src/proto_alpha/lib_sc_rollup_node/interpreter_event.ml @@ -28,7 +28,7 @@ open Protocol.Alpha_context.Sc_rollup module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "interpreter"] + let section = [Protocol.name; "sc_rollup_node"; "interpreter"] let transitioned_pvm = declare_4 diff --git a/src/proto_alpha/lib_sc_rollup_node/layer1_event.ml b/src/proto_alpha/lib_sc_rollup_node/layer1_event.ml index 6e9773d48ec2..6a0987632b05 100644 --- a/src/proto_alpha/lib_sc_rollup_node/layer1_event.ml +++ b/src/proto_alpha/lib_sc_rollup_node/layer1_event.ml @@ -26,7 +26,7 @@ module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "layer_1"] + let section = [Protocol.name; "sc_rollup_node"; "layer_1"] let starting = declare_0 diff --git a/src/proto_alpha/lib_sc_rollup_node/refutation_game_event.ml b/src/proto_alpha/lib_sc_rollup_node/refutation_game_event.ml index 83857af8579a..45530c687e35 100644 --- a/src/proto_alpha/lib_sc_rollup_node/refutation_game_event.ml +++ b/src/proto_alpha/lib_sc_rollup_node/refutation_game_event.ml @@ -31,7 +31,7 @@ open Protocol.Alpha_context module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "refutation_game"] + let section = [Protocol.name; "sc_rollup_node"; "refutation_game"] let timeout = declare_1 @@ -86,6 +86,7 @@ module Simple = struct let timeout_detected = declare_1 + ~section ~name:"sc_rollup_node_timeout_detected" ~msg: "The rollup node has detected that opponent {other} can be timed out." diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index 32bc33d8db67..63665a0dce41 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -2747,12 +2747,7 @@ let test_refutation_scenario ?commitment_period ?challenge_window ~variant ~mode in gather_dissections () in - let* () = - Sc_rollup_node.run - ~event_sections_levels:[("sc_rollup_node.refutation_game", `Debug)] - sc_rollup_node - [] - in + let* () = Sc_rollup_node.run ~event_level:`Debug sc_rollup_node [] in return [ gather_conflicts_promise; -- GitLab From 5d2e74b64c26588c482e75c8a9d254786e24ca01 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Wed, 1 Mar 2023 14:48:59 +0100 Subject: [PATCH 08/12] Manifest: add unit test file for rollup node --- .gitlab/ci/jobs/packaging/opam_package.yml | 2 + dune-project | 1 + manifest/main.ml | 41 ++++++++++++++++ opam/tezos-sc-rollup-node-test.opam | 31 ++++++++++++ src/proto_alpha/lib_sc_rollup_node/test/dune | 49 +++++++++++++++++++ .../lib_sc_rollup_node/test/helpers/dune | 30 ++++++++++++ .../lib_sc_rollup_node/test/main.ml | 33 +++++++++++++ tezt/tests/dune | 1 + 8 files changed, 188 insertions(+) create mode 100644 opam/tezos-sc-rollup-node-test.opam create mode 100644 src/proto_alpha/lib_sc_rollup_node/test/dune create mode 100644 src/proto_alpha/lib_sc_rollup_node/test/helpers/dune create mode 100644 src/proto_alpha/lib_sc_rollup_node/test/main.ml diff --git a/.gitlab/ci/jobs/packaging/opam_package.yml b/.gitlab/ci/jobs/packaging/opam_package.yml index f50455001dde..5db45cf6494c 100644 --- a/.gitlab/ci/jobs/packaging/opam_package.yml +++ b/.gitlab/ci/jobs/packaging/opam_package.yml @@ -1367,6 +1367,8 @@ opam:tezos-sapling: variables: package: tezos-sapling +# Ignoring unreleased package tezos-sc-rollup-node-test. + opam:tezos-scoru-wasm: extends: - .opam_template diff --git a/dune-project b/dune-project index e696ed8ee53f..900301f7f293 100644 --- a/dune-project +++ b/dune-project @@ -202,6 +202,7 @@ (package (name tezos-rpc-http-client-unix)) (package (name tezos-rpc-http-server)) (package (name tezos-sapling)) +(package (name tezos-sc-rollup-node-test)(allow_empty)) (package (name tezos-scoru-wasm)) (package (name tezos-scoru-wasm-durable-snapshot)(allow_empty)) (package (name tezos-scoru-wasm-fast)) diff --git a/manifest/main.ml b/manifest/main.ml index 50ec70a795e6..4dae738c264b 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -5287,6 +5287,47 @@ module Protocol = Protocol octez_node_config |> if_ N.(number >= 016); ] in + let _octez_sc_rollup_node_test = + only_if N.(number >= 017) @@ fun () -> + let helpers = + private_lib + (sf "octez_smart_rollup_node_%s_test_helpers" short_hash) + ~path:(path // "lib_sc_rollup_node/test/helpers") + ~opam:"tezos-sc-rollup-node-test" + ~deps: + [ + octez_base |> open_ ~m:"TzPervasives" + |> open_ ~m:"TzPervasives.Error_monad.Legacy_monad_globals"; + main |> open_; + parameters |> if_some |> open_; + client |> if_some |> open_; + octez_test_helpers |> open_; + qcheck_alcotest; + qcheck_core; + logs_lwt; + octez_sc_rollup_layer2 |> if_some |> open_; + octez_sc_rollup_node |> if_some |> open_; + alcotezt; + ] + in + tezt + ["canary"] + ~path:(path // "lib_sc_rollup_node/test") + ~opam:"tezos-sc-rollup-node-test" + ~synopsis:"Tests for the smart rollup node library" + ~with_macos_security_framework:true + ~deps: + [ + octez_base |> open_ ~m:"TzPervasives" + |> open_ ~m:"TzPervasives.Error_monad.Legacy_monad_globals"; + main |> open_; + octez_test_helpers |> open_; + octez_sc_rollup_layer2 |> if_some |> open_; + octez_sc_rollup_node |> if_some |> open_; + alcotezt; + helpers |> open_; + ] + in let octez_sc_rollup_client = only_if (active && N.(number >= 016)) @@ fun () -> private_lib diff --git a/opam/tezos-sc-rollup-node-test.opam b/opam/tezos-sc-rollup-node-test.opam new file mode 100644 index 000000000000..eeeea892b333 --- /dev/null +++ b/opam/tezos-sc-rollup-node-test.opam @@ -0,0 +1,31 @@ +# This file was automatically generated, do not edit. +# Edit file manifest/main.ml instead. +opam-version: "2.0" +maintainer: "contact@tezos.com" +authors: ["Tezos devteam"] +homepage: "https://www.tezos.com/" +bug-reports: "https://gitlab.com/tezos/tezos/issues" +dev-repo: "git+https://gitlab.com/tezos/tezos.git" +license: "MIT" +depends: [ + "dune" { >= "3.0" } + "ocaml" { >= "4.14" } + "tezos-base" + "tezos-protocol-alpha" + "tezos-client-alpha" + "tezos-test-helpers" + "qcheck-alcotest" { >= "0.20" } + "qcheck-core" + "logs" + "tezos-smart-rollup-layer2-alpha" + "octez-smart-rollup-node-alpha" + "octez-alcotezt" + "tezt" { with-test & >= "3.0.0" } +] +build: [ + ["rm" "-r" "vendors"] + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} + ["dune" "build" "@runtezt" "-p" name "-j" jobs] {with-test} +] +synopsis: "Tests for the smart rollup node library" diff --git a/src/proto_alpha/lib_sc_rollup_node/test/dune b/src/proto_alpha/lib_sc_rollup_node/test/dune new file mode 100644 index 000000000000..c1ab36f7bf65 --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/test/dune @@ -0,0 +1,49 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name src_proto_alpha_lib_sc_rollup_node_test_tezt_lib) + (instrumentation (backend bisect_ppx)) + (libraries + tezt.core + tezos-base + tezos-protocol-alpha + tezos-test-helpers + tezos-smart-rollup-layer2-alpha + octez_smart_rollup_node_alpha + octez-alcotezt + octez_smart_rollup_node_alpha_test_helpers) + (library_flags (:standard -linkall)) + (flags + (:standard) + -open Tezt_core + -open Tezt_core.Base + -open Tezos_base.TzPervasives + -open Tezos_base.TzPervasives.Error_monad.Legacy_monad_globals + -open Tezos_protocol_alpha + -open Tezos_test_helpers + -open Tezos_smart_rollup_layer2_alpha + -open Octez_smart_rollup_node_alpha + -open Octez_alcotezt + -open Octez_smart_rollup_node_alpha_test_helpers) + (modules canary)) + +(executable + (name main) + (instrumentation (backend bisect_ppx --bisect-sigterm)) + (libraries + src_proto_alpha_lib_sc_rollup_node_test_tezt_lib + tezt) + (link_flags + (:standard) + (:include %{workspace_root}/macos-link-flags.sexp)) + (modules main)) + +(rule + (alias runtezt) + (package tezos-sc-rollup-node-test) + (action (run %{dep:./main.exe}))) + +(rule + (targets main.ml) + (action (with-stdout-to %{targets} (echo "let () = Tezt.Test.run ()")))) diff --git a/src/proto_alpha/lib_sc_rollup_node/test/helpers/dune b/src/proto_alpha/lib_sc_rollup_node/test/helpers/dune new file mode 100644 index 000000000000..d8b04b2883ef --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/test/helpers/dune @@ -0,0 +1,30 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name octez_smart_rollup_node_alpha_test_helpers) + (package tezos-sc-rollup-node-test) + (instrumentation (backend bisect_ppx)) + (libraries + tezos-base + tezos-protocol-alpha + tezos-protocol-alpha.parameters + tezos-client-alpha + tezos-test-helpers + qcheck-alcotest + qcheck-core + logs.lwt + tezos-smart-rollup-layer2-alpha + octez_smart_rollup_node_alpha + octez-alcotezt) + (flags + (:standard) + -open Tezos_base.TzPervasives + -open Tezos_base.TzPervasives.Error_monad.Legacy_monad_globals + -open Tezos_protocol_alpha + -open Tezos_protocol_alpha_parameters + -open Tezos_client_alpha + -open Tezos_test_helpers + -open Tezos_smart_rollup_layer2_alpha + -open Octez_smart_rollup_node_alpha + -open Octez_alcotezt)) diff --git a/src/proto_alpha/lib_sc_rollup_node/test/main.ml b/src/proto_alpha/lib_sc_rollup_node/test/main.ml new file mode 100644 index 000000000000..65b7100bdc11 --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/test/main.ml @@ -0,0 +1,33 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, . *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Smart rollup node library + Invocation: dune exec src/proto_alpha/lib_sc_rollup_node/test/main.exe + Subject: Unit tests for the smart rollup node library + *) + +let () = () diff --git a/tezt/tests/dune b/tezt/tests/dune index f1d8e60280c9..d3384d116e5b 100644 --- a/tezt/tests/dune +++ b/tezt/tests/dune @@ -16,6 +16,7 @@ tezos-stdlib-unix tezos-protocol-alpha tezt_self_tests_tezt_lib + src_proto_alpha_lib_sc_rollup_node_test_tezt_lib src_proto_alpha_lib_protocol_test_unit_tezt_lib src_proto_alpha_lib_protocol_test_regression_tezt_lib src_proto_alpha_lib_protocol_test_pbt_tezt_lib -- GitLab From 784e648d2b77afc95ad0ba20eec8ac2d9d1adf90 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Wed, 1 Mar 2023 14:49:28 +0100 Subject: [PATCH 09/12] Test: helpers for smart rollup node unit tests - Building fake client contexts (no RPCs) - Building rollup node contexts - Building L2 chains --- .../test/helpers/faked_client_context.ml | 172 +++++++++++ .../test/helpers/helpers.ml | 285 ++++++++++++++++++ .../test/helpers/helpers.mli | 116 +++++++ .../lib_sc_rollup_node/test/main.ml | 33 -- 4 files changed, 573 insertions(+), 33 deletions(-) create mode 100644 src/proto_alpha/lib_sc_rollup_node/test/helpers/faked_client_context.ml create mode 100644 src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml create mode 100644 src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.mli delete mode 100644 src/proto_alpha/lib_sc_rollup_node/test/main.ml diff --git a/src/proto_alpha/lib_sc_rollup_node/test/helpers/faked_client_context.ml b/src/proto_alpha/lib_sc_rollup_node/test/helpers/faked_client_context.ml new file mode 100644 index 000000000000..fdf0855af827 --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/test/helpers/faked_client_context.ml @@ -0,0 +1,172 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** This module provides a client contexts that stores information (such as the + wallet, etc.) in memory and that cannot do RPCs but which can be used in + unit tests. + + It is largely taken from the mockup simulator of the baker. + *) + +open Tezos_client_base + +class dummy_prompter : Client_context.prompter = + object + method prompt : type a. (a, string tzresult) Client_context.lwt_format -> a + = + fun _msg -> assert false + + method prompt_password : type a. + (a, Bytes.t tzresult) Client_context.lwt_format -> a = + fun _msg -> assert false + + method multiple_password_retries = false + end + +class faked_ctxt : Tezos_rpc.Context.generic = + let local_ctxt = + Tezos_mockup_proxy.RPC_client.local_ctxt Tezos_rpc.Directory.empty + in + object + method base = local_ctxt#base + + method generic_media_type_call meth ?body uri = + local_ctxt#generic_media_type_call meth ?body uri + + method call_service + : 'm 'p 'q 'i 'o. + (([< Resto.meth] as 'm), unit, 'p, 'q, 'i, 'o) Tezos_rpc.Service.t -> + 'p -> + 'q -> + 'i -> + 'o tzresult Lwt.t = + fun service params query body -> + local_ctxt#call_service service params query body + + method call_streamed_service + : 'm 'p 'q 'i 'o. + (([< Resto.meth] as 'm), unit, 'p, 'q, 'i, 'o) Tezos_rpc.Service.t -> + on_chunk:('o -> unit) -> + on_close:(unit -> unit) -> + 'p -> + 'q -> + 'i -> + (unit -> unit) tzresult Lwt.t = + fun service ~on_chunk ~on_close params query body -> + local_ctxt#call_streamed_service + service + ~on_chunk + ~on_close + params + query + body + end + +class faked_wallet ~base_dir ~filesystem : Client_context.wallet = + object (self) + method load_passwords = None + + method read_file fname = + match String.Hashtbl.find filesystem fname with + | None -> failwith "faked_wallet: cannot read file (%s)" fname + | Some (content, _mtime) -> return content + + method private filename alias_name = + Filename.concat + base_dir + (String.map (function ' ' -> '_' | c -> c) alias_name ^ "s") + + val lock_mutex = Lwt_mutex.create () + + method with_lock : type a. (unit -> a Lwt.t) -> a Lwt.t = + fun f -> Lwt_mutex.with_lock lock_mutex f + + method get_base_dir = base_dir + + method load : type a. + string -> default:a -> a Data_encoding.encoding -> a tzresult Lwt.t = + fun alias_name ~default encoding -> + let filename = self#filename alias_name in + if not (String.Hashtbl.mem filesystem filename) then return default + else + self#read_file filename >>=? fun content -> + let json = (Ezjsonm.from_string content :> Data_encoding.json) in + match Data_encoding.Json.destruct encoding json with + | exception e -> + failwith + "did not understand the %s alias file %s : %s" + alias_name + filename + (Printexc.to_string e) + | data -> return data + + method write : type a. + string -> a -> a Data_encoding.encoding -> unit tzresult Lwt.t = + fun alias_name list encoding -> + let filename = self#filename alias_name in + let json = Data_encoding.Json.construct encoding list in + let str = Ezjsonm.value_to_string (json :> Ezjsonm.value) in + String.Hashtbl.replace + filesystem + filename + (str, Some (Ptime.to_float_s (Ptime_clock.now ()))) ; + return_unit + + method last_modification_time : string -> float option tzresult Lwt.t = + let open Lwt_result_syntax in + fun alias_name -> + let filename = self#filename alias_name in + let file = String.Hashtbl.find_opt filesystem filename in + match file with + | None -> return_none + | Some (_content, mtime) -> return mtime + end + +class faked_io_wallet ~base_dir ~filesystem : Client_context.io_wallet = + let log _channel msg = Logs_lwt.info (fun m -> m "%s" msg) in + object + inherit Client_context.simple_printer log + + inherit dummy_prompter + + inherit faked_wallet ~base_dir ~filesystem + end + +class unix_faked ~base_dir ~filesystem : Client_context.full = + object + inherit faked_io_wallet ~base_dir ~filesystem + + inherit faked_ctxt + + inherit Tezos_client_base_unix.Client_context_unix.unix_ui + + method chain = `Main + + method block = `Head 0 + + method confirmations = None + + method verbose_rpc_error_diagnostics = false + end diff --git a/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml b/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml new file mode 100644 index 000000000000..9b71207218a7 --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml @@ -0,0 +1,285 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, . *) +(* *) +(* 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 Alpha_context + +let uid = ref 0 + +let block_hash_of_level level = + let s = Z.of_int32 level |> Z.to_bits in + let len = String.length s in + let s = + String.init Block_hash.size (fun i -> if i >= len then '\000' else s.[i]) + in + Block_hash.of_string_exn s + +module type S = sig + val with_node_context : + ?constants:Constants.Parametric.t -> + Sc_rollup.Kind.t -> + boot_sector:string -> + ([`Read | `Write] Node_context.t -> + genesis:Sc_rollup_block.t -> + 'a tzresult Lwt.t) -> + 'a tzresult Lwt.t + + val add_l2_genesis_block : + [`Read | `Write] Node_context.t -> + boot_sector:string -> + ((Sc_rollup_block.header, unit) Sc_rollup_block.block, tztrace) result Lwt.t + + val append_l2_block : + [`Read | `Write] Node_context.t -> + ?is_migration_block:bool -> + Sc_rollup.Inbox_message.t trace -> + ((Sc_rollup_block.header, unit) Sc_rollup_block.block, tztrace) result Lwt.t +end + +module Make (PVM : Pvm.S) = struct + module Daemon = Daemon.Make (PVM) + module Components = Daemon.Components + + let default_constants = + let constants = Default_parameters.constants_test in + let sc_rollup = + { + constants.sc_rollup with + arith_pvm_enable = true; + challenge_window_in_blocks = 4032; + commitment_period_in_blocks = 3; + } + in + {constants with sc_rollup} + + let add_l2_genesis_block (node_ctxt : _ Node_context.t) ~boot_sector = + let open Lwt_result_syntax in + let head = + Layer1. + { + hash = Block_hash.zero; + level = Raw_level.to_int32 node_ctxt.genesis_info.level; + } + in + let* () = Node_context.save_level node_ctxt head in + let predecessor = head in + let predecessor_timestamp = Time.Protocol.epoch in + let inbox = + Sc_rollup.Inbox.genesis + ~predecessor_timestamp + ~predecessor:predecessor.hash + node_ctxt.genesis_info.level + in + let* inbox_hash = Node_context.save_inbox node_ctxt inbox in + let inbox_witness = Sc_rollup.Inbox.current_witness inbox in + let ctxt = Context.empty node_ctxt.context in + let num_ticks = 0L in + let initial_tick = Sc_rollup.Tick.initial in + let*! initial_state = PVM.initial_state ~empty:(PVM.State.empty ()) in + let*! state = PVM.install_boot_sector initial_state boot_sector in + let*! genesis_state_hash = PVM.state_hash state in + let*! ctxt = PVM.State.set ctxt state in + let*! context_hash = Context.commit ctxt in + let commitment = + Sc_rollup.Commitment.genesis_commitment + ~origination_level:node_ctxt.genesis_info.level + ~genesis_state_hash + in + let* commitment_hash = Node_context.save_commitment node_ctxt commitment in + let previous_commitment_hash = node_ctxt.genesis_info.commitment_hash in + let header = + Sc_rollup_block. + { + block_hash = head.hash; + level = node_ctxt.genesis_info.level; + predecessor = predecessor.hash; + commitment_hash = Some commitment_hash; + previous_commitment_hash; + context = context_hash; + inbox_witness; + inbox_hash; + } + in + let l2_block = + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} + in + let* () = Node_context.save_l2_head node_ctxt l2_block in + return l2_block + + let initialize_node_context ?(constants = default_constants) kind ~boot_sector + = + let open Lwt_result_syntax in + incr uid ; + (* To avoid any conflict with previous runs of this test. *) + let pid = Unix.getpid () in + let data_dir = + Filename.(concat @@ get_temp_dir_name ()) + (Format.sprintf "sc-rollup-node-test-%d-%d" pid !uid) + in + let base_dir = + Filename.(concat @@ get_temp_dir_name ()) + (Format.sprintf "sc-rollup-node-test-base-%d-%d" pid !uid) + in + let filesystem = String.Hashtbl.create 10 in + let cctxt = + new Protocol_client_context.wrap_full + (new Faked_client_context.unix_faked ~base_dir ~filesystem) + in + let* ctxt = + Node_context.Internal_for_tests.create_node_context + cctxt + ~constants + ~data_dir + kind + in + let* genesis = add_l2_genesis_block ctxt ~boot_sector in + let commitment_hash = + WithExceptions.Option.get ~loc:__LOC__ genesis.header.commitment_hash + in + let ctxt = + {ctxt with genesis_info = {ctxt.genesis_info with commitment_hash}} + in + return (ctxt, genesis) + + let with_node_context ?constants kind ~boot_sector f = + let open Lwt_result_syntax in + let* node_ctxt, genesis = + initialize_node_context ?constants kind ~boot_sector + in + Lwt.finalize (fun () -> f node_ctxt ~genesis) @@ fun () -> + let open Lwt_syntax in + let* _ = Node_context.close node_ctxt in + return_unit + + let append_l2_block (node_ctxt : _ Node_context.t) + ?(is_migration_block = false) messages = + let open Lwt_result_syntax in + let* predecessor_l2_block = + Node_context.last_processed_head_opt node_ctxt + in + let* predecessor_l2_block = + match predecessor_l2_block with + | Some b -> return b + | None -> + failwith "No genesis block, please add one with add_l2_genesis_block" + in + let level = + Raw_level.(to_int32 @@ succ predecessor_l2_block.header.level) + in + let hash = block_hash_of_level level in + let predecessor = + Layer1. + { + hash = predecessor_l2_block.header.block_hash; + level = Raw_level.to_int32 predecessor_l2_block.header.level; + } + in + let head = Layer1.{hash; level} in + let predecessor_timestamp = + Time.Protocol.of_seconds (Int64.of_int32 level) + in + Daemon.Internal_for_tests.process_messages + node_ctxt + ~is_migration_block + ~predecessor + ~predecessor_timestamp + head + messages +end + +let l2_chain_builders = + List.map + (fun kind -> + let module PVM = (val Components.pvm_of_kind kind) in + (kind, (module Make (PVM) : S))) + Sc_rollup.Kind.all + +let l2_chain_builder kind = Stdlib.List.assoc kind l2_chain_builders + +let with_node_context ?constants kind ~boot_sector = + let module L = (val l2_chain_builder kind) in + L.with_node_context ?constants kind ~boot_sector + +let add_l2_genesis_block (node_ctxt : _ Node_context.t) = + let module L = (val l2_chain_builder node_ctxt.kind) in + L.add_l2_genesis_block node_ctxt + +let append_l2_block (node_ctxt : _ Node_context.t) = + let module L = (val l2_chain_builder node_ctxt.kind) in + L.append_l2_block node_ctxt + +let append_l2_blocks node_ctxt message_batches = + List.map_es (append_l2_block node_ctxt) message_batches + +let append_dummy_l2_chain node_ctxt ~length = + let open Lwt_result_syntax in + let* head = Node_context.last_processed_head_opt node_ctxt in + let head_level = + match head with + | None -> 0 + | Some h -> h.header.level |> Raw_level.to_int32 |> Int32.to_int + in + let batches = + Stdlib.List.init length (fun i -> + [ + Sc_rollup.Inbox_message.External + (Z.to_bits (Z.of_int (i + head_level + 1))); + ]) + in + append_l2_blocks node_ctxt batches + +module Assert = struct + module Make_with_encoding (X : sig + type t + + val encoding : t Data_encoding.t + end) = + Assert.Make_equalities (struct + type t = X.t + + let eq (b1 : X.t) (b2 : X.t) = + Bytes.equal + (Data_encoding.Binary.to_bytes_exn X.encoding b1) + (Data_encoding.Binary.to_bytes_exn X.encoding b2) + + let pp ppf (b : X.t) = + Data_encoding.Json.pp ppf (Data_encoding.Json.construct X.encoding b) + end) + + module L2_block = Make_with_encoding (Sc_rollup_block) + module Commitment = Make_with_encoding (Sc_rollup.Commitment) + module Commitment_hash = Make_with_encoding (Sc_rollup.Commitment.Hash) + module State_hash = Make_with_encoding (Sc_rollup.State_hash) +end + +let alcotest name speed ?constants kind ~boot_sector f = + Alcotest_lwt.test_case name speed @@ fun _lwt_switch () -> + let open Lwt_result_syntax in + let*! r = with_node_context ?constants kind ~boot_sector f in + match r with + | Ok () -> Lwt.return_unit + | Error err -> + Format.printf "@\n%a@." pp_print_trace err ; + Lwt.fail Alcotest.Test_error diff --git a/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.mli b/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.mli new file mode 100644 index 000000000000..252b6b9eaf91 --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.mli @@ -0,0 +1,116 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, . *) +(* *) +(* 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 Alpha_context + +(** {1 Helper functions to build and run unit tests for the rollup node} *) + +(** {2 Creating Node Contexts} *) + +(** [with_node_context ?constants kind ~boot_sector f] creates a node context + and (with a store, a context, etc.) where protocol [constants] can be + specified, and runs [f] with this node context. The L2 chain is initialized + with a genesis block and the PVM with the [boot_sector]. When [f] terminates + or fails, the created node context is closed properly. Test that need a node + context need to use this function in order to avoid file descriptor + leaks. *) +val with_node_context : + ?constants:Constants.Parametric.t -> + Sc_rollup.Kind.t -> + boot_sector:string -> + ([`Read | `Write] Node_context.t -> + genesis:Sc_rollup_block.t -> + 'a tzresult Lwt.t) -> + 'a tzresult Lwt.t + +(** {2 Building L2 Chains} *) + +(** Create and add a genesis block for the L2 chain. The [boot_sector] for the + rollup/kernel needs to be provided. The newly created L2 block is + returned. *) +val add_l2_genesis_block : + [`Read | `Write] Node_context.t -> + boot_sector:string -> + Sc_rollup_block.t tzresult Lwt.t + +(** [append_l2_block node_ctxt ?is_migration_block messages] creates and append + an L2 block containing the [messages] given in argument. The block is added + on top of the last L2 block in the chain (i.e. the head known by the node), + and is returned. *) +val append_l2_block : + [`Read | `Write] Node_context.t -> + ?is_migration_block:bool -> + Sc_rollup.Inbox_message.t list -> + Sc_rollup_block.t tzresult Lwt.t + +(** [append_l2_block node_ctxt message_batches] appends as many blocks as there + are batches in [message_batches]. Each block contain a batch of + messages. The portion of the chain that was added is returned. *) +val append_l2_blocks : + [`Read | `Write] Node_context.t -> + Sc_rollup.Inbox_message.t list list -> + Sc_rollup_block.t list tzresult Lwt.t + +(** [append_dummy_l2_chain node_ctxt ~length] append [length] L2 blocks with an + arbitrary content to the chain. The portion of the chain that was added is + returned. This function is useful for quickly building long(er) L2 chains + for the tests. *) +val append_dummy_l2_chain : + [`Read | `Write] Node_context.t -> + length:int -> + Sc_rollup_block.t list tzresult Lwt.t + +(** {2 Assertions} *) + +module Assert : sig + (** Assertions on L2 blocks *) + module L2_block : Assert.EQUALITIES with type t = Sc_rollup_block.t + + (** Assertions on commitments *) + module Commitment : Assert.EQUALITIES with type t = Sc_rollup.Commitment.t + + (** Assertions on commitment hashes *) + module Commitment_hash : + Assert.EQUALITIES with type t = Sc_rollup.Commitment.Hash.t + + (** Assertions on PVM state hashes *) + module State_hash : Assert.EQUALITIES with type t = Sc_rollup.State_hash.t +end + +(** {2 Building and Running tests} *) + +(** Build an alcotest test case that executes with node context initialized with + a genesis block with the provided [boot_sector]. *) +val alcotest : + string -> + Alcotest.speed_level -> + ?constants:Constants.Parametric.t -> + Sc_rollup.Kind.t -> + boot_sector:string -> + ([`Read | `Write] Node_context.t -> + genesis:Sc_rollup_block.t -> + unit tzresult Lwt.t) -> + unit Alcotest_lwt.test_case diff --git a/src/proto_alpha/lib_sc_rollup_node/test/main.ml b/src/proto_alpha/lib_sc_rollup_node/test/main.ml deleted file mode 100644 index 65b7100bdc11..000000000000 --- a/src/proto_alpha/lib_sc_rollup_node/test/main.ml +++ /dev/null @@ -1,33 +0,0 @@ -(*****************************************************************************) -(* *) -(* Open Source License *) -(* Copyright (c) Nomadic Labs, . *) -(* *) -(* 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. *) -(* *) -(*****************************************************************************) - -(** Testing - ------- - Component: Smart rollup node library - Invocation: dune exec src/proto_alpha/lib_sc_rollup_node/test/main.exe - Subject: Unit tests for the smart rollup node library - *) - -let () = () -- GitLab From 0923b4185beae31e1dcc38df2b76e1d62879a4ee Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Thu, 23 Feb 2023 21:53:56 +0100 Subject: [PATCH 10/12] Test: canary tests for the helper functions --- .../lib_sc_rollup_node/test/canary.ml | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/proto_alpha/lib_sc_rollup_node/test/canary.ml diff --git a/src/proto_alpha/lib_sc_rollup_node/test/canary.ml b/src/proto_alpha/lib_sc_rollup_node/test/canary.ml new file mode 100644 index 000000000000..168ce5c8e701 --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/test/canary.ml @@ -0,0 +1,102 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, . *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Smart rollup node library + Invocation: dune exec src/proto_alpha/lib_sc_rollup_node/test/main.exe \ + -- -f canary + Subject: Canary unit tests to make sure the test helpers work as intended +*) + +open Protocol.Alpha_context + +let build_chain node_ctxt ~genesis ~length = + let open Lwt_result_syntax in + let* blocks = Helpers.append_dummy_l2_chain node_ctxt ~length in + return (genesis :: blocks) + +let canary_test node_ctxt ~genesis = + let open Lwt_result_syntax in + let length = 100 in + let* chain = build_chain node_ctxt ~genesis ~length in + Assert.Int.equal + ~loc:__LOC__ + ~msg:"chain_length_ok" + (List.length chain) + (length + 1) ; + (* Checking that the chain matches what is stored *) + let* () = + List.iter_es + (fun (block : Sc_rollup_block.t) -> + let* store_block_by_hash = + Node_context.get_l2_block node_ctxt block.header.block_hash + in + let* store_block_by_level = + Node_context.get_l2_block_by_level + node_ctxt + (Raw_level.to_int32 block.header.level) + in + Helpers.Assert.L2_block.equal + ~loc:__LOC__ + ~msg:"stored_block_by_hash_ok" + store_block_by_hash + block ; + Helpers.Assert.L2_block.equal + ~loc:__LOC__ + ~msg:"stored_block_by_level_ok" + store_block_by_level + block ; + return_unit) + chain + in + let* head = Node_context.last_processed_head_opt node_ctxt in + let last = List.last_opt chain in + Helpers.Assert.L2_block.Option.equal + ~loc:__LOC__ + ~msg:"head_is_last_block" + head + last ; + return_unit + +let tests = + [ + Helpers.alcotest + "canary arith" + `Quick + Sc_rollup.Kind.Example_arith + ~boot_sector:"" + canary_test; + Helpers.alcotest + "canary wasm" + `Quick + Sc_rollup.Kind.Wasm_2_0_0 + ~boot_sector:"" + canary_test; + ] + +let () = + Alcotest_lwt.run "canary" [(Protocol.name ^ ": canary", tests)] + |> Lwt_main.run -- GitLab From 80dab4406dfab8561bfe908527398893d8da4b0e Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 28 Mar 2023 17:17:15 +0200 Subject: [PATCH 11/12] Manifest: enable rollup node tests for Mumbai --- manifest/main.ml | 2 +- opam/tezos-sc-rollup-node-test.opam | 10 ++-- .../lib_sc_rollup_node/test/dune | 49 +++++++++++++++++++ .../lib_sc_rollup_node/test/helpers/dune | 30 ++++++++++++ tezt/tests/dune | 1 + 5 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 src/proto_016_PtMumbai/lib_sc_rollup_node/test/dune create mode 100644 src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/dune diff --git a/manifest/main.ml b/manifest/main.ml index 4dae738c264b..ab8288be45db 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -5288,7 +5288,7 @@ module Protocol = Protocol ] in let _octez_sc_rollup_node_test = - only_if N.(number >= 017) @@ fun () -> + only_if N.(number >= 016) @@ fun () -> let helpers = private_lib (sf "octez_smart_rollup_node_%s_test_helpers" short_hash) diff --git a/opam/tezos-sc-rollup-node-test.opam b/opam/tezos-sc-rollup-node-test.opam index eeeea892b333..e94634bce320 100644 --- a/opam/tezos-sc-rollup-node-test.opam +++ b/opam/tezos-sc-rollup-node-test.opam @@ -11,15 +11,19 @@ depends: [ "dune" { >= "3.0" } "ocaml" { >= "4.14" } "tezos-base" - "tezos-protocol-alpha" - "tezos-client-alpha" + "tezos-protocol-016-PtMumbai" + "tezos-client-016-PtMumbai" "tezos-test-helpers" "qcheck-alcotest" { >= "0.20" } "qcheck-core" "logs" + "tezos-smart-rollup-layer2-016-PtMumbai" + "octez-smart-rollup-node-PtMumbai" + "octez-alcotezt" + "tezos-protocol-alpha" + "tezos-client-alpha" "tezos-smart-rollup-layer2-alpha" "octez-smart-rollup-node-alpha" - "octez-alcotezt" "tezt" { with-test & >= "3.0.0" } ] build: [ diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/dune b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/dune new file mode 100644 index 000000000000..a8cd5803af88 --- /dev/null +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/dune @@ -0,0 +1,49 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name src_proto_016_PtMumbai_lib_sc_rollup_node_test_tezt_lib) + (instrumentation (backend bisect_ppx)) + (libraries + tezt.core + tezos-base + tezos-protocol-016-PtMumbai + tezos-test-helpers + tezos-smart-rollup-layer2-016-PtMumbai + octez_smart_rollup_node_PtMumbai + octez-alcotezt + octez_smart_rollup_node_PtMumbai_test_helpers) + (library_flags (:standard -linkall)) + (flags + (:standard) + -open Tezt_core + -open Tezt_core.Base + -open Tezos_base.TzPervasives + -open Tezos_base.TzPervasives.Error_monad.Legacy_monad_globals + -open Tezos_protocol_016_PtMumbai + -open Tezos_test_helpers + -open Tezos_smart_rollup_layer2_016_PtMumbai + -open Octez_smart_rollup_node_PtMumbai + -open Octez_alcotezt + -open Octez_smart_rollup_node_PtMumbai_test_helpers) + (modules canary)) + +(executable + (name main) + (instrumentation (backend bisect_ppx --bisect-sigterm)) + (libraries + src_proto_016_PtMumbai_lib_sc_rollup_node_test_tezt_lib + tezt) + (link_flags + (:standard) + (:include %{workspace_root}/macos-link-flags.sexp)) + (modules main)) + +(rule + (alias runtezt) + (package tezos-sc-rollup-node-test) + (action (run %{dep:./main.exe}))) + +(rule + (targets main.ml) + (action (with-stdout-to %{targets} (echo "let () = Tezt.Test.run ()")))) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/dune b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/dune new file mode 100644 index 000000000000..66a1eb2e0087 --- /dev/null +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/dune @@ -0,0 +1,30 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name octez_smart_rollup_node_PtMumbai_test_helpers) + (package tezos-sc-rollup-node-test) + (instrumentation (backend bisect_ppx)) + (libraries + tezos-base + tezos-protocol-016-PtMumbai + tezos-protocol-016-PtMumbai.parameters + tezos-client-016-PtMumbai + tezos-test-helpers + qcheck-alcotest + qcheck-core + logs.lwt + tezos-smart-rollup-layer2-016-PtMumbai + octez_smart_rollup_node_PtMumbai + octez-alcotezt) + (flags + (:standard) + -open Tezos_base.TzPervasives + -open Tezos_base.TzPervasives.Error_monad.Legacy_monad_globals + -open Tezos_protocol_016_PtMumbai + -open Tezos_protocol_016_PtMumbai_parameters + -open Tezos_client_016_PtMumbai + -open Tezos_test_helpers + -open Tezos_smart_rollup_layer2_016_PtMumbai + -open Octez_smart_rollup_node_PtMumbai + -open Octez_alcotezt)) diff --git a/tezt/tests/dune b/tezt/tests/dune index d3384d116e5b..e503505f9839 100644 --- a/tezt/tests/dune +++ b/tezt/tests/dune @@ -31,6 +31,7 @@ src_proto_alpha_lib_dal_test_tezt_lib src_proto_alpha_lib_dac_plugin_test_tezt_lib src_proto_alpha_lib_client_test_tezt_lib + src_proto_016_PtMumbai_lib_sc_rollup_node_test_tezt_lib src_proto_016_PtMumbai_lib_protocol_test_unit_tezt_lib src_proto_016_PtMumbai_lib_protocol_test_regression_tezt_lib src_proto_016_PtMumbai_lib_protocol_test_pbt_tezt_lib -- GitLab From d689412d820b16c7f1408b548e74a53d1c3f0700 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 24 Feb 2023 10:56:26 +0100 Subject: [PATCH 12/12] SCORU/Node/Tests: backport !7854 to mumbai rollup node - SCORU/Node: allow multiple instances of commitment and batcher workers - SCORU/Node: one directory per RPC server instance - SCORU/Node: expose internal functions for unit tests - SCORU/Node: retrieve genesis inbox from disk first before RPC - Test: helpers for smart rollup node unit tests - Test: canary tests for the helper functions --- .../lib_sc_rollup_node/RPC_server.ml | 174 +++++------ .../lib_sc_rollup_node/batcher.ml | 27 ++ .../lib_sc_rollup_node/batcher_events.ml | 2 +- .../batcher_worker_types.ml | 13 - .../batcher_worker_types.mli | 2 - .../lib_sc_rollup_node/commitment.ml | 28 ++ .../lib_sc_rollup_node/commitment_event.ml | 4 +- .../lib_sc_rollup_node/commitment_event.mli | 3 + .../lib_sc_rollup_node/daemon.ml | 61 ++++ .../lib_sc_rollup_node/daemon_event.ml | 2 +- .../lib_sc_rollup_node/dal_pages_request.ml | 4 +- .../dal_slots_tracker_event.ml | 2 +- .../lib_sc_rollup_node/event.ml | 2 +- .../lib_sc_rollup_node/inbox.ml | 107 ++++--- .../lib_sc_rollup_node/inbox.mli | 18 ++ .../lib_sc_rollup_node/inbox_event.ml | 2 +- .../lib_sc_rollup_node/injector.ml | 2 +- .../lib_sc_rollup_node/interpreter.ml | 18 +- .../lib_sc_rollup_node/interpreter_event.ml | 2 +- .../lib_sc_rollup_node/layer1.ml | 2 +- .../lib_sc_rollup_node/layer1_event.ml | 2 +- .../lib_sc_rollup_node/node_context.ml | 54 ++++ .../lib_sc_rollup_node/node_context.mli | 14 + .../publisher_worker_types.ml | 13 - .../publisher_worker_types.mli | 2 - .../refutation_game_event.ml | 3 +- .../lib_sc_rollup_node/reveals.ml | 6 +- .../sc_rollup_node_errors.ml | 4 + .../lib_sc_rollup_node/test/canary.ml | 102 +++++++ .../test/helpers/faked_client_context.ml | 172 +++++++++++ .../test/helpers/helpers.ml | 283 ++++++++++++++++++ .../test/helpers/helpers.mli | 115 +++++++ 32 files changed, 1056 insertions(+), 189 deletions(-) create mode 100644 src/proto_016_PtMumbai/lib_sc_rollup_node/test/canary.ml create mode 100644 src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/faked_client_context.ml create mode 100644 src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml create mode 100644 src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.mli diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.ml index 5912cfecb54a..a72a534334a1 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.ml @@ -95,109 +95,111 @@ let get_dal_slot_page node_ctxt block slot_index slot_page = | None -> assert false | Some _contents -> return ("Slot page is available", contents_opt)) -module Global_directory = Make_directory (struct - include Sc_rollup_services.Global - - type context = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Proof_helpers_directory = Make_directory (struct - include Sc_rollup_services.Global.Helpers - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type context = Node_context.rw - - let context_of_prefix node_ctxt () = return node_ctxt -end) - -module Local_directory = Make_directory (struct - include Sc_rollup_services.Local - - type context = Node_context.ro +module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct + module PVM = Simulation.PVM + module Interpreter = Simulation.Interpreter + module Outbox = Outbox.Make (PVM) + module Free_pvm = Interpreter.Free_pvm - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) + module Global_directory = Make_directory (struct + include Sc_rollup_services.Global -module Block_directory = Make_directory (struct - include Sc_rollup_services.Global.Block + type context = Node_context.ro - type context = Node_context.ro * Block_hash.t + let context_of_prefix node_ctxt () = + return (Node_context.readonly node_ctxt) + end) - let context_of_prefix node_ctxt (((), block) : prefix) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block) -end) + module Proof_helpers_directory = Make_directory (struct + include Sc_rollup_services.Global.Helpers -module Outbox_directory = Make_directory (struct - include Sc_rollup_services.Global.Block.Outbox + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type context = Node_context.rw - type context = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t + let context_of_prefix node_ctxt () = return node_ctxt + end) - let context_of_prefix node_ctxt (((), block), level) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block, level) -end) + module Local_directory = Make_directory (struct + include Sc_rollup_services.Local -module Common = struct - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.block - @@ fun (node_ctxt, block) () () -> - Node_context.get_full_l2_block node_ctxt block + type context = Node_context.ro - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.num_messages - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* l2_block = Node_context.get_l2_block node_ctxt block in - let+ num_messages = - Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness - in - Z.of_int num_messages + let context_of_prefix node_ctxt () = + return (Node_context.readonly node_ctxt) + end) - let () = - Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address - @@ fun node_ctxt () () -> return @@ node_ctxt.rollup_address + module Block_directory = Make_directory (struct + include Sc_rollup_services.Global.Block - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_head - @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt + type context = Node_context.ro * Block_hash.t - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_level - @@ fun node_ctxt () () -> get_head_level_opt node_ctxt + let context_of_prefix node_ctxt (((), block) : prefix) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block) + end) - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.hash - @@ fun (_node_ctxt, block) () () -> return block + module Outbox_directory = Make_directory (struct + include Sc_rollup_services.Global.Block.Outbox - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.level - @@ fun (node_ctxt, block) () () -> - Node_context.level_of_hash node_ctxt block + type context = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.inbox - @@ fun (node_ctxt, block) () () -> - Node_context.get_inbox_by_block_hash node_ctxt block + let context_of_prefix node_ctxt (((), block), level) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block, level) + end) - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ l2_block = Node_context.get_l2_block node_ctxt block in - Z.of_int64 l2_block.num_ticks -end + module Common = struct + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.block + @@ fun (node_ctxt, block) () () -> + Node_context.get_full_l2_block node_ctxt block -module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct - module PVM = Simulation.PVM - module Interpreter = Simulation.Interpreter - module Outbox = Outbox.Make (PVM) - module Free_pvm = Interpreter.Free_pvm + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.num_messages + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* l2_block = Node_context.get_l2_block node_ctxt block in + let+ num_messages = + Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness + in + Z.of_int num_messages + + let () = + Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address + @@ fun node_ctxt () () -> return @@ node_ctxt.rollup_address + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_head + @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_level + @@ fun node_ctxt () () -> get_head_level_opt node_ctxt + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.hash + @@ fun (_node_ctxt, block) () () -> return block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.level + @@ fun (node_ctxt, block) () () -> + Node_context.level_of_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.inbox + @@ fun (node_ctxt, block) () () -> + Node_context.get_inbox_by_block_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ l2_block = Node_context.get_l2_block node_ctxt block in + Z.of_int64 l2_block.num_ticks + end let get_state (node_ctxt : _ Node_context.t) block_hash = let open Lwt_result_syntax in diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher.ml index 84be3b4fa94e..d45abd021e60 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher.ml @@ -34,6 +34,10 @@ end module Batched_messages = Hash_queue.Make (L2_message.Hash) (L2_batched_message) +(* Count instances of the batcher functor to allow for multiple worker events + without conflicts. *) +let instances_count = ref 0 + module type S = sig type status = Pending_batch | Batched of Injector.Inj_operation.hash @@ -61,6 +65,8 @@ module type S = sig end module Make (Simulation : Simulation.S) : S = struct + let () = incr instances_count + module PVM = Simulation.PVM type status = Pending_batch | Batched of Injector.Inj_operation.hash @@ -265,6 +271,27 @@ module Make (Simulation : Simulation.S) : S = struct } end + module Name = struct + (* We only have a single batcher in the node *) + type t = unit + + let encoding = Data_encoding.unit + + let base = + (* But we can have multiple instances in the unit tests. This is just to + avoid conflicts in the events declarations. *) + Batcher_events.Worker.section + @ [ + ("worker" + ^ if !instances_count = 1 then "" else string_of_int !instances_count + ); + ] + + let pp _ _ = () + + let equal () () = true + end + module Worker = Worker.MakeSingle (Name) (Request) (Types) type worker = Worker.infinite Worker.queue Worker.t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_events.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_events.ml index 5d0ad969a3c0..da9ad33fa984 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_events.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_events.ml @@ -25,7 +25,7 @@ include Internal_event.Simple -let section = ["sc_rollup_node"; "batcher"] +let section = [Protocol.name; "sc_rollup_node"; "batcher"] let queue = declare_1 diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_worker_types.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_worker_types.ml index 63afd1221a6d..c0fe7350814c 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_worker_types.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_worker_types.ml @@ -75,16 +75,3 @@ module Request = struct level | Batch -> Format.pp_print_string ppf "batch" end - -module Name = struct - (* We only have a single batcher right now *) - type t = unit - - let encoding = Data_encoding.unit - - let base = ["sc_rollup_batcher"] - - let pp _ _ = () - - let equal () () = true -end diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_worker_types.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_worker_types.mli index 681291fbd22f..d3a3fd2c09e9 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_worker_types.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher_worker_types.mli @@ -42,5 +42,3 @@ module Request : sig with type ('a, 'request_error) t := ('a, 'request_error) t and type view := view end - -module Name : Worker_intf.NAME with type t = unit diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment.ml index f1c09fefe9d2..237c288a0ba9 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment.ml @@ -81,7 +81,13 @@ let sc_rollup_challenge_window node_ctxt = let next_commitment_level node_ctxt last_commitment_level = add_level last_commitment_level (sc_rollup_commitment_period node_ctxt) +(* Count instances of the commitment functor to allow for multiple worker events + without conflicts. *) +let instances_count = ref 0 + module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct + let () = incr instances_count + module PVM = PVM type state = Node_context.ro @@ -406,6 +412,28 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct type parameters = {node_ctxt : Node_context.ro} end + module Name = struct + (* We only have a single committer in the node *) + type t = unit + + let encoding = Data_encoding.unit + + let base = + (* But we can have multiple instances in the unit tests. This is just to + avoid conflicts in the events declarations. *) + Commitment_event.section + @ [ + ("publisher" + ^ + if !instances_count = 1 then "" else string_of_int !instances_count + ); + ] + + let pp _ _ = () + + let equal () () = true + end + module Worker = Worker.MakeSingle (Name) (Request) (Types) type worker = Worker.infinite Worker.queue Worker.t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_event.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_event.ml index 7f9135d2792a..e5d62ef4337c 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_event.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_event.ml @@ -30,7 +30,7 @@ open Publisher_worker_types module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "commitment"] + let section = [Protocol.name; "sc_rollup_node"; "commitment"] let starting = declare_0 @@ -157,6 +157,8 @@ let stopping = Simple.(emit stopping) open Sc_rollup.Commitment +let section = Simple.section + let emit_commitment_event f commitment_hash {predecessor; inbox_level; compressed_state; number_of_ticks} = Simple.( diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_event.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_event.mli index 8f4baf9b0e14..6f583d61cc72 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_event.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_event.mli @@ -33,6 +33,9 @@ val starting : unit -> unit Lwt.t val stopping : unit -> unit Lwt.t +(** Section for commitment events. *) +val section : string list + (** [commitment_stored commitment_hash commitment] emits the event that the [commitment] was stored. *) val commitment_stored : diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml index 9f067c83c4a4..e52d3e6bed11 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml @@ -572,6 +572,67 @@ module Make (PVM : Pvm.S) = struct Format.eprintf "%!%a@.Exiting.@." pp_print_trace e ; let*! _ = Lwt_exit.exit_and_wait 1 in return_unit) + + module Internal_for_tests = struct + (** Same as {!process_head} but only builds and stores the L2 block + corresponding to [messages]. It is used by the unit tests to build an L2 + chain. *) + let process_messages (node_ctxt : _ Node_context.t) ~predecessor + ~predecessor_timestamp head messages = + let open Lwt_result_syntax in + let* () = Node_context.save_level node_ctxt head in + let* inbox_hash, inbox, inbox_witness, messages, ctxt = + Inbox.Internal_for_tests.process_messages + node_ctxt + ~predecessor + ~predecessor_timestamp + ~level:head.level + messages + in + let* ctxt, _num_messages, num_ticks, initial_tick = + Components.Interpreter.process_head + node_ctxt + ctxt + ~predecessor + head + (inbox, messages) + in + let*! context_hash = Context.commit ctxt in + let* commitment_hash = + Components.Commitment.process_head + node_ctxt + ~predecessor:predecessor.Layer1.hash + head + ctxt + in + let level = Raw_level.of_int32_exn head.level in + let* previous_commitment_hash = + if level = node_ctxt.genesis_info.Sc_rollup.Commitment.level then + (* Previous commitment for rollup genesis is itself. *) + return node_ctxt.genesis_info.Sc_rollup.Commitment.commitment_hash + else + let+ pred = Node_context.get_l2_block node_ctxt predecessor.hash in + Sc_rollup_block.most_recent_commitment pred.header + in + let header = + Sc_rollup_block. + { + block_hash = head.hash; + level; + predecessor = predecessor.hash; + commitment_hash; + previous_commitment_hash; + context = context_hash; + inbox_witness; + inbox_hash; + } + in + let l2_block = + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} + in + let* () = Node_context.save_l2_head node_ctxt l2_block in + return l2_block + end end let run ~data_dir (configuration : Configuration.t) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon_event.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon_event.ml index cf8b076f59f2..73d701430da0 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon_event.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon_event.ml @@ -26,7 +26,7 @@ module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "daemon"] + let section = [Protocol.name; "sc_rollup_node"; "daemon"] let head_processing = declare_3 diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_pages_request.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_pages_request.ml index 03d79c17ef34..0e0e11aae1ec 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_pages_request.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_pages_request.ml @@ -38,7 +38,7 @@ type error += | Dal_invalid_page_for_slot of Dal.Page.t let () = - register_error_kind + Sc_rollup_node_errors.register_error_kind `Permanent ~id:"dal_pages_request.dal_slot_not_found_in_store" ~title:"Dal slot not found in store" @@ -48,7 +48,7 @@ let () = Data_encoding.(obj1 (req "slot_id" Dal.Slot.Header.id_encoding)) (function Dal_slot_not_found_in_store slot_id -> Some slot_id | _ -> None) (fun slot_id -> Dal_slot_not_found_in_store slot_id) ; - register_error_kind + Sc_rollup_node_errors.register_error_kind `Permanent ~id:"dal_pages_request.dal_invalid_page_for_slot" ~title:"Invalid Dal page requested for slot" diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_slots_tracker_event.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_slots_tracker_event.ml index 84ed4506c47f..05620638edb9 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_slots_tracker_event.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_slots_tracker_event.ml @@ -29,7 +29,7 @@ open Alpha_context module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "dal_slots_tracker"] + let section = [Protocol.name; "sc_rollup_node"; "dal_slots_tracker"] let slot_has_been_confirmed = declare_3 diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/event.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/event.ml index ce5d693db33d..ea2fa9aaa35e 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/event.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/event.ml @@ -26,7 +26,7 @@ module Simple = struct include Internal_event.Simple - let section = ["smart_rollup_node"] + let section = [Protocol.name; "smart_rollup_node"] let starting_node = declare_0 diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml index e788af5f0d55..be94f89f5e69 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml @@ -120,6 +120,46 @@ let add_messages ~predecessor_timestamp ~predecessor inbox messages = inbox, messages_with_protocol_internal_messages ) +let process_messages (node_ctxt : _ Node_context.t) ~predecessor + ~predecessor_timestamp ~level messages = + let open Lwt_result_syntax in + let* inbox = Node_context.inbox_of_head node_ctxt predecessor in + let inbox_metrics = Metrics.Inbox.metrics in + Prometheus.Gauge.set inbox_metrics.head_inbox_level @@ Int32.to_float level ; + let*? level = Environment.wrap_tzresult @@ Raw_level.of_int32 level in + let* ctxt = + if Raw_level.(level <= node_ctxt.Node_context.genesis_info.level) then + (* This is before we have interpreted the boot sector, so we start + with an empty context in genesis *) + return (Context.empty node_ctxt.context) + else Node_context.checkout_context node_ctxt predecessor.hash + in + let* ( _messages_history, + witness_hash, + inbox, + messages_with_protocol_internal_messages ) = + add_messages + ~predecessor_timestamp + ~predecessor:predecessor.hash + inbox + messages + in + Metrics.Inbox.Stats.head_messages_list := + messages_with_protocol_internal_messages ; + let* () = + Node_context.save_messages + node_ctxt + witness_hash + {predecessor = predecessor.hash; predecessor_timestamp; messages} + in + let* inbox_hash = Node_context.save_inbox node_ctxt inbox in + return + ( inbox_hash, + inbox, + witness_hash, + messages_with_protocol_internal_messages, + ctxt ) + let process_head (node_ctxt : _ Node_context.t) ~predecessor Layer1.{level; hash = head_hash} = let open Lwt_result_syntax in @@ -127,63 +167,30 @@ let process_head (node_ctxt : _ Node_context.t) ~predecessor Raw_level.to_int32 node_ctxt.genesis_info.level |> Int32.succ in if level >= first_inbox_level then ( - (* - - We compute the inbox of this block using the inbox of its - predecessor. That way, the computation of inboxes is robust - to chain reorganization. - - *) - let* inbox = Node_context.inbox_of_head node_ctxt predecessor in - let inbox_metrics = Metrics.Inbox.metrics in - Prometheus.Gauge.set inbox_metrics.head_inbox_level @@ Int32.to_float level ; - let*? level = Environment.wrap_tzresult @@ Raw_level.of_int32 level in - let* ctxt = - if Raw_level.(level <= node_ctxt.Node_context.genesis_info.level) then - (* This is before we have interpreted the boot sector, so we start - with an empty context in genesis *) - return (Context.empty node_ctxt.context) - else Node_context.checkout_context node_ctxt predecessor.hash - in + (* We compute the inbox of this block using the inbox of its + predecessor. That way, the computation of inboxes is robust to chain + reorganization. *) let* collected_messages, predecessor_timestamp, predecessor_hash = get_messages node_ctxt head_hash in + assert (Block_hash.(predecessor.Layer1.hash = predecessor_hash)) ; let*! () = - Inbox_event.get_messages - head_hash - (Raw_level.to_int32 level) - (List.length collected_messages) + Inbox_event.get_messages head_hash level (List.length collected_messages) in - let* ( _messages_history, - witness_hash, - inbox, - messages_with_protocol_internal_messages ) = - add_messages + let* (( _inbox_hash, + inbox, + _witness_hash, + _messages_with_protocol_internal_messages, + _ctxt ) as res) = + process_messages + node_ctxt + ~predecessor ~predecessor_timestamp - ~predecessor:predecessor_hash - inbox + ~level collected_messages in - Metrics.Inbox.Stats.head_messages_list := - messages_with_protocol_internal_messages ; - let* () = - Node_context.save_messages - node_ctxt - witness_hash - { - predecessor = predecessor_hash; - predecessor_timestamp; - messages = collected_messages; - } - in let* () = same_inbox_as_layer_1 node_ctxt head_hash inbox in - let* inbox_hash = Node_context.save_inbox node_ctxt inbox in - return - ( inbox_hash, - inbox, - witness_hash, - messages_with_protocol_internal_messages, - ctxt )) + return res) else let* inbox = Node_context.genesis_inbox node_ctxt in return @@ -218,3 +225,7 @@ let payloads_history_of_messages ~predecessor ~predecessor_timestamp messages = messages in payloads_history + +module Internal_for_tests = struct + let process_messages = process_messages +end diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli index b078741d65da..3fd50275e5d7 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli @@ -78,3 +78,21 @@ val payloads_history_of_messages : predecessor_timestamp:Timestamp.time -> Sc_rollup.Inbox_message.t list -> Sc_rollup.Inbox_merkelized_payload_hashes.History.t tzresult + +(**/**) + +module Internal_for_tests : sig + val process_messages : + Node_context.rw -> + predecessor:Layer1.head -> + predecessor_timestamp:Timestamp.time -> + level:int32 -> + Sc_rollup.Inbox_message.t list -> + (Sc_rollup.Inbox.Hash.t + * Sc_rollup.Inbox.t + * Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t + * Sc_rollup.Inbox_message.t list + * Context.rw) + tzresult + Lwt.t +end diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox_event.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox_event.ml index 18d83fc56dcd..ed732319c4dc 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox_event.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox_event.ml @@ -26,7 +26,7 @@ module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "inbox"] + let section = [Protocol.name; "sc_rollup_node"; "inbox"] let starting = declare_0 diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/injector.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/injector.ml index 3f4a3648e8fd..1431139541d5 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/injector.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/injector.ml @@ -37,7 +37,7 @@ module Parameters : and type Operation.t = L1_operation.t = struct type state = Node_context.ro - let events_section = ["sc_rollup.injector"] + let events_section = [Protocol.name; "sc_rollup_node"] module Tag : TAG with type t = Configuration.purpose = struct type t = Configuration.purpose diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml index 41301dd9a88d..47833d8e4257 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml @@ -119,15 +119,15 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct let state_of_head node_ctxt ctxt Layer1.{hash; level} = let open Lwt_result_syntax in - let genesis_level = - Raw_level.to_int32 node_ctxt.Node_context.genesis_info.level - in - if level = genesis_level then genesis_state hash node_ctxt ctxt - else - let*! state = PVM.State.find ctxt in - match state with - | None -> tzfail (Sc_rollup_node_errors.Missing_PVM_state (hash, level)) - | Some state -> return (ctxt, state) + let*! state = PVM.State.find ctxt in + match state with + | None -> + let genesis_level = + Raw_level.to_int32 node_ctxt.Node_context.genesis_info.level + in + if level = genesis_level then genesis_state hash node_ctxt ctxt + else tzfail (Sc_rollup_node_errors.Missing_PVM_state (hash, level)) + | Some state -> return (ctxt, state) (** [transition_pvm node_ctxt predecessor head] runs a PVM at the previous state from block [predecessor] by consuming as many messages diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter_event.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter_event.ml index ffc016b9123b..80fab8ac3274 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter_event.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter_event.ml @@ -28,7 +28,7 @@ open Protocol.Alpha_context.Sc_rollup module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "interpreter"] + let section = [Protocol.name; "sc_rollup_node"; "interpreter"] let transitioned_pvm = declare_4 diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml index 0c515602b8c2..fe5776cb19d1 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml @@ -36,7 +36,7 @@ open Protocol_client_context type error += Cannot_find_block of Block_hash.t let () = - register_error_kind + Sc_rollup_node_errors.register_error_kind ~id:"sc_rollup.node.cannot_find_block" ~title:"Cannot find block from L1" ~description:"A block couldn't be found from the L1 node" diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_event.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_event.ml index 6e9773d48ec2..6a0987632b05 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_event.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_event.ml @@ -26,7 +26,7 @@ module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "layer_1"] + let section = [Protocol.name; "sc_rollup_node"; "layer_1"] let starting = declare_0 diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml index d2692f61778a..857ea7d48258 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml @@ -732,3 +732,57 @@ let find_confirmed_slots_histories {store; _} = let save_confirmed_slots_histories {store; _} = Store.Dal_confirmed_slots_histories.add store.irmin_store + +module Internal_for_tests = struct + let create_node_context cctxt + ?(constants = Default_parameters.constants_mainnet) ~data_dir kind = + let open Lwt_result_syntax in + let l2_blocks_cache_size = Configuration.default_l2_blocks_cache_size in + let protocol_constants = + constants + |> Data_encoding.Binary.to_bytes_exn Constants.Parametric.encoding + |> Data_encoding.Binary.of_bytes_exn Constants_parametric_repr.encoding + |> Constants_repr.all_of_parametric + |> Data_encoding.Binary.to_bytes_exn Constants_repr.encoding + |> Data_encoding.Binary.of_bytes_exn Constants.encoding + in + let* store = + Store.load + Read_write + ~l2_blocks_cache_size + Configuration.(default_storage_dir data_dir) + in + let*! context = + Context.load Read_write (Configuration.default_context_dir data_dir) + in + let genesis_info = + Sc_rollup.Commitment.{level = Raw_level.root; commitment_hash = Hash.zero} + in + let l1_ctxt = Layer1.Internal_for_tests.dummy cctxt in + let lcc = + Reference.new_ + {commitment = Sc_rollup.Commitment.Hash.zero; level = Raw_level.root} + in + let lpc = Reference.new_ None in + return + { + cctxt; + dal_cctxt = None; + data_dir; + l1_ctxt; + rollup_address = Sc_rollup.Address.zero; + mode = Observer; + operators = Configuration.Operator_purpose_map.empty; + genesis_info; + lcc; + lpc; + kind; + injector_retention_period = 0; + block_finality_time = 2; + fee_parameters = Configuration.default_fee_parameters; + protocol_constants; + loser_mode = Loser_mode.no_failures; + store; + context; + } +end diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.mli index 4016ced93b15..03fb62b860c4 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.mli @@ -439,3 +439,17 @@ val find_confirmed_slots_histories : val save_confirmed_slots_histories : rw -> Block_hash.t -> Dal.Slots_history.History_cache.t -> unit tzresult Lwt.t + +(**/**) + +module Internal_for_tests : sig + (** Create a node context which really stores data on disk but does not + connect to any layer 1 node. It is meant to be used in unit tests for the + rollup node functions. *) + val create_node_context : + Protocol_client_context.full -> + ?constants:Constants.Parametric.t -> + data_dir:string -> + Sc_rollup.Kind.t -> + Store_sigs.rw t tzresult Lwt.t +end diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher_worker_types.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher_worker_types.ml index 2ae31036e310..c10e697273e3 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher_worker_types.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher_worker_types.ml @@ -55,16 +55,3 @@ module Request = struct | Publish -> Format.pp_print_string ppf "publish" | Cement -> Format.pp_print_string ppf "cement" end - -module Name = struct - (* We only have a single commitment publisher right now *) - type t = unit - - let encoding = Data_encoding.unit - - let base = ["sc_rollup_commitment_publisher"] - - let pp _ _ = () - - let equal () () = true -end diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher_worker_types.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher_worker_types.mli index ab244e31a486..eed1fdcad468 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher_worker_types.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher_worker_types.mli @@ -38,5 +38,3 @@ module Request : sig with type ('a, 'request_error) t := ('a, 'request_error) t and type view := view end - -module Name : Worker_intf.NAME with type t = unit diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/refutation_game_event.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/refutation_game_event.ml index dc873b95d467..f894e7e199dd 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/refutation_game_event.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/refutation_game_event.ml @@ -31,7 +31,7 @@ open Protocol.Alpha_context module Simple = struct include Internal_event.Simple - let section = ["sc_rollup_node"; "refutation_game"] + let section = [Protocol.name; "sc_rollup_node"; "refutation_game"] let timeout = declare_1 @@ -86,6 +86,7 @@ module Simple = struct let timeout_detected = declare_1 + ~section ~name:"sc_rollup_node_timeout_detected" ~msg: "The rollup node has detected that opponent {other} can be timed out." diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/reveals.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/reveals.ml index a414b7114510..696f4de9d06e 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/reveals.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/reveals.ml @@ -32,7 +32,7 @@ type error += | Could_not_encode_raw_data let () = - register_error_kind + Sc_rollup_node_errors.register_error_kind ~id:"sc_rollup.node.wrong_hash_of_reveal_preimage" ~title:"Hash of reveal preimage is not correct" ~description:"Hash of reveal preimage is not correct." @@ -52,7 +52,7 @@ let () = (function | Wrong_hash {found; expected} -> Some (found, expected) | _ -> None) (fun (found, expected) -> Wrong_hash {found; expected}) ; - register_error_kind + Sc_rollup_node_errors.register_error_kind ~id:"sc_rollup.node.could_not_open_reveal_preimage_file" ~title:"Could not open reveal preimage file" ~description:"Could not open reveal preimage file." @@ -66,7 +66,7 @@ let () = (function | Could_not_open_preimage_file filename -> Some filename | _ -> None) (fun filename -> Could_not_open_preimage_file filename) ; - register_error_kind + Sc_rollup_node_errors.register_error_kind ~id:"sc_rollup.node.could_not_encode_raw_data" ~title:"Could not encode raw data to reveal" ~description:"Could not encode raw data to reveal." diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/sc_rollup_node_errors.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/sc_rollup_node_errors.ml index c2b5d2a39946..fb10fab765cf 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/sc_rollup_node_errors.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/sc_rollup_node_errors.ml @@ -25,6 +25,10 @@ open Protocol.Alpha_context +let make_id id = String.concat "." [Protocol.name; id] + +let register_error_kind ~id = register_error_kind ~id:(make_id id) + type error += | Cannot_produce_proof of Sc_rollup.Inbox.t * Raw_level.t | Missing_mode_operators of {mode : string; missing_operators : string list} diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/canary.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/canary.ml new file mode 100644 index 000000000000..ccbf80edb15f --- /dev/null +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/canary.ml @@ -0,0 +1,102 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, . *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Smart rollup node library + Invocation: dune exec src/proto_016_PtMumbai/lib_sc_rollup_node/test/main.exe \ + -- -f canary + Subject: Canary unit tests to make sure the test helpers work as intended +*) + +open Protocol.Alpha_context + +let build_chain node_ctxt ~genesis ~length = + let open Lwt_result_syntax in + let* blocks = Helpers.append_dummy_l2_chain node_ctxt ~length in + return (genesis :: blocks) + +let canary_test node_ctxt ~genesis = + let open Lwt_result_syntax in + let length = 100 in + let* chain = build_chain node_ctxt ~genesis ~length in + Assert.Int.equal + ~loc:__LOC__ + ~msg:"chain_length_ok" + (List.length chain) + (length + 1) ; + (* Checking that the chain matches what is stored *) + let* () = + List.iter_es + (fun (block : Sc_rollup_block.t) -> + let* store_block_by_hash = + Node_context.get_l2_block node_ctxt block.header.block_hash + in + let* store_block_by_level = + Node_context.get_l2_block_by_level + node_ctxt + (Raw_level.to_int32 block.header.level) + in + Helpers.Assert.L2_block.equal + ~loc:__LOC__ + ~msg:"stored_block_by_hash_ok" + store_block_by_hash + block ; + Helpers.Assert.L2_block.equal + ~loc:__LOC__ + ~msg:"stored_block_by_level_ok" + store_block_by_level + block ; + return_unit) + chain + in + let* head = Node_context.last_processed_head_opt node_ctxt in + let last = List.last_opt chain in + Helpers.Assert.L2_block.Option.equal + ~loc:__LOC__ + ~msg:"head_is_last_block" + head + last ; + return_unit + +let tests = + [ + Helpers.alcotest + "canary arith" + `Quick + Sc_rollup.Kind.Example_arith + ~boot_sector:"" + canary_test; + Helpers.alcotest + "canary wasm" + `Quick + Sc_rollup.Kind.Wasm_2_0_0 + ~boot_sector:"" + canary_test; + ] + +let () = + Alcotest_lwt.run "canary" [(Protocol.name ^ ": canary", tests)] + |> Lwt_main.run diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/faked_client_context.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/faked_client_context.ml new file mode 100644 index 000000000000..fdf0855af827 --- /dev/null +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/faked_client_context.ml @@ -0,0 +1,172 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** This module provides a client contexts that stores information (such as the + wallet, etc.) in memory and that cannot do RPCs but which can be used in + unit tests. + + It is largely taken from the mockup simulator of the baker. + *) + +open Tezos_client_base + +class dummy_prompter : Client_context.prompter = + object + method prompt : type a. (a, string tzresult) Client_context.lwt_format -> a + = + fun _msg -> assert false + + method prompt_password : type a. + (a, Bytes.t tzresult) Client_context.lwt_format -> a = + fun _msg -> assert false + + method multiple_password_retries = false + end + +class faked_ctxt : Tezos_rpc.Context.generic = + let local_ctxt = + Tezos_mockup_proxy.RPC_client.local_ctxt Tezos_rpc.Directory.empty + in + object + method base = local_ctxt#base + + method generic_media_type_call meth ?body uri = + local_ctxt#generic_media_type_call meth ?body uri + + method call_service + : 'm 'p 'q 'i 'o. + (([< Resto.meth] as 'm), unit, 'p, 'q, 'i, 'o) Tezos_rpc.Service.t -> + 'p -> + 'q -> + 'i -> + 'o tzresult Lwt.t = + fun service params query body -> + local_ctxt#call_service service params query body + + method call_streamed_service + : 'm 'p 'q 'i 'o. + (([< Resto.meth] as 'm), unit, 'p, 'q, 'i, 'o) Tezos_rpc.Service.t -> + on_chunk:('o -> unit) -> + on_close:(unit -> unit) -> + 'p -> + 'q -> + 'i -> + (unit -> unit) tzresult Lwt.t = + fun service ~on_chunk ~on_close params query body -> + local_ctxt#call_streamed_service + service + ~on_chunk + ~on_close + params + query + body + end + +class faked_wallet ~base_dir ~filesystem : Client_context.wallet = + object (self) + method load_passwords = None + + method read_file fname = + match String.Hashtbl.find filesystem fname with + | None -> failwith "faked_wallet: cannot read file (%s)" fname + | Some (content, _mtime) -> return content + + method private filename alias_name = + Filename.concat + base_dir + (String.map (function ' ' -> '_' | c -> c) alias_name ^ "s") + + val lock_mutex = Lwt_mutex.create () + + method with_lock : type a. (unit -> a Lwt.t) -> a Lwt.t = + fun f -> Lwt_mutex.with_lock lock_mutex f + + method get_base_dir = base_dir + + method load : type a. + string -> default:a -> a Data_encoding.encoding -> a tzresult Lwt.t = + fun alias_name ~default encoding -> + let filename = self#filename alias_name in + if not (String.Hashtbl.mem filesystem filename) then return default + else + self#read_file filename >>=? fun content -> + let json = (Ezjsonm.from_string content :> Data_encoding.json) in + match Data_encoding.Json.destruct encoding json with + | exception e -> + failwith + "did not understand the %s alias file %s : %s" + alias_name + filename + (Printexc.to_string e) + | data -> return data + + method write : type a. + string -> a -> a Data_encoding.encoding -> unit tzresult Lwt.t = + fun alias_name list encoding -> + let filename = self#filename alias_name in + let json = Data_encoding.Json.construct encoding list in + let str = Ezjsonm.value_to_string (json :> Ezjsonm.value) in + String.Hashtbl.replace + filesystem + filename + (str, Some (Ptime.to_float_s (Ptime_clock.now ()))) ; + return_unit + + method last_modification_time : string -> float option tzresult Lwt.t = + let open Lwt_result_syntax in + fun alias_name -> + let filename = self#filename alias_name in + let file = String.Hashtbl.find_opt filesystem filename in + match file with + | None -> return_none + | Some (_content, mtime) -> return mtime + end + +class faked_io_wallet ~base_dir ~filesystem : Client_context.io_wallet = + let log _channel msg = Logs_lwt.info (fun m -> m "%s" msg) in + object + inherit Client_context.simple_printer log + + inherit dummy_prompter + + inherit faked_wallet ~base_dir ~filesystem + end + +class unix_faked ~base_dir ~filesystem : Client_context.full = + object + inherit faked_io_wallet ~base_dir ~filesystem + + inherit faked_ctxt + + inherit Tezos_client_base_unix.Client_context_unix.unix_ui + + method chain = `Main + + method block = `Head 0 + + method confirmations = None + + method verbose_rpc_error_diagnostics = false + end diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml new file mode 100644 index 000000000000..9fb935f54691 --- /dev/null +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml @@ -0,0 +1,283 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, . *) +(* *) +(* 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 Alpha_context + +let uid = ref 0 + +let block_hash_of_level level = + let s = Z.of_int32 level |> Z.to_bits in + let len = String.length s in + let s = + String.init Block_hash.size (fun i -> if i >= len then '\000' else s.[i]) + in + Block_hash.of_string_exn s + +module type S = sig + val with_node_context : + ?constants:Constants.Parametric.t -> + Sc_rollup.Kind.t -> + boot_sector:string -> + ([`Read | `Write] Node_context.t -> + genesis:Sc_rollup_block.t -> + 'a tzresult Lwt.t) -> + 'a tzresult Lwt.t + + val add_l2_genesis_block : + [`Read | `Write] Node_context.t -> + boot_sector:string -> + ((Sc_rollup_block.header, unit) Sc_rollup_block.block, tztrace) result Lwt.t + + val append_l2_block : + [`Read | `Write] Node_context.t -> + Sc_rollup.Inbox_message.t trace -> + ((Sc_rollup_block.header, unit) Sc_rollup_block.block, tztrace) result Lwt.t +end + +module Make (PVM : Pvm.S) = struct + module Daemon = Daemon.Make (PVM) + module Components = Daemon.Components + + let default_constants = + let constants = Default_parameters.constants_test in + let sc_rollup = + { + constants.sc_rollup with + arith_pvm_enable = true; + challenge_window_in_blocks = 4032; + commitment_period_in_blocks = 3; + } + in + {constants with sc_rollup} + + let add_l2_genesis_block (node_ctxt : _ Node_context.t) ~boot_sector = + let open Lwt_result_syntax in + let head = + Layer1. + { + hash = Block_hash.zero; + level = Raw_level.to_int32 node_ctxt.genesis_info.level; + } + in + let* () = Node_context.save_level node_ctxt head in + let predecessor = head in + let predecessor_timestamp = Time.Protocol.epoch in + let*? inbox = + Environment.wrap_tzresult + @@ Sc_rollup.Inbox.genesis + ~predecessor_timestamp + ~predecessor:predecessor.hash + node_ctxt.genesis_info.level + in + let* inbox_hash = Node_context.save_inbox node_ctxt inbox in + let inbox_witness = Sc_rollup.Inbox.current_witness inbox in + let ctxt = Context.empty node_ctxt.context in + let num_ticks = 0L in + let initial_tick = Sc_rollup.Tick.initial in + let*! initial_state = PVM.initial_state ~empty:(PVM.State.empty ()) in + let*! state = PVM.install_boot_sector initial_state boot_sector in + let*! genesis_state_hash = PVM.state_hash state in + let*! ctxt = PVM.State.set ctxt state in + let*! context_hash = Context.commit ctxt in + let commitment = + Sc_rollup.Commitment.genesis_commitment + ~origination_level:node_ctxt.genesis_info.level + ~genesis_state_hash + in + let* commitment_hash = Node_context.save_commitment node_ctxt commitment in + let previous_commitment_hash = node_ctxt.genesis_info.commitment_hash in + let header = + Sc_rollup_block. + { + block_hash = head.hash; + level = node_ctxt.genesis_info.level; + predecessor = predecessor.hash; + commitment_hash = Some commitment_hash; + previous_commitment_hash; + context = context_hash; + inbox_witness; + inbox_hash; + } + in + let l2_block = + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} + in + let* () = Node_context.save_l2_head node_ctxt l2_block in + return l2_block + + let initialize_node_context ?(constants = default_constants) kind ~boot_sector + = + let open Lwt_result_syntax in + incr uid ; + (* To avoid any conflict with previous runs of this test. *) + let pid = Unix.getpid () in + let data_dir = + Filename.(concat @@ get_temp_dir_name ()) + (Format.sprintf "sc-rollup-node-test-%d-%d" pid !uid) + in + let base_dir = + Filename.(concat @@ get_temp_dir_name ()) + (Format.sprintf "sc-rollup-node-test-base-%d-%d" pid !uid) + in + let filesystem = String.Hashtbl.create 10 in + let cctxt = + new Protocol_client_context.wrap_full + (new Faked_client_context.unix_faked ~base_dir ~filesystem) + in + let* ctxt = + Node_context.Internal_for_tests.create_node_context + cctxt + ~constants + ~data_dir + kind + in + let* genesis = add_l2_genesis_block ctxt ~boot_sector in + let commitment_hash = + WithExceptions.Option.get ~loc:__LOC__ genesis.header.commitment_hash + in + let ctxt = + {ctxt with genesis_info = {ctxt.genesis_info with commitment_hash}} + in + return (ctxt, genesis) + + let with_node_context ?constants kind ~boot_sector f = + let open Lwt_result_syntax in + let* node_ctxt, genesis = + initialize_node_context ?constants kind ~boot_sector + in + Lwt.finalize (fun () -> f node_ctxt ~genesis) @@ fun () -> + let open Lwt_syntax in + let* _ = Node_context.close node_ctxt in + return_unit + + let append_l2_block (node_ctxt : _ Node_context.t) messages = + let open Lwt_result_syntax in + let* predecessor_l2_block = + Node_context.last_processed_head_opt node_ctxt + in + let* predecessor_l2_block = + match predecessor_l2_block with + | Some b -> return b + | None -> + failwith "No genesis block, please add one with add_l2_genesis_block" + in + let level = + Raw_level.(to_int32 @@ succ predecessor_l2_block.header.level) + in + let hash = block_hash_of_level level in + let predecessor = + Layer1. + { + hash = predecessor_l2_block.header.block_hash; + level = Raw_level.to_int32 predecessor_l2_block.header.level; + } + in + let head = Layer1.{hash; level} in + let predecessor_timestamp = + Time.Protocol.of_seconds (Int64.of_int32 level) + in + Daemon.Internal_for_tests.process_messages + node_ctxt + ~predecessor + ~predecessor_timestamp + head + messages +end + +let l2_chain_builders = + List.map + (fun kind -> + let module PVM = (val Components.pvm_of_kind kind) in + (kind, (module Make (PVM) : S))) + Sc_rollup.Kind.all + +let l2_chain_builder kind = Stdlib.List.assoc kind l2_chain_builders + +let with_node_context ?constants kind ~boot_sector = + let module L = (val l2_chain_builder kind) in + L.with_node_context ?constants kind ~boot_sector + +let add_l2_genesis_block (node_ctxt : _ Node_context.t) = + let module L = (val l2_chain_builder node_ctxt.kind) in + L.add_l2_genesis_block node_ctxt + +let append_l2_block (node_ctxt : _ Node_context.t) = + let module L = (val l2_chain_builder node_ctxt.kind) in + L.append_l2_block node_ctxt + +let append_l2_blocks node_ctxt message_batches = + List.map_es (append_l2_block node_ctxt) message_batches + +let append_dummy_l2_chain node_ctxt ~length = + let open Lwt_result_syntax in + let* head = Node_context.last_processed_head_opt node_ctxt in + let head_level = + match head with + | None -> 0 + | Some h -> h.header.level |> Raw_level.to_int32 |> Int32.to_int + in + let batches = + Stdlib.List.init length (fun i -> + [ + Sc_rollup.Inbox_message.External + (Z.to_bits (Z.of_int (i + head_level + 1))); + ]) + in + append_l2_blocks node_ctxt batches + +module Assert = struct + module Make_with_encoding (X : sig + type t + + val encoding : t Data_encoding.t + end) = + Assert.Make_equalities (struct + type t = X.t + + let eq (b1 : X.t) (b2 : X.t) = + Bytes.equal + (Data_encoding.Binary.to_bytes_exn X.encoding b1) + (Data_encoding.Binary.to_bytes_exn X.encoding b2) + + let pp ppf (b : X.t) = + Data_encoding.Json.pp ppf (Data_encoding.Json.construct X.encoding b) + end) + + module L2_block = Make_with_encoding (Sc_rollup_block) + module Commitment = Make_with_encoding (Sc_rollup.Commitment) + module Commitment_hash = Make_with_encoding (Sc_rollup.Commitment.Hash) + module State_hash = Make_with_encoding (Sc_rollup.State_hash) +end + +let alcotest name speed ?constants kind ~boot_sector f = + Alcotest_lwt.test_case name speed @@ fun _lwt_switch () -> + let open Lwt_result_syntax in + let*! r = with_node_context ?constants kind ~boot_sector f in + match r with + | Ok () -> Lwt.return_unit + | Error err -> + Format.printf "@\n%a@." pp_print_trace err ; + Lwt.fail Alcotest.Test_error diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.mli new file mode 100644 index 000000000000..04b986c0394c --- /dev/null +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.mli @@ -0,0 +1,115 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, . *) +(* *) +(* 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 Alpha_context + +(** {1 Helper functions to build and run unit tests for the rollup node} *) + +(** {2 Creating Node Contexts} *) + +(** [with_node_context ?constants kind ~boot_sector f] creates a node context + and (with a store, a context, etc.) where protocol [constants] can be + specified, and runs [f] with this node context. The L2 chain is initialized + with a genesis block and the PVM with the [boot_sector]. When [f] terminates + or fails, the created node context is closed properly. Test that need a node + context need to use this function in order to avoid file descriptor + leaks. *) +val with_node_context : + ?constants:Constants.Parametric.t -> + Sc_rollup.Kind.t -> + boot_sector:string -> + ([`Read | `Write] Node_context.t -> + genesis:Sc_rollup_block.t -> + 'a tzresult Lwt.t) -> + 'a tzresult Lwt.t + +(** {2 Building L2 Chains} *) + +(** Create and add a genesis block for the L2 chain. The [boot_sector] for the + rollup/kernel needs to be provided. The newly created L2 block is + returned. *) +val add_l2_genesis_block : + [`Read | `Write] Node_context.t -> + boot_sector:string -> + Sc_rollup_block.t tzresult Lwt.t + +(** [append_l2_block node_ctxt messages] creates and append + an L2 block containing the [messages] given in argument. The block is added + on top of the last L2 block in the chain (i.e. the head known by the node), + and is returned. *) +val append_l2_block : + [`Read | `Write] Node_context.t -> + Sc_rollup.Inbox_message.t list -> + Sc_rollup_block.t tzresult Lwt.t + +(** [append_l2_block node_ctxt message_batches] appends as many blocks as there + are batches in [message_batches]. Each block contain a batch of + messages. The portion of the chain that was added is returned. *) +val append_l2_blocks : + [`Read | `Write] Node_context.t -> + Sc_rollup.Inbox_message.t list list -> + Sc_rollup_block.t list tzresult Lwt.t + +(** [append_dummy_l2_chain node_ctxt ~length] append [length] L2 blocks with an + arbitrary content to the chain. The portion of the chain that was added is + returned. This function is useful for quickly building long(er) L2 chains + for the tests. *) +val append_dummy_l2_chain : + [`Read | `Write] Node_context.t -> + length:int -> + Sc_rollup_block.t list tzresult Lwt.t + +(** {2 Assertions} *) + +module Assert : sig + (** Assertions on L2 blocks *) + module L2_block : Assert.EQUALITIES with type t = Sc_rollup_block.t + + (** Assertions on commitments *) + module Commitment : Assert.EQUALITIES with type t = Sc_rollup.Commitment.t + + (** Assertions on commitment hashes *) + module Commitment_hash : + Assert.EQUALITIES with type t = Sc_rollup.Commitment.Hash.t + + (** Assertions on PVM state hashes *) + module State_hash : Assert.EQUALITIES with type t = Sc_rollup.State_hash.t +end + +(** {2 Building and Running tests} *) + +(** Build an alcotest test case that executes with node context initialized with + a genesis block with the provided [boot_sector]. *) +val alcotest : + string -> + Alcotest.speed_level -> + ?constants:Constants.Parametric.t -> + Sc_rollup.Kind.t -> + boot_sector:string -> + ([`Read | `Write] Node_context.t -> + genesis:Sc_rollup_block.t -> + unit tzresult Lwt.t) -> + unit Alcotest_lwt.test_case -- GitLab